diff --git a/docs/design/mono/mono-library-mode.md b/docs/design/mono/mono-library-mode.md index f6c6a98e5a746e..b786450c1a0b20 100644 --- a/docs/design/mono/mono-library-mode.md +++ b/docs/design/mono/mono-library-mode.md @@ -109,7 +109,7 @@ public class MainActivity extends AppCompatActivity { After building the mono library with `dotnet publish -r ios-arm64`, it can be found as `lib.dylib` in the binaries folder (i.e. `library-mode-sample/ManagedProject/bin/Release/net8.0/ios-arm64/Bundle/libManagedProject.dylib`). The mono library when built as a shared library with bundling (on by default) can be loaded and used with the following steps: -1. Open/Create the iOS native project in XCode. +1. Open/Create the iOS native project in Xcode. 2. Copy the mono library into the project's root directory (not creating a reference). diff --git a/docs/project/os-onboarding.md b/docs/project/os-onboarding.md new file mode 100644 index 00000000000000..c233c5ebdfab86 --- /dev/null +++ b/docs/project/os-onboarding.md @@ -0,0 +1,134 @@ +# Onboarding Guide for New Operating System Versions + +Adding support for new operating systems versions is a frequent need. This guide describes how we do that, including the policies we use. + +> Being _active_ in `main` enables being _lazy_ in `release/`. + +This witticism is the underlying philosophy of our approach. By actively maintaining OS versions in our active coding branch, we get the double benefit of bleeding-edge coverage and (in many cases) can avoid EOL remediation cost in `release/` branches. Spending time on avoidable work is a failure of planning. + +> Users are best served when we act _quickly_ not _exhaustively_. + + +This double meaning is instructing us to be boldly pragmatic. Each new OS release brings a certain risk of breakage. The risk is far from uniform across the various repos and components that we maintain. Users are best served when we've developed 80% confidence and to leave the remaining (potential) 20% to bug reports. Exhaustive testing serves no one. We've also found that our users do a great job finding corner cases and enthusiastically participate in the process by opening issues in the appropriate repo. + + +Continuing with the idea of pragmatism, if you only read this far, you've got the basic idea. The rest of the doc describes more context and mechanics. + +References: + +- [.NET OS Support Tracking](https://github.com/dotnet/core/issues/9638) +- [.NET Support](https://github.com/dotnet/core/blob/main/support.md) +- [Prereq container image lifecycle](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/blob/main/lifecycle.md) +- [Support for Linux Distros](https://dev.azure.com/dnceng/internal/_wiki/wikis/DNCEng%20Services%20Wiki/940/Support-for-Linux-Distros) (MS internal) +- [Support for Apple Operating Systems](https://dev.azure.com/dnceng/internal/_wiki/wikis/DNCEng%20Services%20Wiki/933/Support-for-Apple-Operating-Systems-(macOS-iOS-and-tvOS)) (MS internal) +- [Support for Windows Operating Systems](https://dev.azure.com/dnceng/internal/_wiki/wikis/DNCEng%20Services%20Wiki/939/Support-for-Windows-Operating-Systems) (MS internal) + +## Context + +In most cases, we find that new OS versions _may_ uncover problems in dotnet/runtime, but don't affect up-stack components or apps once resolved. A key design point of our runtime is to be a quite complete cross-platform and -architecture abstraction, so resolving OS compatibility breaks for higher-level code is an enduring intent. + +Nearly all the APIs that touch native code (networking, cryptography) and deal with standard formats (time zones, ASN.1) are in dotnet/runtime. In many cases, we only see test breaks when we onboard a new OS, often from code that tests edge cases. + +## Approach + +Our rule is that we declare support (for all [supported .NET releases](https://github.com/dotnet/core/blob/main/releases.md)) for a new OS version after it is validated in dotnet/runtime `main`. We will only hold support on additional testing in special cases (which are uncommon). + +We aim to have "day of" support for about half the OSes we support, including Azure Linux, Ubuntu LTS, and Windows. This means we need to perform ahead-of-time signoff on non-final builds. + +Our testing philosophy is based on perceived risk and past experience. The effective test matrix is huge, the product of OSes \* supported versions \* architectures. We try to make smart choices to **skip testing most of the matrix** while retaining much of the **practical coverage**. We also know where we tend to get bitten most when we don't pay sufficient attention. For example, our bug risk across Linux, macOS, and Windows is not uniform. + +We use pragmatism and efficiency to drive our decision making. All things being equal, we'll choose the lowest cost approach. + +## Testing + +Testing is the bread and butter of OS onboarding, particularly for a mature runtime like ours. New OS support always needs some form of test enablement. + +Linux, Wasm, and some Windows testing is done in container images. This approach enables us to test many and regularly changing OS versions in a fixed/limited VM environment. The container image creation/update process is self-service (discussed later). + +We use VMs (Linux and Windows) and raw metal hardware (Android and Apple) in cases where containers are not practical or where direct testing is desired. This is the primary model for Apple and Windows OSes. The VMs and mobile/Apple hardware are relatively slow to change and require support from dnceng (discussed later). + +### Adding coverage + +New OS coverage should be added/tested first in `main`. If changes are required, we should prove them out first in `main` before committing to shipping them in a servicing release, if necessary. + +There are multiple reasons to add a new OS reference in a release branch: + +- Known product (as opposed to test) breaks that require validation and regression testing. +- Past experience suggests that coverage is required to protect against risk. +- OS version is or [will soon go EOL](https://github.com/dotnet/runtime/issues/111818#issuecomment-2613642202) and should be replaced by a newer version. + +For example, we frequently need to backport Alpine updates to release branches to avoid EOL references but less commonly for Ubuntu, given the vast difference in support length. + +A good strategy is to keep `main` at the bleeding edge of new OS versions. That way those references have a decent chance of never needing remediation once they end up in release branches. + +### Updating or removing coverage + +We will often replace an older OS version with a new one, when it comes available. This approach is an effective strategy of maintaining the same level of coverage and of remediating EOL OSes ahead of time. For the most part, we don't need to care about a specific version. We just want coverage for the OS, like Alpine. + +We should remediate any EOL OS references in our codebase. They don't serve any benefit and come with some risk. They are also likely to result in compliance tickets (that come with a deadline) that we want to avoid. + +In the case that a .NET version will be EOL in <6 (and certainly <3) months, new coverage can typically be skipped. We may even be able to skip remediating EOL OS references. We often opt to stop updating [supported OSes](https://github.com/dotnet/core/blob/main/os-lifecycle-policy.md) late in support period for related reasons. A lazy approach is often the best approach late in the game. Don't upset what's working. + +## Building + +Our [build methodology](https://github.com/dotnet/runtime/blob/main/docs/project/linux-build-methodology.md) is oriented around cross-compiling, enabling us to target an old OS version and run on newer ones. It is uncommon for us to need to make changes to the build to address new OS versions, however, there are [rare cases where we need to make adjustments](https://github.com/dotnet/runtime/issues/101944). + +We use both containers and VMs for building, depending on the OS. If we test in a container, we likely build in a container. Same for VMs. + +Our primary concern is ensuring that we are using [supported operating systems and tools for our build](https://github.com/dotnet/runtime/tree/main/docs/workflow/requirements). + +Our Linux build containers are based on Azure Linux. We [typically need to update them](https://github.com/dotnet/runtime/issues/112191) with a new version of Azure Linux once per release. We do not update the toolset, however. That's fixed, per release. + +For Apple, we likely need to make an adjustment at each macOS or iOS release to account for an Xcode version no longer being supported. + +## Environments + +We rely on a set of standard environments for building and testing. These environments are managed with a "config as code" paradigm in our source code. This approach delivers CI reliability -- which we greatly value -- but also comes with the tedious cost of needing to regularly update various reference strings in several files. Updating version strings almost always requires sorting through build breaks, which is a reminder of why we value our approach. + +We may use multiple environments for building and testing a given OS. + +### Containers + +New container images need to be created for each new OS version in the [dotnet/dotnet-buildtools-prereqs-docker](https://github.com/dotnet/dotnet-buildtools-prereqs-docker) repo. These are used for building and testing Android, Linux, Wasm, and Windows OSes/targets. + +The repo is self-service and largely self-explanatory. One typically creates a new image using the pattern demonstrated by the previous version. Look at commits and [blame](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/blame/776324ff16d38e22fd9f06c9842ec338a4b98489/src/alpine/3.20/helix/Dockerfile) to find people who are best suited to help. + +Installing/building the Helix client can be quite involved, particularly for Arm platforms. Don't struggle with that. Just ask for help. + +Container images are referenced in our pipeline files: + +- [eng/pipelines/coreclr/templates/helix-queues-setup.yml](https://github.com/dotnet/runtime/blob/main/eng/pipelines/coreclr/templates/helix-queues-setup.yml) +- [eng/pipelines/libraries/helix.yml](https://github.com/dotnet/runtime/blob/main/eng/pipelines/libraries/helix.yml) +- [eng/pipelines/common/templates/pipeline-with-resources.yml](https://github.com/dotnet/runtime/blob/main/eng/pipelines/common/templates/pipeline-with-resources.yml) + +Notes: + +- The first two links are for testing and the last for building. +- The links are for the `main` branch. Release branches should have the same layout. + +Example PRs: + +- [dotnet/runtime #111768](https://github.com/dotnet/runtime/pull/111768) +- [dotnet/runtime #111504](https://github.com/dotnet/runtime/pull/111504) +- [dotnet/runtime #110492](https://github.com/dotnet/runtime/pull/110492) +- [dotnet/dotnet-buildtools-prereqs-docker #1282](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/pull/1282) +- [dotnet/dotnet-buildtools-prereqs-docker #1314](https://github.com/dotnet/dotnet-buildtools-prereqs-docker/pull/1314) + +### VMs + +VMs and raw metal environments are used for Android, Apple, Linux, and Windows OSes. They need to be [requested from dnceng](https://github.com/dotnet/dnceng/issues/4307). The turnaround can be long so put in your request before you need it. + +- Android: Raw metal hosts for some forms of Android testing. +- Apple: Raw metal hosts for all forms of Apple testing. +- Linux: All Linux VMs are moving to Azure Linux as a container host. +- Windows: Windows Client and Server VMs + +### Other + +Other environments are typically use a custom process. + +- [Browser Wasm](https://github.com/dotnet/runtime/pull/112066) + +## Porting + +[Porting .NET to a new operating system or architecture](../design/coreclr/botr/guide-for-porting.md) is a related task. The same patterns likely apply, but the overall task is much larger in scope. diff --git a/docs/workflow/building/coreclr/ios.md b/docs/workflow/building/coreclr/ios.md index 9061fd17da438c..9574010e2f8b15 100644 --- a/docs/workflow/building/coreclr/ios.md +++ b/docs/workflow/building/coreclr/ios.md @@ -20,7 +20,7 @@ Build and run the sample app with ./dotnet.sh publish src/mono/sample/iOS/Program.csproj -c Release /p:TargetOS=iossimulator /p:TargetArchitecture=arm64 /p:DeployAndRun=true /p:UseMonoRuntime=false /p:RunAOTCompilation=false /p:MonoForceInterpreter=false ``` -The command also produces an XCode project that can be opened with `open ./src/mono/sample/iOS/bin/iossimulator-arm64/Bundle/HelloiOS/HelloiOS.xcodeproj` and debugged in Xcode. +The command also produces an Xcode project that can be opened with `open ./src/mono/sample/iOS/bin/iossimulator-arm64/Bundle/HelloiOS/HelloiOS.xcodeproj` and debugged in Xcode. ## Running the runtime tests diff --git a/docs/workflow/testing/libraries/testing-apple.md b/docs/workflow/testing/libraries/testing-apple.md index 09e08d1b0cc178..2f6f7128be4d64 100644 --- a/docs/workflow/testing/libraries/testing-apple.md +++ b/docs/workflow/testing/libraries/testing-apple.md @@ -2,10 +2,10 @@ ## Prerequisites -- XCode 11.3 or higher +- Xcode 11.3 or higher - a certificate and provisioning profile if using a device - a simulator with a proper device type and OS version. -Go `XCode > Window > Devices and Simulators` to revise the list of the available simulators and then `"+" button on bottom left > OS Version dropdown selection > Download more simulator runtimes` in case you need to download more simulators. +Go `Xcode > Window > Devices and Simulators` to revise the list of the available simulators and then `"+" button on bottom left > OS Version dropdown selection > Download more simulator runtimes` in case you need to download more simulators. ## Building Libs and Tests diff --git a/eng/CodeAnalysis.src.globalconfig b/eng/CodeAnalysis.src.globalconfig index 500c19397f38fc..3569479b9ae045 100644 --- a/eng/CodeAnalysis.src.globalconfig +++ b/eng/CodeAnalysis.src.globalconfig @@ -1139,8 +1139,7 @@ dotnet_diagnostic.RS2008.severity = warning dotnet_diagnostic.SA0001.severity = none # SA1000: Spacing around keywords -# suggestion until https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3478 is resolved -dotnet_diagnostic.SA1000.severity = suggestion +dotnet_diagnostic.SA1000.severity = warning # SA1001: Commas should not be preceded by whitespace dotnet_diagnostic.SA1001.severity = warning diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 705bd1ffbd0c25..c9ceae82ec8700 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -71,9 +71,9 @@ - + https://github.com/dotnet/source-build-reference-packages - f18184030957ae8b02c6cb971ff2a58a06d2e7b1 + 81b495268ffb3f5cffbe63724ad085f831bcc1b1 @@ -84,87 +84,87 @@ - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c https://github.com/dotnet/runtime-assets @@ -348,9 +348,9 @@ https://github.com/dotnet/xharness 0d72885f0fd3329e58254831f04f4517a73e1b56 - + https://github.com/dotnet/arcade - bbea86c614fcf4380c58c80eacd279a0b8305a79 + 91630b31ce859c28f637b62b566ea8829b982f2c https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index 94c3410dd3a996..ff327bd5ac11d1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -85,22 +85,22 @@ 10.0.100-alpha.1.25077.2 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 2.9.2-beta.25080.7 - 10.0.0-beta.25080.7 - 2.9.2-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 - 10.0.0-beta.25080.7 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 2.9.2-beta.25106.4 + 10.0.0-beta.25106.4 + 2.9.2-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 + 10.0.0-beta.25106.4 1.4.0 diff --git a/eng/common/sdk-task.sh b/eng/common/sdk-task.sh new file mode 100644 index 00000000000000..b9b9e58db9ad03 --- /dev/null +++ b/eng/common/sdk-task.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +show_usage() { + echo "Common settings:" + echo " --task Name of Arcade task (name of a project in SdkTasks directory of the Arcade SDK package)" + echo " --restore Restore dependencies" + echo " --verbosity Msbuild verbosity: q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]" + echo " --help Print help and exit" + echo "" + echo "Command line arguments not listed above are passed thru to msbuild." +} + +source="${BASH_SOURCE[0]}" + +# resolve $source until the file is no longer a symlink +while [[ -h "$source" ]]; do + scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + source="$(readlink "$source")" + # if $source was a relative symlink, we need to resolve it relative to the path where the + # symlink file was located + [[ $source != /* ]] && source="$scriptroot/$source" +done +scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" + +Build() { + local target=$1 + local log_suffix="" + [[ "$target" != "Execute" ]] && log_suffix=".$target" + local log="$log_dir/$task$log_suffix.binlog" + local output_path="$toolset_dir/$task/" + + MSBuild "$taskProject" \ + /bl:"$log" \ + /t:"$target" \ + /p:Configuration="$configuration" \ + /p:RepoRoot="$repo_root" \ + /p:BaseIntermediateOutputPath="$output_path" \ + /v:"$verbosity" \ + $properties +} + +configuration="Debug" +verbosity="minimal" +restore=false +help=false +properties='' + +while (($# > 0)); do + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" + case $lowerI in + --task) + task=$2 + shift 2 + ;; + --restore) + restore=true + shift 1 + ;; + --verbosity) + verbosity=$2 + shift 2 + ;; + --help) + help=true + shift 1 + ;; + *) + properties="$properties $1" + shift 1 + ;; + esac +done + +ci=true +binaryLog=true +warnAsError=true + +if $help; then + show_usage + exit 0 +fi + +. "$scriptroot/tools.sh" +InitializeToolset + +if [[ -z "$task" ]]; then + Write-PipelineTelemetryError -Category 'Task' -Name 'MissingTask' -Message "Missing required parameter '-task '" + ExitWithExitCode 1 +fi + +taskProject=$(GetSdkTaskProject "$task") +if [[ ! -e "$taskProject" ]]; then + Write-PipelineTelemetryError -Category 'Task' -Name 'UnknownTask' -Message "Unknown task: $task" + ExitWithExitCode 1 +fi + +if $restore; then + Build "Restore" +fi + +Build "Execute" + + +ExitWithExitCode 0 diff --git a/eng/common/tools.sh b/eng/common/tools.sh index df203b5178421d..4a5fa99478d146 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -528,6 +528,12 @@ function GetDarc { "$eng_root/common/darc-init.sh" --toolpath "$darc_path" $version } +# Returns a full path to an Arcade SDK task project file. +function GetSdkTaskProject { + taskName=$1 + echo "$(dirname $_InitializeToolset)/SdkTasks/$taskName.proj" +} + ResolvePath "${BASH_SOURCE[0]}" _script_dir=`dirname "$_ResolvePath"` diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index f39bb89d3294fb..4dfaa73326a087 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -609,7 +609,6 @@ extends: - osx_x64 - linux_arm64 - windows_arm64 - - osx_arm64 variables: - name: timeoutPerTestInMinutes value: 60 diff --git a/global.json b/global.json index d00329a3e0da85..12965784ea2d87 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "10.0.100-alpha.1.25077.2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25080.7", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25080.7", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25080.7", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25106.4", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25106.4", + "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25106.4", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "10.0.0-alpha.1.25068.1" diff --git a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs index c52520e9c68f9d..258a1a6632c734 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs @@ -70,7 +70,7 @@ public override object ChangeType(object value, Type type, CultureInfo? cultureI return RetObj; } #if DISPLAY_DEBUG_INFO - catch(NotSupportedException e) + catch (NotSupportedException e) #else catch (NotSupportedException) #endif diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.cs index 70b88afc0726bb..c438aeb1d9a057 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.cs @@ -231,7 +231,7 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName, } return null; } - return GetTypeFromDefaultAssemblies(TypeNameHelpers.Unescape(escapedTypeName), nestedTypeNames, parsedName); + return GetTypeFromDefaultAssemblies(TypeName.Unescape(escapedTypeName), nestedTypeNames, parsedName); } if (assembly is RuntimeAssembly runtimeAssembly) @@ -239,7 +239,7 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName, // Compat: Non-extensible parser allows ambiguous matches with ignore case lookup bool useReflectionForNestedTypes = _extensibleParser && _ignoreCase; - type = runtimeAssembly.GetTypeCore(TypeNameHelpers.Unescape(escapedTypeName), useReflectionForNestedTypes ? default : nestedTypeNames, + type = runtimeAssembly.GetTypeCore(TypeName.Unescape(escapedTypeName), useReflectionForNestedTypes ? default : nestedTypeNames, throwOnFileNotFound: _throwOnError, ignoreCase: _ignoreCase); if (type is null) @@ -282,7 +282,7 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName, if (_throwOnError) { throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType, - nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : TypeNameHelpers.Unescape(escapedTypeName)), + nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : TypeName.Unescape(escapedTypeName)), typeName: parsedName.FullName); } return null; diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index ef91f916fb9563..ae8c38daadb92c 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -4962,22 +4962,11 @@ GenTree* Compiler::optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tr // Arguments: // op - tree to check // assertions - set of live assertions -// pVnBased - [out] set to true if value numbers were used -// pIndex - [out] the assertion used in the proof // // Return Value: // true if the tree's value will be non-null // -// Notes: -// Sets "pVnBased" if the assertion is value number based. If no matching -// assertions are found from the table, then returns "NO_ASSERTION_INDEX." -// -// If both VN and assertion table yield a matching assertion, "pVnBased" -// is only set and the return value is "NO_ASSERTION_INDEX." -// -bool Compiler::optAssertionIsNonNull(GenTree* op, - ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased) - DEBUGARG(AssertionIndex* pIndex)) +bool Compiler::optAssertionIsNonNull(GenTree* op, ASSERT_VALARG_TP assertions) { if (op->OperIs(GT_ADD) && op->AsOp()->gtGetOp2()->IsCnsIntOrI() && !fgIsBigOffset(op->AsOp()->gtGetOp2()->AsIntCon()->IconValue())) @@ -4985,90 +4974,39 @@ bool Compiler::optAssertionIsNonNull(GenTree* op, op = op->AsOp()->gtGetOp1(); } - bool vnBased = (!optLocalAssertionProp && vnStore->IsKnownNonNull(op->gtVNPair.GetConservative())); -#ifdef DEBUG - *pIndex = NO_ASSERTION_INDEX; - *pVnBased = vnBased; -#endif - - if (vnBased) + // Fast path when we have a VN + if (!optLocalAssertionProp && vnStore->IsKnownNonNull(op->gtVNPair.GetConservative())) { return true; } - op = op->gtEffectiveVal(); - - if (!op->OperIs(GT_LCL_VAR)) + if (!optCanPropNonNull || BitVecOps::MayBeUninit(assertions)) { return false; } - AssertionIndex index = optAssertionIsNonNullInternal(op, assertions DEBUGARG(pVnBased)); -#ifdef DEBUG - *pIndex = index; -#endif - return index != NO_ASSERTION_INDEX; -} - -//------------------------------------------------------------------------ -// optAssertionIsNonNullInternal: see if we can prove a tree's value will -// be non-null based on assertions -// -// Arguments: -// op - tree to check -// assertions - set of live assertions -// pVnBased - [out] set to true if value numbers were used -// -// Return Value: -// index of assertion, or NO_ASSERTION_INDEX -// -AssertionIndex Compiler::optAssertionIsNonNullInternal(GenTree* op, - ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased)) -{ - -#ifdef DEBUG - // Initialize the out param - // - *pVnBased = false; -#endif - - if (!optCanPropNonNull) + op = op->gtEffectiveVal(); + if (!op->OperIs(GT_LCL_VAR)) { - return NO_ASSERTION_INDEX; + return false; } // If local assertion prop use lcl comparison, else use VN comparison. if (!optLocalAssertionProp) { - if (BitVecOps::MayBeUninit(assertions) || BitVecOps::IsEmpty(apTraits, assertions)) - { - return NO_ASSERTION_INDEX; - } - // Look at both the top-level vn, and // the vn we get by stripping off any constant adds. // - ValueNum vn = vnStore->VNConservativeNormalValue(op->gtVNPair); - ValueNum vnBase = vn; - VNFuncApp funcAttr; - - while (vnStore->GetVNFunc(vnBase, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD)) + ValueNum vn = vnStore->VNConservativeNormalValue(op->gtVNPair); + if (vn == ValueNumStore::NoVN) { - if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1]))) - { - vnBase = funcAttr.m_args[0]; - } - else if (vnStore->IsVNConstant(funcAttr.m_args[0]) && - varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0]))) - { - vnBase = funcAttr.m_args[1]; - } - else - { - break; - } + return false; } + ValueNum vnBase = vn; + target_ssize_t offset = 0; + vnStore->PeelOffsets(&vnBase, &offset); + // Check each assertion to find if we have a vn != null assertion. // BitVecOps::Iter iter(apTraits, assertions); @@ -5076,26 +5014,11 @@ AssertionIndex Compiler::optAssertionIsNonNullInternal(GenTree* while (iter.NextElem(&index)) { AssertionIndex assertionIndex = GetAssertionIndex(index); - if (assertionIndex > optAssertionCount) - { - break; - } - AssertionDsc* curAssertion = optGetAssertion(assertionIndex); - if (!curAssertion->CanPropNonNull()) - { - continue; - } - - if ((curAssertion->op1.vn != vn) && (curAssertion->op1.vn != vnBase)) + AssertionDsc* curAssertion = optGetAssertion(assertionIndex); + if (curAssertion->CanPropNonNull() && ((curAssertion->op1.vn == vn) || (curAssertion->op1.vn == vnBase))) { - continue; + return true; } - -#ifdef DEBUG - *pVnBased = true; -#endif - - return assertionIndex; } } else @@ -5119,11 +5042,11 @@ AssertionIndex Compiler::optAssertionIsNonNullInternal(GenTree* (curAssertion->op2.kind == O2K_CONST_INT) && // op2 (curAssertion->op1.lcl.lclNum == lclNum) && (curAssertion->op2.u1.iconVal == 0)) { - return assertionIndex; + return true; } } } - return NO_ASSERTION_INDEX; + return false; } //------------------------------------------------------------------------ @@ -5177,20 +5100,10 @@ GenTree* Compiler::optNonNullAssertionProp_Call(ASSERT_VALARG_TP assertions, Gen GenTree* op1 = call->gtArgs.GetThisArg()->GetNode(); noway_assert(op1 != nullptr); -#ifdef DEBUG - bool vnBased = false; - AssertionIndex index = NO_ASSERTION_INDEX; -#endif - if (optAssertionIsNonNull(op1, assertions DEBUGARG(&vnBased) DEBUGARG(&index))) + if (optAssertionIsNonNull(op1, assertions)) { -#ifdef DEBUG - if (verbose) - { - (vnBased) ? printf("\nVN based non-null prop in " FMT_BB ":\n", compCurBB->bbNum) - : printf("\nNon-null prop for index #%02u in " FMT_BB ":\n", index, compCurBB->bbNum); - gtDispTree(call, nullptr, nullptr, true); - } -#endif + JITDUMP("Non-null assertion prop for tree [%06d] in " FMT_BB ":\n", dspTreeID(op1), compCurBB->bbNum); + call->gtFlags &= ~GTF_CALL_NULLCHECK; call->gtFlags &= ~GTF_EXCEPT; noway_assert(call->gtFlags & GTF_SIDE_EFFECT); @@ -5219,20 +5132,10 @@ bool Compiler::optNonNullAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* return false; } -#ifdef DEBUG - bool vnBased = false; - AssertionIndex index = NO_ASSERTION_INDEX; -#endif - if (optAssertionIsNonNull(indir->AsIndir()->Addr(), assertions DEBUGARG(&vnBased) DEBUGARG(&index))) + if (optAssertionIsNonNull(indir->AsIndir()->Addr(), assertions)) { -#ifdef DEBUG - if (verbose) - { - (vnBased) ? printf("\nVN based non-null prop in " FMT_BB ":\n", compCurBB->bbNum) - : printf("\nNon-null prop for index #%02u in " FMT_BB ":\n", index, compCurBB->bbNum); - gtDispTree(indir, nullptr, nullptr, true); - } -#endif + JITDUMP("Non-null assertion prop for indirection [%06d] in " FMT_BB ":\n", dspTreeID(indir), compCurBB->bbNum); + indir->gtFlags &= ~GTF_EXCEPT; indir->gtFlags |= GTF_IND_NONFAULTING; @@ -5429,11 +5332,9 @@ GenTree* Compiler::optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCal } // Leave a hint for fgLateCastExpansion that obj is never null. - INDEBUG(AssertionIndex nonNullIdx = NO_ASSERTION_INDEX); - INDEBUG(bool vnBased = false); // GTF_CALL_M_CAST_CAN_BE_EXPANDED check is to improve TP if (((call->gtCallMoreFlags & GTF_CALL_M_CAST_CAN_BE_EXPANDED) != 0) && - optAssertionIsNonNull(objArg, assertions DEBUGARG(&vnBased) DEBUGARG(&nonNullIdx))) + optAssertionIsNonNull(objArg, assertions)) { call->gtCallMoreFlags |= GTF_CALL_M_CAST_OBJ_NONNULL; return optAssertionProp_Update(call, call, stmt); diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 8efe6e0827125c..75f5c73dcfb592 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -1090,29 +1090,44 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre emitAttr size = emitActualTypeSize(tree); double constValue = tree->AsDblCon()->DconValue(); - // Make sure we use "fmv.w.x reg, zero" only for positive zero (0.0) - // and not for negative zero (-0.0) + assert(emitter::isFloatReg(targetReg)); + + // Make sure we use "fmv.w.x reg, zero" only for positive zero (0.0) and not for negative zero (-0.0) if (FloatingPointUtils::isPositiveZero(constValue)) { // A faster/smaller way to generate 0.0 - // We will just zero out the entire vector register for both float and double + // We will just zero out the entire register for both float and double emit->emitIns_R_R(size == EA_4BYTE ? INS_fmv_w_x : INS_fmv_d_x, size, targetReg, REG_R0); + break; } - else + + int64_t bits = + (size == EA_4BYTE) + ? (int32_t)BitOperations::SingleToUInt32Bits(FloatingPointUtils::convertToSingle(constValue)) + : (int64_t)BitOperations::DoubleToUInt64Bits(constValue); + bool fitsInLui = ((bits & 0xfff) == 0) && emitter::isValidSimm20(bits >> 12); + if (fitsInLui || emitter::isValidSimm12(bits)) // can we synthesize bits with a single instruction? { - // Get a temp integer register to compute long address. - // regNumber addrReg = internalRegisters.GetSingle(tree); + regNumber temp = internalRegisters.GetSingle(tree); + if (fitsInLui) + { + emit->emitIns_R_I(INS_lui, size, temp, bits >> 12); + } + else + { + emit->emitIns_R_R_I(INS_addi, size, temp, REG_ZERO, bits); + } - // We must load the FP constant from the constant pool - // Emit a data section constant for the float or double constant. - CORINFO_FIELD_HANDLE hnd = emit->emitFltOrDblConst(constValue, size); + emit->emitIns_R_R(size == EA_4BYTE ? INS_fmv_w_x : INS_fmv_d_x, size, targetReg, temp); + break; + } - // Load the FP constant. - assert(emit->isFloatReg(targetReg)); + // We must load the FP constant from the constant pool + // Emit a data section constant for the float or double constant. + CORINFO_FIELD_HANDLE hnd = emit->emitFltOrDblConst(constValue, size); - // Compute the address of the FP constant and load the data. - emit->emitIns_R_C(size == EA_4BYTE ? INS_flw : INS_fld, size, targetReg, REG_NA, hnd, 0); - } + // Compute the address of the FP constant and load the data. + emit->emitIns_R_C(size == EA_4BYTE ? INS_flw : INS_fld, size, targetReg, REG_NA, hnd, 0); } break; @@ -3581,7 +3596,24 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) unreached(); } - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm); + GenTreeIntCon* con = op2->AsIntCon(); + + emitAttr attr = emitActualTypeSize(op2Type); + // TODO-CQ: Currently we cannot do this for all handles because of + // https://github.com/dotnet/runtime/issues/60712 + if (con->ImmedValNeedsReloc(compiler)) + { + attr = EA_SET_FLG(attr, EA_CNS_RELOC_FLG); + } + + if (op2Type == TYP_BYREF) + { + attr = EA_SET_FLG(attr, EA_BYREF_FLG); + } + + instGen_Set_Reg_To_Imm(attr, REG_RA, imm, + INS_FLAGS_DONT_CARE DEBUGARG(con->gtTargetHandle) DEBUGARG(con->gtFlags)); + regSet.verifyRegUsed(REG_RA); regs = (int)REG_RA << 5; } else diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 00fe1977a0695a..811fa2d56035a9 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5262,6 +5262,13 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl DoPhase(this, PHASE_OPTIMIZE_LAYOUT, lateLayoutPhase); } + else + { + // If we didn't run 3-opt, we might still have a profile-aware DFS tree computed during LSRA available. + // This tree's presence can trigger asserts if pre/postorder numbers are recomputed, + // so invalidate the tree either way. + fgInvalidateDfsTree(); + } // Now that the flowgraph is finalized, run post-layout optimizations. // diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index dd967f33cbed9e..2f5d219ed94d43 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -8247,10 +8247,8 @@ class Compiler // Used for respective assertion propagations. AssertionIndex optAssertionIsSubrange(GenTree* tree, IntegralRange range, ASSERT_VALARG_TP assertions); AssertionIndex optAssertionIsSubtype(GenTree* tree, GenTree* methodTableArg, ASSERT_VALARG_TP assertions); - AssertionIndex optAssertionIsNonNullInternal(GenTree* op, ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased)); bool optAssertionVNIsNonNull(ValueNum vn, ASSERT_VALARG_TP assertions); - bool optAssertionIsNonNull(GenTree* op, - ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased) DEBUGARG(AssertionIndex* pIndex)); + bool optAssertionIsNonNull(GenTree* op, ASSERT_VALARG_TP assertions); AssertionIndex optGlobalAssertionIsEqualOrNotEqual(ASSERT_VALARG_TP assertions, GenTree* op1, GenTree* op2); AssertionIndex optGlobalAssertionIsEqualOrNotEqualZero(ASSERT_VALARG_TP assertions, GenTree* op1); diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index c7766b91a40d2b..ef00369ea1b309 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -5062,12 +5062,22 @@ void Compiler::fgVisitBlocksInLoopAwareRPO(FlowGraphDfsTree* dfsTree, FlowGraphN } }; - LoopAwareVisitor visitor(dfsTree, loops, func); - - for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--) + if (loops->NumLoops() == 0) + { + for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--) + { + BasicBlock* const block = dfsTree->GetPostOrder(i - 1); + func(block); + } + } + else { - BasicBlock* const block = dfsTree->GetPostOrder(i - 1); - visitor.VisitBlock(block); + LoopAwareVisitor visitor(dfsTree, loops, func); + for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--) + { + BasicBlock* const block = dfsTree->GetPostOrder(i - 1); + visitor.VisitBlock(block); + } } } diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 7ee0d80aa9dc49..7f5cf679ff64f8 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -4663,120 +4663,86 @@ void Compiler::fgDoReversePostOrderLayout() } #endif // DEBUG - // If LSRA didn't create any new blocks, we can reuse its loop-aware RPO traversal, - // which is cached in Compiler::fgBBs. - // If the cache isn't available, we need to recompute the loop-aware RPO. + // If LSRA didn't create any new blocks, we can reuse its flowgraph annotations. // - BasicBlock** rpoSequence = fgBBs; - - if (rpoSequence == nullptr) + if (m_dfsTree == nullptr) { - assert(m_dfsTree == nullptr); - m_dfsTree = fgComputeDfs(); - FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(m_dfsTree); - rpoSequence = new (this, CMK_BasicBlock) BasicBlock*[m_dfsTree->GetPostOrderCount()]; - unsigned index = 0; - auto addToSequence = [rpoSequence, &index](BasicBlock* block) { - rpoSequence[index++] = block; - }; - - fgVisitBlocksInLoopAwareRPO(m_dfsTree, loops, addToSequence); + m_dfsTree = fgComputeDfs(); + m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); } else { - assert(m_dfsTree != nullptr); + assert(m_loops != nullptr); } - // Fast path: We don't have any EH regions, so just reorder the blocks - // - if (compHndBBtabCount == 0) - { - for (unsigned i = 1; i < m_dfsTree->GetPostOrderCount(); i++) + BasicBlock** const rpoSequence = new (this, CMK_BasicBlock) BasicBlock*[m_dfsTree->GetPostOrderCount()]; + unsigned numBlocks = 0; + auto addToSequence = [rpoSequence, &numBlocks](BasicBlock* block) { + // Exclude handler regions from being reordered. + // + if (!block->hasHndIndex()) { - BasicBlock* const block = rpoSequence[i - 1]; - BasicBlock* const blockToMove = rpoSequence[i]; - - if (!block->NextIs(blockToMove)) - { - fgUnlinkBlock(blockToMove); - fgInsertBBafter(block, blockToMove); - } + rpoSequence[numBlocks++] = block; } + }; - fgMoveHotJumps(); - - return; - } + fgVisitBlocksInLoopAwareRPO(m_dfsTree, m_loops, addToSequence); - // The RPO will break up call-finally pairs, so save them before re-ordering + // Reorder blocks. // - struct CallFinallyPair + for (unsigned i = 1; i < numBlocks; i++) { - BasicBlock* callFinally; - BasicBlock* callFinallyRet; + BasicBlock* block = rpoSequence[i - 1]; + BasicBlock* const blockToMove = rpoSequence[i]; - // Constructor provided so we can call ArrayStack::Emplace - // - CallFinallyPair(BasicBlock* first, BasicBlock* second) - : callFinally(first) - , callFinallyRet(second) + if (block->NextIs(blockToMove)) { + continue; } - }; - - ArrayStack callFinallyPairs(getAllocator()); - for (EHblkDsc* const HBtab : EHClauses(this)) - { - if (HBtab->HasFinallyHandler()) + // Only reorder blocks within the same try region. We don't want to make them non-contiguous. + // + if (!BasicBlock::sameTryRegion(block, blockToMove)) { - for (BasicBlock* const pred : HBtab->ebdHndBeg->PredBlocks()) - { - assert(pred->KindIs(BBJ_CALLFINALLY)); - if (pred->isBBCallFinallyPair()) - { - callFinallyPairs.Emplace(pred, pred->Next()); - } - } + continue; } - } - // Reorder blocks - // - for (unsigned i = 1; i < m_dfsTree->GetPostOrderCount(); i++) - { - BasicBlock* const block = rpoSequence[i - 1]; - BasicBlock* const blockToMove = rpoSequence[i]; + // Don't move call-finally pair tails independently. + // When we encounter the head, we will move the entire pair. + // + if (blockToMove->isBBCallFinallyPairTail()) + { + continue; + } - // Only reorder blocks within the same EH region -- we don't want to make them non-contiguous + // Don't break up call-finally pairs by inserting something in the middle. // - if (BasicBlock::sameEHRegion(block, blockToMove)) + if (block->isBBCallFinallyPair()) { - // Don't reorder EH regions with filter handlers -- we want the filter to come first - // - if (block->hasHndIndex() && ehGetDsc(block->getHndIndex())->HasFilter()) - { - continue; - } + block = block->Next(); + } - if (!block->NextIs(blockToMove)) - { - fgUnlinkBlock(blockToMove); - fgInsertBBafter(block, blockToMove); - } + if (blockToMove->isBBCallFinallyPair()) + { + BasicBlock* const callFinallyRet = blockToMove->Next(); + fgUnlinkRange(blockToMove, callFinallyRet); + fgMoveBlocksAfter(blockToMove, callFinallyRet, block); + } + else + { + fgUnlinkBlock(blockToMove); + fgInsertBBafter(block, blockToMove); } } - // Fix up call-finally pairs - // - for (int i = 0; i < callFinallyPairs.Height(); i++) + if (compHndBBtabCount == 0) { - const CallFinallyPair& pair = callFinallyPairs.BottomRef(i); - fgUnlinkBlock(pair.callFinallyRet); - fgInsertBBafter(pair.callFinally, pair.callFinallyRet); + fgMoveHotJumps(); + } + else + { + fgMoveHotJumps(); } - - fgMoveHotJumps(); } //----------------------------------------------------------------------------- @@ -5133,14 +5099,6 @@ void Compiler::ThreeOptLayout::ConsiderEdge(FlowEdge* edge) return; } - // Don't waste time reordering within handler regions. - // Note that if a finally region is sufficiently hot, - // we should have cloned it into the main method body already. - if (srcBlk->hasHndIndex() || dstBlk->hasHndIndex()) - { - return; - } - // For backward jumps, we will consider partitioning before 'srcBlk'. // If 'srcBlk' is a BBJ_CALLFINALLYRET, this partition will split up a call-finally pair. // Thus, don't consider edges out of BBJ_CALLFINALLYRET blocks. @@ -5256,7 +5214,8 @@ void Compiler::ThreeOptLayout::Run() // Initialize the current block order for (BasicBlock* const block : compiler->Blocks(compiler->fgFirstBB, finalBlock)) { - if (!compiler->m_dfsTree->Contains(block)) + // Exclude unreachable blocks and handler blocks from being reordered + if (!compiler->m_dfsTree->Contains(block) || block->hasHndIndex()) { continue; } @@ -5289,14 +5248,14 @@ void Compiler::ThreeOptLayout::Run() continue; } - // Only reorder within EH regions to maintain contiguity. - if (!BasicBlock::sameEHRegion(block, next)) + // Only reorder within try regions to maintain contiguity. + if (!BasicBlock::sameTryRegion(block, next)) { continue; } - // Don't move the entry of an EH region. - if (compiler->bbIsTryBeg(next) || compiler->bbIsHandlerBeg(next)) + // Don't move the entry of a try region. + if (compiler->bbIsTryBeg(next)) { continue; } diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 5c70851b5c8b7a..dc4f2c312045e4 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -972,7 +972,7 @@ void LinearScan::setBlockSequence() FlowGraphDfsTree* const dfsTree = compiler->m_dfsTree; blockSequence = new (compiler, CMK_LSRA) BasicBlock*[compiler->fgBBcount]; - if (compiler->opts.OptimizationEnabled() && dfsTree->HasCycle()) + if (compiler->opts.OptimizationEnabled()) { // Ensure loop bodies are compact in the visitation order. compiler->m_loops = FlowGraphNaturalLoops::Find(dfsTree); @@ -1333,15 +1333,10 @@ PhaseStatus LinearScan::doLinearScan() compiler->compLSRADone = true; // If edge resolution didn't create new blocks, - // cache the block sequence so it can be used as an initial layout during block reordering. - if (compiler->fgBBcount == bbSeqCount) - { - compiler->fgBBs = blockSequence; - } - else + // we can reuse the current flowgraph annotations during block layout. + if (compiler->fgBBcount != bbSeqCount) { assert(compiler->fgBBcount > bbSeqCount); - compiler->fgBBs = nullptr; compiler->fgInvalidateDfsTree(); } diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index 1185eac4cea938..7583d7687d54f2 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -143,10 +143,22 @@ int LinearScan::BuildNode(GenTree* tree) case GT_CNS_DBL: { - // There is no instruction for loading float/double imm directly into FPR. - // Reserve int to load constant from memory (IF_LARGELDC) - buildInternalIntRegisterDefForNode(tree); - buildInternalRegisterUses(); + emitAttr size = emitActualTypeSize(tree); + + double constValue = tree->AsDblCon()->DconValue(); + if (!FloatingPointUtils::isPositiveZero(constValue)) + { + int64_t bits = + (size == EA_4BYTE) + ? (int32_t)BitOperations::SingleToUInt32Bits(FloatingPointUtils::convertToSingle(constValue)) + : (int64_t)BitOperations::DoubleToUInt64Bits(constValue); + bool fitsInLui = ((bits & 0xfff) == 0) && emitter::isValidSimm20(bits >> 12); + if (fitsInLui || emitter::isValidSimm12(bits)) // can we synthesize bits with a single instruction? + { + buildInternalIntRegisterDefForNode(tree); + buildInternalRegisterUses(); + } + } } FALLTHROUGH; diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index ecd7f4fbdf475c..3de41dc1bccf3c 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -12123,28 +12123,15 @@ bool Compiler::GetObjectHandleAndOffset(GenTree* tree, ssize_t* byteOffset, CORI return false; } - ValueNum treeVN = tree->gtVNPair.GetLiberal(); - VNFuncApp funcApp; - target_ssize_t offset = 0; - while (vnStore->GetVNFunc(treeVN, &funcApp) && (funcApp.m_func == (VNFunc)GT_ADD)) + ValueNum treeVN = tree->gtVNPair.GetLiberal(); + if (treeVN == ValueNumStore::NoVN) { - if (vnStore->IsVNConstantNonHandle(funcApp.m_args[0]) && (vnStore->TypeOfVN(funcApp.m_args[0]) == TYP_I_IMPL)) - { - offset += vnStore->ConstantValue(funcApp.m_args[0]); - treeVN = funcApp.m_args[1]; - } - else if (vnStore->IsVNConstantNonHandle(funcApp.m_args[1]) && - (vnStore->TypeOfVN(funcApp.m_args[1]) == TYP_I_IMPL)) - { - offset += vnStore->ConstantValue(funcApp.m_args[1]); - treeVN = funcApp.m_args[0]; - } - else - { - return false; - } + return false; } + target_ssize_t offset = 0; + vnStore->PeelOffsets(&treeVN, &offset); + if (vnStore->IsVNObjHandle(treeVN)) { *pObj = vnStore->ConstantObjHandle(treeVN); @@ -12269,7 +12256,6 @@ bool Compiler::fgValueNumberConstLoad(GenTreeIndir* tree) size_t index = -1; // First, let see if we have PtrToArrElem - ValueNum addr = funcApp.m_args[0]; if (funcApp.m_func == VNF_PtrToArrElem) { ValueNum arrVN = funcApp.m_args[1]; @@ -12283,34 +12269,9 @@ bool Compiler::fgValueNumberConstLoad(GenTreeIndir* tree) } else if (funcApp.m_func == (VNFunc)GT_ADD) { - ssize_t dataOffset = 0; - ValueNum baseVN = ValueNumStore::NoVN; - - // Loop to accumulate total dataOffset, e.g.: - // ADD(C1, ADD(ObjHandle, C2)) -> C1 + C2 - do - { - ValueNum op1VN = funcApp.m_args[0]; - ValueNum op2VN = funcApp.m_args[1]; - - if (vnStore->IsVNConstant(op1VN) && varTypeIsIntegral(vnStore->TypeOfVN(op1VN)) && - !isCnsObjHandle(vnStore, op1VN, &objHandle)) - { - dataOffset += vnStore->CoercedConstantValue(op1VN); - baseVN = op2VN; - } - else if (vnStore->IsVNConstant(op2VN) && varTypeIsIntegral(vnStore->TypeOfVN(op2VN)) && - !isCnsObjHandle(vnStore, op2VN, &objHandle)) - { - dataOffset += vnStore->CoercedConstantValue(op2VN); - baseVN = op1VN; - } - else - { - // one of the args is expected to be an integer constant - return false; - } - } while (vnStore->GetVNFunc(baseVN, &funcApp) && (funcApp.m_func == (VNFunc)GT_ADD)); + target_ssize_t dataOffset = 0; + vnStore->PeelOffsets(&addrVN, &dataOffset); + ValueNum baseVN = addrVN; if (isCnsObjHandle(vnStore, baseVN, &objHandle) && (dataOffset >= (ssize_t)OFFSETOF__CORINFO_String__chars) && ((dataOffset % 2) == 0)) @@ -14434,67 +14395,27 @@ void Compiler::fgValueNumberAddExceptionSetForIndirection(GenTree* tree, GenTree ValueNumPair baseVNP = vnStore->VNPNormalPair(baseAddr->gtVNPair); ValueNum baseLVN = baseVNP.GetLiberal(); ValueNum baseCVN = baseVNP.GetConservative(); - ssize_t offsetL = 0; - ssize_t offsetC = 0; - VNFuncApp funcAttr; - while (vnStore->GetVNFunc(baseLVN, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD) && - (vnStore->TypeOfVN(baseLVN) == TYP_BYREF)) - { - // The arguments in value numbering functions are sorted in increasing order - // Thus either arg could be the constant. - if (vnStore->IsVNConstant(funcAttr.m_args[0]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0]))) - { - offsetL += vnStore->CoercedConstantValue(funcAttr.m_args[0]); - baseLVN = funcAttr.m_args[1]; - } - else if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1]))) - { - offsetL += vnStore->CoercedConstantValue(funcAttr.m_args[1]); - baseLVN = funcAttr.m_args[0]; - } - else // neither argument is a constant - { - break; - } + assert(baseVNP.BothDefined()); - if (fgIsBigOffset(offsetL)) - { - // Failure: Exit this loop if we have a "big" offset + target_ssize_t offsetL = 0; + target_ssize_t offsetC = 0; - // reset baseLVN back to the full address expression - baseLVN = baseVNP.GetLiberal(); - break; - } - } - - while (vnStore->GetVNFunc(baseCVN, &funcAttr) && (funcAttr.m_func == (VNFunc)GT_ADD) && - (vnStore->TypeOfVN(baseCVN) == TYP_BYREF)) + // baseAddr could be a SIMD for certain implicit indirs, e.g. GatherVector API. + if (!varTypeIsSIMD(baseAddr)) { - // The arguments in value numbering functions are sorted in increasing order - // Thus either arg could be the constant. - if (vnStore->IsVNConstant(funcAttr.m_args[0]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[0]))) - { - offsetL += vnStore->CoercedConstantValue(funcAttr.m_args[0]); - baseCVN = funcAttr.m_args[1]; - } - else if (vnStore->IsVNConstant(funcAttr.m_args[1]) && varTypeIsIntegral(vnStore->TypeOfVN(funcAttr.m_args[1]))) - { - offsetC += vnStore->CoercedConstantValue(funcAttr.m_args[1]); - baseCVN = funcAttr.m_args[0]; - } - else // neither argument is a constant + vnStore->PeelOffsets(&baseLVN, &offsetL); + if (fgIsBigOffset(offsetL)) { - break; + // Reset baseLVN back to the full address expression + baseLVN = baseVNP.GetLiberal(); } + vnStore->PeelOffsets(&baseCVN, &offsetC); if (fgIsBigOffset(offsetC)) { - // Failure: Exit this loop if we have a "big" offset - - // reset baseCVN back to the full address expression + // Reset baseCVN back to the full address expression baseCVN = baseVNP.GetConservative(); - break; } } @@ -15247,12 +15168,14 @@ void ValueNumStore::PeelOffsets(ValueNum* vn, target_ssize_t* offset) VNFuncApp app; while (GetVNFunc(*vn, &app) && (app.m_func == VNF_ADD)) { - if (IsVNConstantNonHandle(app.m_args[0])) + // We don't treat handles and null as constant offset. + + if (IsVNConstantNonHandle(app.m_args[0]) && (app.m_args[0] != VNForNull())) { *offset += ConstantValue(app.m_args[0]); *vn = app.m_args[1]; } - else if (IsVNConstantNonHandle(app.m_args[1])) + else if (IsVNConstantNonHandle(app.m_args[1]) && (app.m_args[1] != VNForNull())) { *offset += ConstantValue(app.m_args[1]); *vn = app.m_args[0]; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.NativeAot.cs index e512d04c3ac099..4c9356e0ad533b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.NativeAot.cs @@ -158,7 +158,7 @@ internal partial struct TypeNameResolver { if (assembly is RuntimeAssemblyInfo runtimeAssembly) { - type = runtimeAssembly.GetTypeCore(TypeNameHelpers.Unescape(escapedTypeName), throwOnError: _throwOnError, ignoreCase: _ignoreCase); + type = runtimeAssembly.GetTypeCore(TypeName.Unescape(escapedTypeName), throwOnError: _throwOnError, ignoreCase: _ignoreCase); } else { @@ -173,7 +173,7 @@ internal partial struct TypeNameResolver } else { - string? unescapedTypeName = TypeNameHelpers.Unescape(escapedTypeName); + string? unescapedTypeName = TypeName.Unescape(escapedTypeName); RuntimeAssemblyInfo? defaultAssembly = null; if (_defaultAssemblyName != null) @@ -235,7 +235,7 @@ internal partial struct TypeNameResolver if (_throwOnError) { throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType, - nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : TypeNameHelpers.Unescape(escapedTypeName)), + nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : TypeName.Unescape(escapedTypeName)), typeName: parsedName.FullName); } return null; diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index 26d59f72d02b37..7f93e48a8949f6 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -56,7 +56,7 @@ public static List BuildPathList(IReadOnlyList tokens) public static TargetOS GetTargetOS(string token) { - if(string.IsNullOrEmpty(token)) + if (string.IsNullOrEmpty(token)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return TargetOS.Windows; @@ -87,7 +87,7 @@ public static TargetOS GetTargetOS(string token) public static TargetArchitecture GetTargetArchitecture(string token) { - if(string.IsNullOrEmpty(token)) + if (string.IsNullOrEmpty(token)) { return RuntimeInformation.ProcessArchitecture switch { diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs index aed078619dfd13..caf1dad16a1af5 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs @@ -126,7 +126,7 @@ public void EmitRETIfZero(Register regSrc) public void EmitJMPIfZero(Register regSrc, ISymbolNode symbol) { - uint offset = symbol.RepresentsIndirectionCell ? 28u : 8u; + uint offset = symbol.RepresentsIndirectionCell ? 28u : 12u; uint encodedOffset = ((offset & 0x1e) << 7) | ((offset & 0x7e0) << 20) | ((offset & 0x800) >> 4) | ((offset & 0x1000) << 19); // bne regSrc, x0, offset Builder.EmitUInt((uint)(0x00001063 | ((uint)regSrc << 15) | encodedOffset)); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c635b416708be2..70dde2b702ea3b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -270,9 +270,9 @@ IntPtr LocalObjectToHandle(object input) bool isType = nativeSchema[index + 1].InstrumentationKind == PgoInstrumentationKind.HandleHistogramTypes; - fixed(PgoInstrumentationSchema* pSchema = &nativeSchema[index]) + fixed (PgoInstrumentationSchema* pSchema = &nativeSchema[index]) { - fixed(byte* pInstrumentationData = &instrumentationData[0]) + fixed (byte* pInstrumentationData = &instrumentationData[0]) { // We're going to store only the most popular type/method to reduce size of the profile LikelyClassMethodRecord* likelyClassMethods = stackalloc LikelyClassMethodRecord[1]; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs index e4f67b4070fe42..7752d65befc82c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs @@ -497,17 +497,17 @@ public void ExpandInstructionSetByImplication(TargetArchitecture architecture) public static InstructionSet ConvertToImpliedInstructionSetForVectorInstructionSets(TargetArchitecture architecture, InstructionSet input) { - switch(architecture) + switch (architecture) { case TargetArchitecture.ARM64: - switch(input) + switch (input) { case InstructionSet.ARM64_Vector64: return InstructionSet.ARM64_AdvSimd; case InstructionSet.ARM64_Vector128: return InstructionSet.ARM64_AdvSimd; } break; case TargetArchitecture.X64: - switch(input) + switch (input) { case InstructionSet.X64_Vector128: return InstructionSet.X64_SSE; case InstructionSet.X64_Vector256: return InstructionSet.X64_AVX; @@ -515,7 +515,7 @@ public static InstructionSet ConvertToImpliedInstructionSetForVectorInstructionS } break; case TargetArchitecture.X86: - switch(input) + switch (input) { case InstructionSet.X86_Vector128: return InstructionSet.X86_SSE; case InstructionSet.X86_Vector256: return InstructionSet.X86_AVX; @@ -984,7 +984,7 @@ private static InstructionSetFlags ExpandInstructionSetByReverseImplicationHelpe do { oldflags = resultflags; - switch(architecture) + switch (architecture) { case TargetArchitecture.ARM64: diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs index 97bceb5b1b8362..67690ee960073b 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemConstraintsHelpers.cs @@ -162,7 +162,7 @@ private static bool CanCastConstraint(ref ArrayBuilder instantiatedCon public static bool CheckValidInstantiationArguments(this Instantiation instantiation) { - foreach(var arg in instantiation) + foreach (var arg in instantiation) { if (arg.IsPointer || arg.IsByRef || arg.IsGenericParameter || arg.IsVoid) return false; diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs b/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs index 7147f65cbb681f..a7fe823d3df95d 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs @@ -182,7 +182,7 @@ public TypeDesc GetInlineArrayType(InlineArrayCandidate candidate) public FieldDesc GetPInvokeLazyFixupField(MethodDesc method, MethodSignature nativeSig) { - return _pInvokeLazyFixupFieldHashtable.GetOrCreateValue(new (method, nativeSig)); + return _pInvokeLazyFixupFieldHashtable.GetOrCreateValue(new(method, nativeSig)); } public MethodDesc GetPInvokeCalliStub(MethodSignature signature, ModuleDesc moduleContext) diff --git a/src/coreclr/tools/SuperFileCheck/Program.cs b/src/coreclr/tools/SuperFileCheck/Program.cs index 540f1df989d8aa..b7803582051c4f 100644 --- a/src/coreclr/tools/SuperFileCheck/Program.cs +++ b/src/coreclr/tools/SuperFileCheck/Program.cs @@ -660,7 +660,7 @@ static async Task Main(string[] args) Console.Write(String.Join(' ', methodDeclInfos.Select(x => x.FullyQualifiedName))); return 0; } - catch(Exception ex) + catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(ex.Message); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs index 8b1a535fdfca58..d87edb86db983f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceDispatchMapNode.cs @@ -179,7 +179,7 @@ private void EmitDispatchMap(ref ObjectDataBuilder builder, NodeFactory factory) if (!declMethod.Signature.IsStatic && !needsEntriesForInstanceInterfaceMethodImpls) continue; - if(!interfaceType.IsTypeDefinition) + if (!interfaceType.IsTypeDefinition) declMethod = factory.TypeSystemContext.GetMethodForInstantiatedType(declMethod.GetTypicalMethodDefinition(), (InstantiatedType)definitionInterfaceType); var implMethod = declMethod.Signature.IsStatic ? diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs index 86d61a8f86f67e..8815fea96e9b56 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/InterfaceGenericVirtualMethodTableNode.cs @@ -69,7 +69,7 @@ public static void GetGenericVirtualMethodImplementationDependencies(ref Depende if (!openImplementationType.IsInterface) { - for(int index = 0; index < openImplementationType.RuntimeInterfaces.Length; index++) + for (int index = 0; index < openImplementationType.RuntimeInterfaces.Length; index++) { if (openImplementationType.RuntimeInterfaces[index] == callingMethod.OwningType) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs index 17e61805721ea2..24f84ba902eef1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs @@ -55,7 +55,7 @@ public static bool MethodHasAssociatedData(IMethodNode methodNode) { // Instantiating unboxing stubs. We need to store their non-unboxing target pointer (looked up by runtime) ISpecialUnboxThunkNode unboxThunk = methodNode as ISpecialUnboxThunkNode; - if(unboxThunk != null && unboxThunk.IsSpecialUnboxingThunk) + if (unboxThunk != null && unboxThunk.IsSpecialUnboxingThunk) return true; return false; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs index 1ed3a086cb1a99..b5bb5760b5e29f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeGVMEntriesNode.cs @@ -65,7 +65,7 @@ public override IEnumerable GetStaticDependencies(NodeFacto { _staticDependencies = new DependencyList(); - foreach(var entry in ScanForGenericVirtualMethodEntries()) + foreach (var entry in ScanForGenericVirtualMethodEntries()) GenericVirtualMethodTableNode.GetGenericVirtualMethodImplementationDependencies(ref _staticDependencies, context, entry.CallingMethod, entry.ImplementationMethod); foreach (var entry in ScanForInterfaceGenericVirtualMethodEntries()) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs index 49fc928c06eb93..f4645566fa52b8 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExportsFileWriter.cs @@ -41,7 +41,7 @@ public void EmitExportedMethods() foreach (var method in _methods) streamWriter.WriteLine($" {method.GetUnmanagedCallersOnlyExportName()}"); } - else if(_context.Target.IsApplePlatform) + else if (_context.Target.IsApplePlatform) { foreach (string symbol in _exportSymbols) streamWriter.WriteLine($"_{symbol}"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs index 2629c3960594b4..d12d18c4c0afa1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AttributePresenceFilterNode.cs @@ -439,7 +439,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) bucketCount *= 2; } } - while(tryAgainWithBiggerTable && ((countOfRetries++) < 2)); + while (tryAgainWithBiggerTable && ((countOfRetries++) < 2)); byte[] result; if (tryAgainWithBiggerTable) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs index f6522b1492bd59..7d0275ff2eaaee 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DevirtualizationManager.cs @@ -143,7 +143,7 @@ protected override MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefTyp if (!implType.IsObject) { TypeDesc typeThatDerivesFromObject = implType; - while(!typeThatDerivesFromObject.BaseType.IsObject) + while (!typeThatDerivesFromObject.BaseType.IsObject) { typeThatDerivesFromObject = typeThatDerivesFromObject.BaseType; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs index 54d0c1968ea8f4..0cf6a8cb727638 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs @@ -126,7 +126,7 @@ private EmptyProfileData() { } - public override MibcConfig Config { get; } = new (); + public override MibcConfig Config { get; } = new(); public override bool PartialNGen => false; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index e1803a3f5d7931..6f7585c07e3ab8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -801,7 +801,7 @@ void CompileMethodList(IEnumerable> methodList) void CompilationThread(object objThreadId) { - while(true) + while (true) { _compilationThreadSemaphore.Wait(); lock(this) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs index b083e3ecc2d53f..ed05deb5f23581 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs @@ -24,7 +24,7 @@ public struct PInvokeILEmitter private readonly MethodDesc _targetMethod; private readonly Marshaller[] _marshallers; private readonly PInvokeMetadata _importMetadata; - private static readonly ConditionalWeakTable> s_contexts = new (); + private static readonly ConditionalWeakTable> s_contexts = new(); private PInvokeILEmitter(MethodDesc targetMethod) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 7cc3343a56d416..f58775b559198c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -215,7 +215,7 @@ TypeDesc HandleContext(IEcmaModule module, EntityHandle handle, TypeDesc methodT { derivesFromTypeDefinition = currentType.GetTypeDefinition() == tokenOnlyOwningType; currentType = currentType.BaseType; - } while(currentType != null && !derivesFromTypeDefinition); + } while (currentType != null && !derivesFromTypeDefinition); if (derivesFromTypeDefinition) { @@ -311,7 +311,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return Method.GetHashCode() ^ unchecked(17 * Token.GetHashCode()) ^ unchecked (39 * (ConstrainedType?.GetHashCode() ?? 0)); + return Method.GetHashCode() ^ unchecked(17 * Token.GetHashCode()) ^ unchecked(39 * (ConstrainedType?.GetHashCode() ?? 0)); } public bool Equals(MethodWithToken methodWithToken) @@ -2113,7 +2113,7 @@ private void ceeInfoGetCallInfo( // of shared generic code calling a shared generic implementation method, which should be rare. // // An alternative design would be to add a new generic dictionary entry kind to hold the MethodDesc - // of the constrained target instead, and use that in some circumstances; however, implementation of + // of the constrained target instead, and use that in some circumstances; however, implementation of // that design requires refactoring variuos parts of the JIT interface as well as // TryResolveConstraintMethodApprox. In particular we would need to be abled to embed a constrained lookup // via EmbedGenericHandle, as well as decide in TryResolveConstraintMethodApprox if the call can be made @@ -3152,7 +3152,7 @@ private void setEHinfo(uint EHnumber, ref CORINFO_EH_CLAUSE clause) // Currently, the only place we are using a token here is for a COM-to-CLR exception-to-HRESULT // mapping catch clause. We want this catch clause to catch all exceptions, so we override the - // token to be mdTypeRefNil, which used by the EH system to mean catch(...) + // token to be mdTypeRefNil, which used by the EH system to mean catch (...) Debug.Assert(clauseType.IsObject); clause.ClassTokenOrOffset = 0; } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcSlotTable.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcSlotTable.cs index 3c670f7d0f5a4f..534780b671834f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcSlotTable.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/x86/GcSlotTable.cs @@ -81,7 +81,7 @@ public override string ToString() sb.Append($" LowBits: "); if ((Flags & GcSlotFlags.GC_SLOT_UNTRACKED) != 0) { - if((LowBits & pinned_OFFSET_FLAG) != 0) sb.Append("pinned "); + if ((LowBits & pinned_OFFSET_FLAG) != 0) sb.Append("pinned "); if ((LowBits & byref_OFFSET_FLAG) != 0) sb.Append("byref "); } sb.AppendLine(); diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs index c62434f172dd2f..2f89b128c32ebe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCasesRunner/AssemblyChecker.cs @@ -1028,7 +1028,7 @@ private IEnumerable VerifyExportedTypes (AssemblyDefinition original, As protected virtual IEnumerable VerifyPseudoAttributes (MethodDefinition src, MethodDefinition linked) { var expected = (MethodAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - if(!linked.Attributes.Equals(expected)) + if (!linked.Attributes.Equals(expected)) { yield return $"Method `{src}' pseudo attributes did not match expected"; } @@ -1038,7 +1038,7 @@ protected virtual IEnumerable VerifyPseudoAttributes (TypeDefinition src { var expected = (TypeAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - if(!linked.Attributes.Equals(expected)) + if (!linked.Attributes.Equals(expected)) { yield return $"Type `{src}' pseudo attributes did not match expected"; } @@ -1047,7 +1047,7 @@ protected virtual IEnumerable VerifyPseudoAttributes (TypeDefinition src protected virtual IEnumerable VerifyPseudoAttributes (FieldDefinition src, FieldDefinition linked) { var expected = (FieldAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - if(!linked.Attributes.Equals(expected)) + if (!linked.Attributes.Equals(expected)) { yield return $"Field `{src}' pseudo attributes did not match expected"; } @@ -1056,7 +1056,7 @@ protected virtual IEnumerable VerifyPseudoAttributes (FieldDefinition sr protected virtual IEnumerable VerifyPseudoAttributes (PropertyDefinition src, PropertyDefinition linked) { var expected = (PropertyAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - if(!linked.Attributes.Equals(expected)) + if (!linked.Attributes.Equals(expected)) { yield return $"Property `{src}' pseudo attributes did not match expected"; } @@ -1066,7 +1066,7 @@ protected virtual IEnumerable VerifyPseudoAttributes (PropertyDefinition protected virtual IEnumerable VerifyPseudoAttributes (EventDefinition src, EventDefinition linked) { var expected = (EventAttributes) GetExpectedPseudoAttributeValue (src, (uint) src.Attributes); - if(!linked.Attributes.Equals(expected)) + if (!linked.Attributes.Equals(expected)) { yield return $"Event `{src}' pseudo attributes did not match expected"; } @@ -1077,7 +1077,7 @@ protected virtual IEnumerable VerifyCustomAttributes (ICustomAttributePr var expectedAttrs = GetExpectedAttributes (src).ToList (); var linkedAttrs = FilterLinkedAttributes (linked).ToList (); - if(!linkedAttrs.SequenceEqual(expectedAttrs)) + if (!linkedAttrs.SequenceEqual(expectedAttrs)) { yield return $"Custom attributes on `{src}' are not matching"; } @@ -1091,7 +1091,7 @@ protected virtual IEnumerable VerifySecurityAttributes (ICustomAttribute var linkedAttrs = FilterLinkedSecurityAttributes (linked).ToList (); - if(!linkedAttrs.SequenceEqual(expectedAttrs)) + if (!linkedAttrs.SequenceEqual(expectedAttrs)) { yield return $"Security attributes on `{src}' are not matching"; } diff --git a/src/coreclr/tools/cdac-build-tool/ComposeCommand.cs b/src/coreclr/tools/cdac-build-tool/ComposeCommand.cs index 7305bcd08808c4..adb3675695941a 100644 --- a/src/coreclr/tools/cdac-build-tool/ComposeCommand.cs +++ b/src/coreclr/tools/cdac-build-tool/ComposeCommand.cs @@ -14,7 +14,7 @@ internal sealed class ComposeCommand : CliCommand private readonly CliOption outputFile = new("-o") { Arity = ArgumentArity.ExactlyOne, HelpName = "OUTPUT", Required = true, Description = "Output file" }; private readonly CliOption contractFile = new("-c") { Arity = ArgumentArity.ZeroOrMore, HelpName = "CONTRACT", Description = "Contract file (may be specified multiple times)" }; private readonly CliOption baselinePath = new("-b", "--baseline") { Arity = ArgumentArity.ExactlyOne, HelpName = "BASELINEPATH", Description = "Directory containing the baseline contracts"}; - private readonly CliOption templateFile = new ("-i", "--input-template") { Arity = ArgumentArity.ExactlyOne, HelpName = "TEMPLATE", Description = "Contract descriptor template to be filled in" }; + private readonly CliOption templateFile = new("-i", "--input-template") { Arity = ArgumentArity.ExactlyOne, HelpName = "TEMPLATE", Description = "Contract descriptor template to be filled in" }; private readonly CliOption _verboseOption; public ComposeCommand(CliOption verboseOption) : base("compose") { diff --git a/src/coreclr/tools/cdac-build-tool/Program.cs b/src/coreclr/tools/cdac-build-tool/Program.cs index 132c13d30fa119..bf86ebec0db4cd 100644 --- a/src/coreclr/tools/cdac-build-tool/Program.cs +++ b/src/coreclr/tools/cdac-build-tool/Program.cs @@ -10,7 +10,7 @@ public class Program { public static async Task Main(string[] args) { - CliRootCommand rootCommand = new (); + CliRootCommand rootCommand = new(); var verboseOption = new CliOption("-v", "--verbose") {Recursive = true, Description = "Verbose"}; rootCommand.Add(verboseOption); rootCommand.Add(new DiagramDirective()); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs index a5436fbe0f22c3..90ccd89cc201a1 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs @@ -116,7 +116,7 @@ public readonly void Validate(int bufferSize) ByteLength > bufferSize || NameOffset < 0 || NameLength < 0 || - checked (NameOffset + NameLength) > ByteLength) + checked(NameOffset + NameLength) > ByteLength) { ThrowInvalidOperationException(typeof(PERF_INSTANCE_DEFINITION)); } diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs index 75744ec2c1d30c..5c8490fc96cb0c 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.cs @@ -637,7 +637,7 @@ public void Refresh() // Lastly complete any changes added to the atomicComposition during the change event atomicComposition.Complete(); - // Break out of the while(true) + // Break out of the while (true) break; } // WriteLock } // AtomicComposition diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationManagerTest.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationManagerTest.cs index f60a6fff75036e..ca65cb92533a72 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationManagerTest.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/ConfigurationManagerTest.cs @@ -131,15 +131,6 @@ public void OpenExeConfiguration2_ExePath_DoesNotExist() } } - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/21319", TargetFrameworkMonikers.NetFramework)] - public void exePath_UserLevelNone() - { - string name = TestUtil.ThisApplicationPath; - SysConfig config = ConfigurationManager.OpenExeConfiguration(name); - Assert.Equal(TestUtil.ThisApplicationPath + ".config", config.FilePath); - } - [Fact] public void exePath_UserLevelPerRoaming() { diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/TestUtil.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/TestUtil.cs index b14d833c97a218..be2254e89ce52a 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/TestUtil.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/Mono/TestUtil.cs @@ -54,15 +54,6 @@ public static void RunWithTempFiles(MyAction action) } } - public static string ThisApplicationPath - { - get - { - return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, - Assembly.GetEntryAssembly().ManifestModule.Name); - } - } - public static string ThisConfigFileName { get 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 74ddb68a640d17..fe94ac1455ddee 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 @@ -1545,7 +1545,7 @@ private void UpdateRowExecute(RowUpdatedEventArgs rowUpdatedEvent, IDbCommand da } finally { - // using Close which can optimize its { while(dataReader.NextResult()); } loop + // using Close which can optimize its { while (dataReader.NextResult()); } loop dataReader.Close(); // RecordsAffected is available after Close, but don't trust it after Dispose 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 93250857111adf..a1d7ed8b26dc69 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 @@ -53,6 +53,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) _archive = archive; _originallyInArchive = true; + Changes = ZipArchive.ChangeState.Unchanged; _diskNumberStart = cd.DiskNumberStart; _versionMadeByPlatform = (ZipVersionMadeByPlatform)cd.VersionMadeByCompatibility; @@ -60,6 +61,7 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) _versionToExtract = (ZipVersionNeededValues)cd.VersionNeededToExtract; _generalPurposeBitFlag = (BitFlagValues)cd.GeneralPurposeBitFlag; _isEncrypted = (_generalPurposeBitFlag & BitFlagValues.IsEncrypted) != 0; + // Setting CompressionMethod can change the _versionToExtract variable, which can change the value of Changes CompressionMethod = (CompressionMethodValues)cd.CompressionMethod; _lastModified = new DateTimeOffset(ZipHelper.DosTimeToDateTime(cd.LastModified)); _compressedSize = cd.CompressedSize; @@ -88,8 +90,6 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) _fileComment = cd.FileComment; _compressionLevel = MapCompressionLevel(_generalPurposeBitFlag, CompressionMethod); - - Changes = ZipArchive.ChangeState.Unchanged; } // Initializes a ZipArchiveEntry instance for a new archive entry with a specified compression level. @@ -1243,10 +1243,12 @@ private void VersionToExtractAtLeast(ZipVersionNeededValues value) if (_versionToExtract < value) { _versionToExtract = value; + Changes |= ZipArchive.ChangeState.FixedLengthMetadata; } if (_versionMadeBySpecification < value) { _versionMadeBySpecification = value; + Changes |= ZipArchive.ChangeState.FixedLengthMetadata; } } diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs index f0f63a34d61a63..10407fd6706676 100644 --- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs +++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs @@ -907,6 +907,146 @@ public static async Task ZipArchive_InvalidHuffmanData() } } + [Fact] + public static void ZipArchive_InvalidVersionToExtract() + { + using (MemoryStream updatedStream = new MemoryStream()) + { + int originalLocalVersionToExtract = s_inconsistentVersionToExtract[4]; + int originalCentralDirectoryVersionToExtract = s_inconsistentVersionToExtract[57]; + + // The existing archive will have a "version to extract" of 0.0, but will contain entries + // with deflate compression (which has a minimum version to extract of 2.0.) + Assert.Equal(0x00, originalLocalVersionToExtract); + Assert.Equal(0x00, originalCentralDirectoryVersionToExtract); + + // Write the example data to the stream. We expect to be able to read it (and the entry contents) successfully. + updatedStream.Write(s_inconsistentVersionToExtract); + updatedStream.Seek(0, SeekOrigin.Begin); + + using (ZipArchive originalArchive = new ZipArchive(updatedStream, ZipArchiveMode.Read, true)) + { + Assert.Equal(1, originalArchive.Entries.Count); + + ZipArchiveEntry firstEntry = originalArchive.Entries[0]; + + Assert.Equal("first.bin", firstEntry.Name); + Assert.Equal(10, firstEntry.Length); + + using (Stream entryStream = firstEntry.Open()) + { + Assert.Equal(10, firstEntry.Length); + + byte[] uncompressedBytes = new byte[firstEntry.Length]; + int bytesRead = entryStream.Read(uncompressedBytes); + + Assert.Equal(10, bytesRead); + + Assert.Equal(new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }, uncompressedBytes); + } + } + + updatedStream.Seek(0, SeekOrigin.Begin); + + // Create a new entry, forcing the central directory headers to be rewritten. The local file header + // for first.bin would normally be skipped (because it hasn't changed) but it needs to be rewritten + // because the central directory headers will be rewritten with a valid value and the local file header + // needs to match. + using (ZipArchive updatedArchive = new ZipArchive(updatedStream, ZipArchiveMode.Update)) + { + ZipArchiveEntry newEntry = updatedArchive.CreateEntry("second.bin", CompressionLevel.NoCompression); + } + + byte[] updatedContents = updatedStream.ToArray(); + int updatedLocalVersionToExtract = updatedContents[4]; + int updatedCentralDirectoryVersionToExtract = updatedContents[97]; + + Assert.Equal(20, updatedCentralDirectoryVersionToExtract); + Assert.Equal(20, updatedLocalVersionToExtract); + } + } + + private static readonly byte[] s_inconsistentVersionToExtract = + { + // ===== Local file header signature 0x04034b50 + 0x50, 0x4b, 0x03, 0x04, + // version to extract 0.0 (invalid - this should be at least 2.0 to make use of deflate compression) + 0x00, 0x00, + // general purpose flags + 0x02, 0x00, // 0000_0002 'for maximum-compression deflating' + // Deflate + 0x08, 0x00, + // Last mod file time + 0x3b, 0x33, + // Last mod date + 0x3f, 0x5a, + // CRC32 + 0x46, 0xd7, 0x6c, 0x45, + // compressed size + 0x0c, 0x00, 0x00, 0x00, + // uncompressed size + 0x0a, 0x00, 0x00, 0x00, + // file name length + 0x09, 0x00, + // extra field length + 0x00, 0x00, + // filename + 0x66, 0x69, 0x72, 0x73, 0x74, 0x2e, 0x62, 0x69, 0x6e, + // ------------- + // Data! + 0x63, 0x60, 0x64, 0x62, 0x66, 0x61, 0x65, 0x63, 0xe7, 0xe0, 0x04, 0x00, + // -------- Central directory signature 0x02014b50 + 0x50, 0x4b, 0x01, 0x02, + // version made by 2.0 + 0x14, 0x00, + // version to extract 0.0 (invalid - this should be at least 2.0 to make use of deflate compression) + 0x00, 0x00, + // general purpose flags + 0x02, 0x00, + // Deflate + 0x08, 0x00, + // Last mod file time + 0x3b, 0x33, + // Last mod date + 0x3f, 0x5a, + // CRC32 + 0x46, 0xd7, 0x6c, 0x45, + // compressed size + 0x0c, 0x00, 0x00, 0x00, + // uncompressed size + 0x0a, 0x00, 0x00, 0x00, + // file name length + 0x09, 0x00, + // extra field length + 0x00, 0x00, + // file comment length + 0x00, 0x00, + // disk number start + 0x00, 0x00, + // internal file attributes + 0x00, 0x00, + // external file attributes + 0x00, 0x00, 0x00, 0x00, + // relative offset of local header + 0x00, 0x00, 0x00, 0x00, + // file name + 0x66, 0x69, 0x72, 0x73, 0x74, 0x2e, 0x62, 0x69, 0x6e, + // == 'end of CD' signature 0x06054b50 + 0x50, 0x4b, 0x05, 0x06, + // disk number, disk number with CD + 0x00, 0x00, + 0x00, 0x00, + // total number of entries in CD on this disk, and overall + 0x01, 0x00, + 0x01, 0x00, + // size of CD + 0x37, 0x00, 0x00, 0x00, + // offset of start of CD wrt start disk + 0x33, 0x00, 0x00, 0x00, + // comment length + 0x00, 0x00 + }; + private static readonly byte[] s_slightlyIncorrectZip64 = { // ===== Local file header signature 0x04034b50 @@ -925,7 +1065,7 @@ public static async Task ZipArchive_InvalidHuffmanData() 0x0c, 0x7e, 0x7f, 0xd8, // compressed size 0xff, 0xff, 0xff, 0xff, - // UNcompressed size + // uncompressed size 0xff, 0xff, 0xff, 0xff, // file name length 0x08, 0x00, @@ -976,7 +1116,7 @@ public static async Task ZipArchive_InvalidHuffmanData() 0x0c, 0x7e, 0x7f, 0xd8, // 4 byte compressed size, index 120 (-1 indicates refer to Zip64 extra field) 0xff, 0xff, 0xff, 0xff, - // 4 byte UNcompressed size, index 124 (-1 indicates refer to Zip64 extra field) + // 4 byte uncompressed size, index 124 (-1 indicates refer to Zip64 extra field) 0xff, 0xff, 0xff, 0xff, // file name length 0x08, 0x00, @@ -1066,7 +1206,7 @@ public static async Task ZipArchive_InvalidHuffmanData() 0x0c, 0x7e, 0x7f, 0xd8, // compressed size 0xff, 0xff, 0xff, 0xff, - // UNcompressed size + // uncompressed size 0xff, 0xff, 0xff, 0xff, // file name length @@ -1079,7 +1219,7 @@ public static async Task ZipArchive_InvalidHuffmanData() 0x01, 0x00, // size of extra field block 0x20, 0x00, - // 8 byte Zip64 UNcompressed size, index 42 + // 8 byte Zip64 uncompressed size, index 42 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 byte Zip64 compressed size, index 50 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1122,7 +1262,7 @@ public static async Task ZipArchive_InvalidHuffmanData() 0x0c, 0x7e, 0x7f, 0xd8, // 4 byte compressed size, index 120 (-1 indicates refer to Zip64 extra field) 0xff, 0xff, 0xff, 0xff, - // 4 byte UNcompressed size, index 124 (-1 indicates refer to Zip64 extra field) + // 4 byte uncompressed size, index 124 (-1 indicates refer to Zip64 extra field) 0xff, 0xff, 0xff, 0xff, // file name length 0x08, 0x00, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/AggregateBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/AggregateBy.cs index 6456462d950637..4f3e6efb081c76 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/AggregateBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/AggregateBy.cs @@ -42,7 +42,9 @@ public static IAsyncEnumerable> AggregateBy>() : + Impl(source, keySelector, seed, func, keyComparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -117,7 +119,9 @@ public static IAsyncEnumerable> AggregateBy>() : + Impl(source, keySelector, seed, func, keyComparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -188,7 +192,9 @@ public static IAsyncEnumerable> AggregateBy>() : + Impl(source, keySelector, seedSelector, func, keyComparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -264,7 +270,9 @@ public static IAsyncEnumerable> AggregateBy>() : + Impl(source, keySelector, seedSelector, func, keyComparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -277,28 +285,26 @@ static async IAsyncEnumerable> Impl( IAsyncEnumerator enumerator = source.GetAsyncEnumerator(cancellationToken); try { - if (!await enumerator.MoveNextAsync().ConfigureAwait(false)) + if (await enumerator.MoveNextAsync().ConfigureAwait(false)) { - yield break; - } + Dictionary dict = new(keyComparer); - Dictionary dict = new(keyComparer); - - do - { - TSource value = enumerator.Current; - TKey key = await keySelector(value, cancellationToken).ConfigureAwait(false); + do + { + TSource value = enumerator.Current; + TKey key = await keySelector(value, cancellationToken).ConfigureAwait(false); - dict[key] = await func( - dict.TryGetValue(key, out TAccumulate? acc) ? acc : await seedSelector(key, cancellationToken).ConfigureAwait(false), - value, - cancellationToken).ConfigureAwait(false); - } - while (await enumerator.MoveNextAsync().ConfigureAwait(false)); + dict[key] = await func( + dict.TryGetValue(key, out TAccumulate? acc) ? acc : await seedSelector(key, cancellationToken).ConfigureAwait(false), + value, + cancellationToken).ConfigureAwait(false); + } + while (await enumerator.MoveNextAsync().ConfigureAwait(false)); - foreach (KeyValuePair countBy in dict) - { - yield return countBy; + foreach (KeyValuePair countBy in dict) + { + yield return countBy; + } } } finally diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Cast.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Cast.cs index 37121ea5f3977e..f065dacbd148d0 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Cast.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Cast.cs @@ -26,8 +26,9 @@ public static IAsyncEnumerable Cast( // satisfies the C# query { ThrowHelper.ThrowIfNull(source); - return source is IAsyncEnumerable result ? - result : + return + source.IsKnownEmpty() ? Empty() : + source as IAsyncEnumerable ?? Impl(source, default); static async IAsyncEnumerable Impl( diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Chunk.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Chunk.cs index 5392d953e1d960..f653d23970d9f4 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Chunk.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Chunk.cs @@ -30,7 +30,9 @@ public static IAsyncEnumerable Chunk( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNegativeOrZero(size); - return Chunk(source, size, default); + return + source.IsKnownEmpty() ? Empty() : + Chunk(source, size, default); async static IAsyncEnumerable Chunk( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Concat.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Concat.cs index e29d2f45384907..21d3f82988bdff 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Concat.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Concat.cs @@ -23,7 +23,10 @@ public static IAsyncEnumerable Concat( ThrowHelper.ThrowIfNull(first); ThrowHelper.ThrowIfNull(second); - return Impl(first, second, default); + return + first.IsKnownEmpty() ? second : + second.IsKnownEmpty() ? first : + Impl(first, second, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/CountBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/CountBy.cs index ba26521df5ca0a..612c21558ab115 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/CountBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/CountBy.cs @@ -28,7 +28,9 @@ public static IAsyncEnumerable> CountBy( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(keySelector); - return Impl(source, keySelector, keyComparer, default); + return + source.IsKnownEmpty() ? Empty>() : + Impl(source, keySelector, keyComparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, Func keySelector, IEqualityComparer? keyComparer, [EnumeratorCancellation] CancellationToken cancellationToken) @@ -83,7 +85,9 @@ public static IAsyncEnumerable> CountBy( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(keySelector); - return Impl(source, keySelector, keyComparer, default); + return + source.IsKnownEmpty() ? Empty>() : + Impl(source, keySelector, keyComparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, Func> keySelector, IEqualityComparer? keyComparer, [EnumeratorCancellation] CancellationToken cancellationToken) diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Distinct.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Distinct.cs index ea3d75e87bed25..eca8261d3a0996 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Distinct.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Distinct.cs @@ -21,7 +21,9 @@ public static IAsyncEnumerable Distinct( { ThrowHelper.ThrowIfNull(source); - return Impl(source, comparer, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/DistinctBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/DistinctBy.cs index 8c71a327abc514..18a228f7eb98b0 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/DistinctBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/DistinctBy.cs @@ -32,7 +32,9 @@ public static IAsyncEnumerable DistinctBy( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(keySelector); - return Impl(source, keySelector, comparer, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -86,7 +88,9 @@ public static IAsyncEnumerable DistinctBy( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(keySelector); - return Impl(source, keySelector, comparer, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Empty.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Empty.cs index fc1fb0dc7e9d78..2c39758ce16c28 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Empty.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Empty.cs @@ -16,9 +16,14 @@ public static partial class AsyncEnumerable /// An empty whose type argument is . public static IAsyncEnumerable Empty() => EmptyAsyncEnumerable.Instance; - private sealed class EmptyAsyncEnumerable : IAsyncEnumerable, IAsyncEnumerator + /// Determines whether is known to be an always-empty enumerable. + private static bool IsKnownEmpty(this IAsyncEnumerable source) => + ReferenceEquals(source, EmptyAsyncEnumerable.Instance); + + private sealed class EmptyAsyncEnumerable : + IAsyncEnumerable, IAsyncEnumerator, IOrderedAsyncEnumerable { - public static EmptyAsyncEnumerable Instance { get; } = new EmptyAsyncEnumerable(); + public static readonly EmptyAsyncEnumerable Instance = new(); public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) => this; @@ -27,6 +32,18 @@ private sealed class EmptyAsyncEnumerable : IAsyncEnumerable, public TResult Current => default!; public ValueTask DisposeAsync() => default; + + public IOrderedAsyncEnumerable CreateOrderedAsyncEnumerable(Func keySelector, IComparer? comparer, bool descending) + { + ThrowHelper.ThrowIfNull(keySelector); + return this; + } + + public IOrderedAsyncEnumerable CreateOrderedAsyncEnumerable(Func> keySelector, IComparer? comparer, bool descending) + { + ThrowHelper.ThrowIfNull(keySelector); + return this; + } } } } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Except.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Except.cs index a1946b1c0b04fa..e7a278c597abe6 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Except.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Except.cs @@ -26,7 +26,9 @@ public static IAsyncEnumerable Except( ThrowHelper.ThrowIfNull(first); ThrowHelper.ThrowIfNull(second); - return Impl(first, second, comparer, default); + return + first.IsKnownEmpty() ? Empty() : + Impl(first, second, comparer, default); async static IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -34,19 +36,34 @@ async static IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - HashSet set = new(comparer); - - await foreach (TSource element in second.WithCancellation(cancellationToken).ConfigureAwait(false)) + IAsyncEnumerator firstEnumerator = first.GetAsyncEnumerator(cancellationToken); + try { - set.Add(element); - } + if (!await firstEnumerator.MoveNextAsync().ConfigureAwait(false)) + { + yield break; + } - await foreach (TSource element in first.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - if (set.Add(element)) + HashSet set = new(comparer); + + await foreach (TSource element in second.WithCancellation(cancellationToken).ConfigureAwait(false)) { - yield return element; + set.Add(element); } + + do + { + TSource firstElement = firstEnumerator.Current; + if (set.Add(firstElement)) + { + yield return firstElement; + } + } + while (await firstEnumerator.MoveNextAsync().ConfigureAwait(false)); + } + finally + { + await firstEnumerator.DisposeAsync().ConfigureAwait(false); } } } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ExceptBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ExceptBy.cs index cabc34ceeaaa7a..1d813d8923d21d 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ExceptBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ExceptBy.cs @@ -33,7 +33,9 @@ public static IAsyncEnumerable ExceptBy( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(keySelector); - return Impl(first, second, keySelector, comparer, default); + return + first.IsKnownEmpty() ? Empty() : + Impl(first, second, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -42,19 +44,34 @@ static async IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - HashSet set = new(comparer); - - await foreach (TKey key in second.WithCancellation(cancellationToken).ConfigureAwait(false)) + IAsyncEnumerator firstEnumerator = first.GetAsyncEnumerator(cancellationToken); + try { - set.Add(key); - } + if (!await firstEnumerator.MoveNextAsync().ConfigureAwait(false)) + { + yield break; + } - await foreach (TSource element in first.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - if (set.Add(keySelector(element))) + HashSet set = new(comparer); + + await foreach (TKey key in second.WithCancellation(cancellationToken).ConfigureAwait(false)) { - yield return element; + set.Add(key); } + + do + { + TSource firstElement = firstEnumerator.Current; + if (set.Add(keySelector(firstElement))) + { + yield return firstElement; + } + } + while (await firstEnumerator.MoveNextAsync().ConfigureAwait(false)); + } + finally + { + await firstEnumerator.DisposeAsync().ConfigureAwait(false); } } } @@ -82,7 +99,9 @@ public static IAsyncEnumerable ExceptBy( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(keySelector); - return Impl(first, second, keySelector, comparer, default); + return + first.IsKnownEmpty() ? Empty() : + Impl(first, second, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -91,19 +110,34 @@ static async IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - HashSet set = new(comparer); - - await foreach (TKey key in second.WithCancellation(cancellationToken).ConfigureAwait(false)) + IAsyncEnumerator firstEnumerator = first.GetAsyncEnumerator(cancellationToken); + try { - set.Add(key); - } + if (!await firstEnumerator.MoveNextAsync().ConfigureAwait(false)) + { + yield break; + } - await foreach (TSource element in first.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - if (set.Add(await keySelector(element, cancellationToken).ConfigureAwait(false))) + HashSet set = new(comparer); + + await foreach (TKey key in second.WithCancellation(cancellationToken).ConfigureAwait(false)) { - yield return element; + set.Add(key); } + + do + { + TSource firstElement = firstEnumerator.Current; + if (set.Add(await keySelector(firstElement, cancellationToken).ConfigureAwait(false))) + { + yield return firstElement; + } + } + while (await firstEnumerator.MoveNextAsync().ConfigureAwait(false)); + } + finally + { + await firstEnumerator.DisposeAsync().ConfigureAwait(false); } } } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupBy.cs index 8eac9561756b5e..9fddf1cbd17c86 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupBy.cs @@ -32,7 +32,9 @@ public static IAsyncEnumerable> GroupBy( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(keySelector); - return Impl(source, keySelector, comparer, default); + return + source.IsKnownEmpty() ? Empty>() : + Impl(source, keySelector, comparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -67,7 +69,9 @@ public static IAsyncEnumerable> GroupBy( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(keySelector); - return Impl(source, keySelector, comparer, default); + return + source.IsKnownEmpty() ? Empty>() : + Impl(source, keySelector, comparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -111,7 +115,9 @@ public static IAsyncEnumerable> GroupBy>() : + Impl(source, keySelector, elementSelector, comparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -156,7 +162,9 @@ public static IAsyncEnumerable> GroupBy>() : + Impl(source, keySelector, elementSelector, comparer, default); static async IAsyncEnumerable> Impl( IAsyncEnumerable source, @@ -200,7 +208,9 @@ public static IAsyncEnumerable GroupBy( ThrowHelper.ThrowIfNull(keySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(source, keySelector, resultSelector, comparer, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, keySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -209,10 +219,12 @@ static async IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - var lookup = (AsyncLookup)await ToLookupAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false); - foreach (TResult item in lookup.ApplyResultSelector(resultSelector)) + if (await ToLookupAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false) is AsyncLookup lookup) { - yield return item; + foreach (TResult item in lookup.ApplyResultSelector(resultSelector)) + { + yield return item; + } } } } @@ -245,7 +257,9 @@ public static IAsyncEnumerable GroupBy( ThrowHelper.ThrowIfNull(keySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(source, keySelector, resultSelector, comparer, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, keySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -254,10 +268,12 @@ static async IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - var lookup = (AsyncLookup)await ToLookupAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false); - await foreach (TResult item in lookup.ApplyResultSelector(resultSelector, cancellationToken).ConfigureAwait(false)) + if (await ToLookupAsync(source, keySelector, comparer, cancellationToken).ConfigureAwait(false) is AsyncLookup lookup) { - yield return item; + await foreach (TResult item in lookup.ApplyResultSelector(resultSelector, cancellationToken).ConfigureAwait(false)) + { + yield return item; + } } } } @@ -294,7 +310,9 @@ public static IAsyncEnumerable GroupBy() : + Impl(source, keySelector, elementSelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -304,10 +322,12 @@ static async IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - var lookup = (AsyncLookup)await ToLookupAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false); - foreach (TResult item in lookup.ApplyResultSelector(resultSelector)) + if (await ToLookupAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false) is AsyncLookup lookup) { - yield return item; + foreach (TResult item in lookup.ApplyResultSelector(resultSelector)) + { + yield return item; + } } } } @@ -344,7 +364,9 @@ public static IAsyncEnumerable GroupBy() : + Impl(source, keySelector, elementSelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -354,10 +376,12 @@ static async IAsyncEnumerable Impl( IEqualityComparer? comparer, [EnumeratorCancellation] CancellationToken cancellationToken) { - var lookup = (AsyncLookup)await ToLookupAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false); - await foreach (TResult item in lookup.ApplyResultSelector(resultSelector, cancellationToken).ConfigureAwait(false)) + if (await ToLookupAsync(source, keySelector, elementSelector, comparer, cancellationToken).ConfigureAwait(false) is AsyncLookup lookup) { - yield return item; + await foreach (TResult item in lookup.ApplyResultSelector(resultSelector, cancellationToken).ConfigureAwait(false)) + { + yield return item; + } } } } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupJoin.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupJoin.cs index 3b2d56147bb31d..769ccb65b14284 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupJoin.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/GroupJoin.cs @@ -47,7 +47,9 @@ public static IAsyncEnumerable GroupJoin ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + outer.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, @@ -116,7 +118,9 @@ public static IAsyncEnumerable GroupJoin ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + outer.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Index.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Index.cs index 758ca28b6b5d72..efbdb9aa203caa 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Index.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Index.cs @@ -20,7 +20,9 @@ public static partial class AsyncEnumerable { ThrowHelper.ThrowIfNull(source); - return Impl(source, default); + return + source.IsKnownEmpty() ? Empty<(int Index, TSource Item)>() : + Impl(source, default); static async IAsyncEnumerable<(int Index, TSource Item)> Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Intersect.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Intersect.cs index 97382080923145..22e1eab34c34a4 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Intersect.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Intersect.cs @@ -26,7 +26,9 @@ public static IAsyncEnumerable Intersect( ThrowHelper.ThrowIfNull(first); ThrowHelper.ThrowIfNull(second); - return Impl(first, second, comparer, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() ? Empty() : + Impl(first, second, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/IntersectBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/IntersectBy.cs index 2960209f2f43c5..18c4a52f3cc13f 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/IntersectBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/IntersectBy.cs @@ -38,7 +38,9 @@ public static IAsyncEnumerable IntersectBy( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(keySelector); - return Impl(first, second, keySelector, comparer, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() ? Empty() : + Impl(first, second, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -106,7 +108,9 @@ public static IAsyncEnumerable IntersectBy( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(keySelector); - return Impl(first, second, keySelector, comparer, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() ? Empty() : + Impl(first, second, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Join.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Join.cs index d6acc4a3012d35..cac87ca1f4f724 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Join.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Join.cs @@ -44,7 +44,9 @@ public static IAsyncEnumerable Join( // ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + outer.IsKnownEmpty() || inner.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, IAsyncEnumerable inner, @@ -121,7 +123,9 @@ public static IAsyncEnumerable Join( ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + outer.IsKnownEmpty() || inner.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/LeftJoin.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/LeftJoin.cs index acce0f765694ad..38eebe64e5251c 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/LeftJoin.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/LeftJoin.cs @@ -41,7 +41,9 @@ public static IAsyncEnumerable LeftJoin( ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + outer.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, IAsyncEnumerable inner, @@ -116,7 +118,9 @@ public static IAsyncEnumerable LeftJoin( ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + outer.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OfType.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OfType.cs index 2aa02ebb54858b..0abad165b4dd7b 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OfType.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OfType.cs @@ -26,7 +26,9 @@ public static IAsyncEnumerable OfType( { ThrowHelper.ThrowIfNull(source); - return Impl(source, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OrderBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OrderBy.cs index 64414b2e11bc7b..a4ee8f30d65c16 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OrderBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/OrderBy.cs @@ -33,8 +33,15 @@ public static IOrderedAsyncEnumerable Order( public static IOrderedAsyncEnumerable OrderBy( // satisfies the C# query-expression pattern this IAsyncEnumerable source, Func keySelector, - IComparer? comparer = null) => - new OrderedIterator(source, keySelector, comparer, false, null); + IComparer? comparer = null) + { + ThrowHelper.ThrowIfNull(source); + ThrowHelper.ThrowIfNull(keySelector); + + return + source.IsKnownEmpty() ? EmptyAsyncEnumerable.Instance : + new OrderedIterator(source, keySelector, comparer, false, null); + } /// Sorts the elements of a sequence in ascending order. /// The type of the elements of . @@ -48,8 +55,15 @@ public static IOrderedAsyncEnumerable OrderBy( // satisf public static IOrderedAsyncEnumerable OrderBy( this IAsyncEnumerable source, Func> keySelector, - IComparer? comparer = null) => - new OrderedIterator(source, keySelector, comparer, false, null); + IComparer? comparer = null) + { + ThrowHelper.ThrowIfNull(source); + ThrowHelper.ThrowIfNull(keySelector); + + return + source.IsKnownEmpty() ? EmptyAsyncEnumerable.Instance : + new OrderedIterator(source, keySelector, comparer, false, null); + } /// Sorts the elements of a sequence in descending order. /// The type of the elements of . @@ -74,8 +88,15 @@ public static IOrderedAsyncEnumerable OrderDescending( public static IOrderedAsyncEnumerable OrderByDescending( // satisfies the C# query-expression pattern this IAsyncEnumerable source, Func keySelector, - IComparer? comparer = null) => - new OrderedIterator(source, keySelector, comparer, true, null); + IComparer? comparer = null) + { + ThrowHelper.ThrowIfNull(source); + ThrowHelper.ThrowIfNull(keySelector); + + return + source.IsKnownEmpty() ? EmptyAsyncEnumerable.Instance : + new OrderedIterator(source, keySelector, comparer, true, null); + } /// Sorts the elements of a sequence in descending order. /// The type of the elements of . @@ -89,8 +110,15 @@ public static IOrderedAsyncEnumerable OrderByDescending( public static IOrderedAsyncEnumerable OrderByDescending( this IAsyncEnumerable source, Func> keySelector, - IComparer? comparer = null) => - new OrderedIterator(source, keySelector, comparer, true, null); + IComparer? comparer = null) + { + ThrowHelper.ThrowIfNull(source); + ThrowHelper.ThrowIfNull(keySelector); + + return + source.IsKnownEmpty() ? EmptyAsyncEnumerable.Instance : + new OrderedIterator(source, keySelector, comparer, true, null); + } /// Performs a subsequent ordering of the elements in a sequence in ascending order. /// The type of the elements of . @@ -198,9 +226,6 @@ private sealed partial class OrderedIterator : OrderedIterator source, object keySelector, IComparer? comparer, bool descending, OrderedIterator? parent) : base(source) { - ThrowHelper.ThrowIfNull(source); - ThrowHelper.ThrowIfNull(keySelector); - Debug.Assert(keySelector is Func or Func>); _parent = parent; diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Reverse.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Reverse.cs index 4ecc083ca54a13..424d6d78144135 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Reverse.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Reverse.cs @@ -19,7 +19,9 @@ public static IAsyncEnumerable Reverse( { ThrowHelper.ThrowIfNull(source); - return Impl(source, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/RightJoin.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/RightJoin.cs index 810496d53ea793..bec5def85f942b 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/RightJoin.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/RightJoin.cs @@ -41,7 +41,9 @@ public static IAsyncEnumerable RightJoin ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + inner.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, @@ -117,7 +119,9 @@ public static IAsyncEnumerable RightJoin ThrowHelper.ThrowIfNull(innerKeySelector); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); + return + inner.IsKnownEmpty() ? Empty() : + Impl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable outer, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Select.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Select.cs index a366e55111b158..448f77f09ab3f9 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Select.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Select.cs @@ -28,7 +28,9 @@ public static IAsyncEnumerable Select( // satisfies t ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -60,7 +62,9 @@ public static IAsyncEnumerable Select( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -95,7 +99,9 @@ public static IAsyncEnumerable Select( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -131,7 +137,9 @@ public static IAsyncEnumerable Select( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SelectMany.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SelectMany.cs index 3789754f2a994e..770eb9f70a7b07 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SelectMany.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SelectMany.cs @@ -31,7 +31,9 @@ public static IAsyncEnumerable SelectMany( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -69,7 +71,9 @@ public static IAsyncEnumerable SelectMany( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -107,7 +111,9 @@ public static IAsyncEnumerable SelectMany( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -146,7 +152,9 @@ public static IAsyncEnumerable SelectMany( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -186,7 +194,9 @@ public static IAsyncEnumerable SelectMany( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -226,7 +236,9 @@ public static IAsyncEnumerable SelectMany( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(selector); - return Impl(source, selector, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, selector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -274,7 +286,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -322,7 +336,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -370,7 +386,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -418,7 +436,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); async static IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -465,7 +485,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -513,7 +535,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -561,7 +585,9 @@ public static IAsyncEnumerable SelectMany() : + Impl(source, collectionSelector, resultSelector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Shuffle.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Shuffle.cs index a798976a5aef84..e2d5d89a59eb98 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Shuffle.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Shuffle.cs @@ -24,7 +24,9 @@ public static IAsyncEnumerable Shuffle( { ThrowHelper.ThrowIfNull(source); - return Impl(source, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Skip.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Skip.cs index 764f9511c4e644..c6e9cb13e89230 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Skip.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Skip.cs @@ -21,8 +21,9 @@ public static IAsyncEnumerable Skip( { ThrowHelper.ThrowIfNull(source); - return count <= 0 ? - source : + return + source.IsKnownEmpty() ? Empty() : + count <= 0 ? source : Impl(source, count, default); static async IAsyncEnumerable Impl( diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipLast.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipLast.cs index 9856d9d7f56a8b..e6f6966e09f465 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipLast.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipLast.cs @@ -26,6 +26,7 @@ public static IAsyncEnumerable SkipLast( ThrowHelper.ThrowIfNull(source); return + source.IsKnownEmpty() ? Empty() : count <= 0 ? source : TakeRangeFromEndIterator(source, isStartIndexFromEnd: false, startIndex: 0, isEndIndexFromEnd: true, endIndex: count, default); } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipWhile.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipWhile.cs index 976508a944ff05..7738765e360f45 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipWhile.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/SkipWhile.cs @@ -31,7 +31,9 @@ public static IAsyncEnumerable SkipWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -84,7 +86,9 @@ public static IAsyncEnumerable SkipWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -141,7 +145,9 @@ public static IAsyncEnumerable SkipWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -199,7 +205,9 @@ public static IAsyncEnumerable SkipWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Take.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Take.cs index 003df03d59177f..0e1cd012395200 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Take.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Take.cs @@ -26,8 +26,8 @@ public static IAsyncEnumerable Take( { ThrowHelper.ThrowIfNull(source); - return count <= 0 ? - Empty() : + return + source.IsKnownEmpty() || count <= 0 ? Empty() : Impl(source, count, default); static async IAsyncEnumerable Impl( @@ -63,6 +63,11 @@ public static IAsyncEnumerable Take( { ThrowHelper.ThrowIfNull(source); + if (source.IsKnownEmpty()) + { + return Empty(); + } + Index start = range.Start, end = range.End; bool isStartIndexFromEnd = start.IsFromEnd, isEndIndexFromEnd = end.IsFromEnd; int startIndex = start.Value, endIndex = end.Value; @@ -78,8 +83,8 @@ public static IAsyncEnumerable Take( } else if (!isEndIndexFromEnd) { - return startIndex >= endIndex ? - Empty() : + return + startIndex >= endIndex ? Empty() : Impl(source, startIndex, endIndex, default); } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeLast.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeLast.cs index b3fd0df73684cc..dff268880d09a4 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeLast.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeLast.cs @@ -18,8 +18,8 @@ public static IAsyncEnumerable TakeLast( { ThrowHelper.ThrowIfNull(source); - return count <= 0 ? - Empty() : + return + source.IsKnownEmpty() || count <= 0 ? Empty() : TakeRangeFromEndIterator(source, isStartIndexFromEnd: true, startIndex: count, isEndIndexFromEnd: true, endIndex: 0, default); } } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeWhile.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeWhile.cs index cf9e95b5047a74..e1fac70ccf9cb2 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeWhile.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/TakeWhile.cs @@ -27,7 +27,9 @@ public static IAsyncEnumerable TakeWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, Func predicate, @@ -62,7 +64,9 @@ public static IAsyncEnumerable TakeWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -101,7 +105,9 @@ public static IAsyncEnumerable TakeWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -141,7 +147,9 @@ public static IAsyncEnumerable TakeWhile( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToArrayAsync.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToArrayAsync.cs index 6e5dd1e16d1188..beefd39d3c1340 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToArrayAsync.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToArrayAsync.cs @@ -27,13 +27,27 @@ public static ValueTask ToArrayAsync( static async ValueTask Impl( ConfiguredCancelableAsyncEnumerable source) { - List list = []; - await foreach (TSource element in source) + ConfiguredCancelableAsyncEnumerable.Enumerator e = source.GetAsyncEnumerator(); + try { - list.Add(element); - } + if (await e.MoveNextAsync()) + { + List list = []; + do + { + list.Add(e.Current); + } + while (await e.MoveNextAsync()); + + return list.ToArray(); + } - return list.ToArray(); + return []; + } + finally + { + await e.DisposeAsync(); + } } } } diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToAsyncEnumerable.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToAsyncEnumerable.cs index 7f867a5aa51fa8..ff8b8c880a0f09 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToAsyncEnumerable.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToAsyncEnumerable.cs @@ -22,9 +22,10 @@ public static IAsyncEnumerable ToAsyncEnumerable( return source switch { - TSource[] array => FromArray(array), + TSource[] array => array.Length == 0 ? Empty() : FromArray(array), List list => FromList(list), IList list => FromIList(list), + _ when source == Enumerable.Empty() => Empty(), _ => FromIterator(source), }; diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToLookupAsync.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToLookupAsync.cs index 17cd7f1475bbca..dda7798c5f4826 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToLookupAsync.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/ToLookupAsync.cs @@ -41,13 +41,28 @@ static async ValueTask> Impl( Func keySelector, IEqualityComparer? comparer) { - var lookup = new AsyncLookup(comparer); - await foreach (TSource item in source) + ConfiguredCancelableAsyncEnumerable.Enumerator e = source.GetAsyncEnumerator(); + try { - lookup.GetGrouping(keySelector(item), create: true)!.Add(item); - } + if (!await e.MoveNextAsync()) + { + return EmptyLookup.Instance; + } - return lookup; + AsyncLookup lookup = new(comparer); + do + { + TSource item = e.Current; + lookup.GetGrouping(keySelector(item), create: true)!.Add(item); + } + while (await e.MoveNextAsync()); + + return lookup; + } + finally + { + await e.DisposeAsync(); + } } } @@ -81,13 +96,28 @@ static async ValueTask> Impl( IEqualityComparer? comparer, CancellationToken cancellationToken) { - var lookup = new AsyncLookup(comparer); - await foreach (TSource item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + IAsyncEnumerator e = source.GetAsyncEnumerator(cancellationToken); + try { - lookup.GetGrouping(await keySelector(item, cancellationToken).ConfigureAwait(false), create: true)!.Add(item); - } + if (!await e.MoveNextAsync().ConfigureAwait(false)) + { + return EmptyLookup.Instance; + } - return lookup; + AsyncLookup lookup = new(comparer); + do + { + TSource item = e.Current; + lookup.GetGrouping(await keySelector(item, cancellationToken).ConfigureAwait(false), create: true)!.Add(item); + } + while (await e.MoveNextAsync().ConfigureAwait(false)); + + return lookup; + } + finally + { + await e.DisposeAsync().ConfigureAwait(false); + } } } @@ -125,13 +155,28 @@ static async ValueTask> Impl( Func elementSelector, IEqualityComparer? comparer) { - var lookup = new AsyncLookup(comparer); - await foreach (TSource item in source) + ConfiguredCancelableAsyncEnumerable.Enumerator e = source.GetAsyncEnumerator(); + try { - lookup.GetGrouping(keySelector(item), create: true)!.Add(elementSelector(item)); - } + if (!await e.MoveNextAsync()) + { + return EmptyLookup.Instance; + } - return lookup; + AsyncLookup lookup = new(comparer); + do + { + TSource item = e.Current; + lookup.GetGrouping(keySelector(item), create: true)!.Add(elementSelector(item)); + } + while (await e.MoveNextAsync()); + + return lookup; + } + finally + { + await e.DisposeAsync(); + } } } @@ -170,15 +215,76 @@ static async ValueTask> Impl( IEqualityComparer? comparer, CancellationToken cancellationToken) { - var lookup = new AsyncLookup(comparer); - await foreach (TSource item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + IAsyncEnumerator e = source.GetAsyncEnumerator(cancellationToken); + try { - lookup.GetGrouping(await keySelector(item, cancellationToken).ConfigureAwait(false), create: true)! - .Add(await elementSelector(item, cancellationToken).ConfigureAwait(false)); + if (!await e.MoveNextAsync().ConfigureAwait(false)) + { + return EmptyLookup.Instance; + } + + AsyncLookup lookup = new(comparer); + do + { + TSource item = e.Current; + lookup.GetGrouping(await keySelector(item, cancellationToken).ConfigureAwait(false), create: true)!.Add(await elementSelector(item, cancellationToken).ConfigureAwait(false)); + } + while (await e.MoveNextAsync().ConfigureAwait(false)); + + return lookup; + } + finally + { + await e.DisposeAsync().ConfigureAwait(false); } + } + } - return lookup; + [DebuggerDisplay("Count = 0")] + private sealed class EmptyLookup : ILookup, IList>, IReadOnlyCollection> + { + public static readonly EmptyLookup Instance = new(); + + public bool IsReadOnly => true; + + public int Count => 0; + + public IEnumerable this[TKey key] => []; + + public IEnumerator> GetEnumerator() => Enumerable.Empty>().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Contains(TKey key) => false; + + public bool Contains(IGrouping item) => false; + + public void CopyTo(IGrouping[] array, int arrayIndex) + { + ThrowHelper.ThrowIfNull(array); + if ((uint)arrayIndex > (uint)array.Length) + { + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(arrayIndex)); + } + } + + public int IndexOf(IGrouping item) => -1; + + public void Add(IGrouping item) => throw new NotSupportedException(); + + public void Clear() => throw new NotSupportedException(); + + public IGrouping this[int index] + { + get => throw new ArgumentOutOfRangeException(nameof(index)); + set => throw new NotSupportedException(); } + + public void Insert(int index, IGrouping item) => throw new NotSupportedException(); + + public bool Remove(IGrouping item) => throw new NotSupportedException(); + + public void RemoveAt(int index) => throw new NotSupportedException(); } [DebuggerDisplay("Count = {Count}")] diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Union.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Union.cs index 17b9e857f3599d..0d0ebe01d94543 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Union.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Union.cs @@ -26,7 +26,9 @@ public static IAsyncEnumerable Union( ThrowHelper.ThrowIfNull(first); ThrowHelper.ThrowIfNull(second); - return Impl(first, second, comparer, default); + return + first.IsKnownEmpty() && second.IsKnownEmpty() ? Empty() : + Impl(first, second, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/UnionBy.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/UnionBy.cs index ccf80ed292c923..94500949c2172a 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/UnionBy.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/UnionBy.cs @@ -30,7 +30,9 @@ public static IAsyncEnumerable UnionBy( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(keySelector); - return Impl(first, second, keySelector, comparer, default); + return + first.IsKnownEmpty() && second.IsKnownEmpty() ? Empty() : + Impl(first, second, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -79,7 +81,9 @@ public static IAsyncEnumerable UnionBy( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(keySelector); - return Impl(first, second, keySelector, comparer, default); + return + first.IsKnownEmpty() && second.IsKnownEmpty() ? Empty() : + Impl(first, second, keySelector, comparer, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Where.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Where.cs index e915df69ac4166..768d1b75dfd603 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Where.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Where.cs @@ -24,7 +24,9 @@ public static IAsyncEnumerable Where( // satisfies the C# quer ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -55,7 +57,9 @@ public static IAsyncEnumerable Where( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -92,7 +96,9 @@ public static IAsyncEnumerable Where( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, @@ -130,7 +136,9 @@ public static IAsyncEnumerable Where( ThrowHelper.ThrowIfNull(source); ThrowHelper.ThrowIfNull(predicate); - return Impl(source, predicate, default); + return + source.IsKnownEmpty() ? Empty() : + Impl(source, predicate, default); static async IAsyncEnumerable Impl( IAsyncEnumerable source, diff --git a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Zip.cs b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Zip.cs index d4e7e6b2e6c0e4..37ce9d33609948 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Zip.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/src/System/Linq/Zip.cs @@ -33,7 +33,9 @@ public static IAsyncEnumerable Zip( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(first, second, resultSelector, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() ? Empty() : + Impl(first, second, resultSelector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -88,7 +90,9 @@ public static IAsyncEnumerable Zip( ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(resultSelector); - return Impl(first, second, resultSelector, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() ? Empty() : + Impl(first, second, resultSelector, default); static async IAsyncEnumerable Impl( IAsyncEnumerable first, @@ -135,7 +139,9 @@ await e2.MoveNextAsync().ConfigureAwait(false)) ThrowHelper.ThrowIfNull(first); ThrowHelper.ThrowIfNull(second); - return Impl(first, second, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() ? Empty<(TFirst, TSecond)>() : + Impl(first, second, default); static async IAsyncEnumerable<(TFirst First, TSecond Second)> Impl( IAsyncEnumerable first, @@ -186,7 +192,9 @@ await e2.MoveNextAsync().ConfigureAwait(false)) ThrowHelper.ThrowIfNull(second); ThrowHelper.ThrowIfNull(third); - return Impl(first, second, third, default); + return + first.IsKnownEmpty() || second.IsKnownEmpty() || third.IsKnownEmpty() ? Empty<(TFirst, TSecond, TThird)>() : + Impl(first, second, third, default); static async IAsyncEnumerable<(TFirst First, TSecond Second, TThird)> Impl( IAsyncEnumerable first, IAsyncEnumerable second, IAsyncEnumerable third, [EnumeratorCancellation] CancellationToken cancellationToken) diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/AggregateByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/AggregateByTests.cs index 3db27d3f02219a..09e94193c658bf 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/AggregateByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/AggregateByTests.cs @@ -32,6 +32,15 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("func", () => AsyncEnumerable.AggregateBy(AsyncEnumerable.Empty(), async (x, ct) => x, async (x, ct) => x, (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().AggregateBy(x => x, x => x, (x, y) => x + y)); + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().AggregateBy(x => x, 42, (x, y) => x + y)); + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().AggregateBy(async (x, ct) => x, async (x, ct) => x, async (x, y, ct) => x + y)); + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().AggregateBy(async (x, ct) => x, 42, async (x, y, ct) => x + y)); + } + public static IEnumerable VariousValues_MatchesEnumerable_String_MemberData() { yield return new object[] { new string[0] }; diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/AsyncEnumerableTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/AsyncEnumerableTests.cs index f1000bb272420a..ccbf00771f68ae 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/AsyncEnumerableTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/AsyncEnumerableTests.cs @@ -16,6 +16,12 @@ protected static IAsyncEnumerable CreateSource(params T[] items) => protected static IEnumerable> CreateSources(params T[] items) { + if (items.Length == 0) + { + yield return Enumerable.Empty().ToAsyncEnumerable(); + yield return AsyncEnumerable.Empty(); + } + yield return items.ToAsyncEnumerable(); yield return items.ToAsyncEnumerable().Yield(); } diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/CastTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/CastTests.cs index a7a88b8644d234..9f4b4d90a4267e 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/CastTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/CastTests.cs @@ -17,10 +17,10 @@ public void InvalidInputs_Throws() } [Fact] - public async Task Empty_ProducesEmpty() + public void Empty_ProducesEmpty() // validating an optimization / implementation detail { - await AssertEqual(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Cast()); - await AssertEqual(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Cast()); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Cast()); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Cast()); } [Fact] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ChunkTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ChunkTests.cs index aa900c15e633f4..3af01028ec5a00 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ChunkTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ChunkTests.cs @@ -20,6 +20,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("size", () => AsyncEnumerable.Chunk(AsyncEnumerable.Empty(), -1)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Chunk(1)); + } + #if NET [Fact] public async Task VariousValues_MatchesEnumerable() diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ConcatTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ConcatTests.cs index b6d649300af7ae..35110b12aae896 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ConcatTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ConcatTests.cs @@ -17,6 +17,17 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("second", () => AsyncEnumerable.Concat(AsyncEnumerable.Empty(), null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource(1, 3, 5); + + Assert.Same(empty, empty.Concat(empty)); + Assert.Same(nonEmpty, nonEmpty.Concat(empty)); + Assert.Same(nonEmpty, empty.Concat(nonEmpty)); + } + [Theory] [InlineData(new int[0], new int[0])] [InlineData(new int[0], new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/CountByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/CountByTests.cs index 4ba4aff94fb9de..b877602ea5088f 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/CountByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/CountByTests.cs @@ -18,6 +18,13 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("keySelector", () => AsyncEnumerable.CountBy(AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().CountBy(i => i)); + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().CountBy(async (i, ct) => i)); + } + #if NET [Fact] public async Task VariousValues_MatchesEnumerable_Strings() diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctByTests.cs index 3a8b4fd64145b0..d7079fd462bf5f 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctByTests.cs @@ -20,6 +20,13 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("keySelector", () => AsyncEnumerable.DistinctBy(AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().DistinctBy(i => i)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().DistinctBy(async (i, ct) => i)); + } + #if NET [Theory] [InlineData(new int[0])] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctTests.cs index f30c58a5d97ab5..0f7524827eb405 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/DistinctTests.cs @@ -16,6 +16,13 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.Distinct(null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Distinct()); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Distinct()); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 1 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptByTests.cs index b455f76f33492f..894063958c5178 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptByTests.cs @@ -22,6 +22,13 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("keySelector", () => AsyncEnumerable.ExceptBy(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().ExceptBy(CreateSource(1, 2, 3), i => i)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().ExceptBy(CreateSource(1, 2, 3), async (i, ct) => i)); + } + #if NET [Theory] [InlineData(new int[0], new int[0])] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptTests.cs index b79ad34dda52ca..fcd16bfc103792 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ExceptTests.cs @@ -17,6 +17,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("second", () => AsyncEnumerable.Except(AsyncEnumerable.Empty(), null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Except(CreateSource(1, 2, 3))); + } + [Theory] [InlineData(new int[0], new int[0])] [InlineData(new int[0], new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/GroupByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/GroupByTests.cs index a908913f80a018..0d8656995c6821 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/GroupByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/GroupByTests.cs @@ -42,6 +42,22 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("resultSelector", () => AsyncEnumerable.GroupBy(AsyncEnumerable.Empty(), async (s, ct) => s, async (s, ct) => s, (Func, CancellationToken, ValueTask>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().GroupBy(i => i)); + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().GroupBy(async (i, ct) => i)); + + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().GroupBy(i => i, i => i.Length)); + Assert.Same(AsyncEnumerable.Empty>(), AsyncEnumerable.Empty().GroupBy(async (i, ct) => i, async (i, ct) => i.Length)); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().GroupBy(i => i, (i, elements) => i.Length)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().GroupBy(async (i, ct) => i, async (i, elements, ct) => i.Length)); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().GroupBy(i => i, i => i.Length, (i, elements) => i.Length)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().GroupBy(async (i, ct) => i, async (i, ct) => i.Length, async (i, elements, ct) => i.Length)); + } + [Fact] public async Task VariousValues_MatchesEnumerable_String() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/GroupJoinTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/GroupJoinTests.cs index c6cb865ca61e07..305faf3c77681f 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/GroupJoinTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/GroupJoinTests.cs @@ -26,6 +26,13 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("resultSelector", () => AsyncEnumerable.GroupJoin(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), async (outer, ct) => outer, async (inner, ct) => inner, (Func, CancellationToken, ValueTask>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().GroupJoin(CreateSource(1, 2, 3), s => s, i => i.ToString(), (s, e) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().GroupJoin(CreateSource(1, 2, 3), async (s, ct) => s, async (i, ct) => i.ToString(), async (s, e, ct) => s)); + } + [Fact] public async Task VariousValues_MatchesEnumerable_String() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/IndexTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/IndexTests.cs index 3c3c081bd5a16e..8aabdb7fc8ef6e 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/IndexTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/IndexTests.cs @@ -16,6 +16,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.Index(null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty<(int, string)>(), AsyncEnumerable.Empty().Index()); + } + #if NET [Theory] [InlineData(new int[0])] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectByTests.cs index 0d60650ae80b79..68ae73c5cbe484 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectByTests.cs @@ -22,6 +22,19 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("keySelector", () => AsyncEnumerable.IntersectBy(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource(1, 2, 3); + + Assert.Same(empty, empty.IntersectBy(nonEmpty, x => x)); + Assert.Same(empty, empty.IntersectBy(nonEmpty, async (x, ct) => x)); + + Assert.Same(empty, nonEmpty.IntersectBy(empty, x => x)); + Assert.Same(empty, nonEmpty.IntersectBy(empty, async (x, ct) => x)); + } + #if NET [Theory] [InlineData(new int[0], new int[0])] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectTests.cs index 423ec8a5a7e741..a3ed3eb479d53c 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/IntersectTests.cs @@ -17,6 +17,16 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("second", () => AsyncEnumerable.Intersect(AsyncEnumerable.Empty(), null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource(1, 2, 3); + + Assert.Same(empty, empty.Intersect(nonEmpty)); + Assert.Same(empty, nonEmpty.Intersect(empty)); + } + [Theory] [InlineData(new int[0], new int[0])] [InlineData(new int[0], new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/JoinTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/JoinTests.cs index 5863f7fb2c4ef9..258ea44c57ba49 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/JoinTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/JoinTests.cs @@ -26,6 +26,22 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("resultSelector", () => AsyncEnumerable.Join(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), async (outer, ct) => outer, async (inner, ct) => inner, (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource("1", "2", "3"); + + Assert.Same(AsyncEnumerable.Empty(), empty.Join(empty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), empty.Join(empty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + + Assert.Same(AsyncEnumerable.Empty(), nonEmpty.Join(empty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), nonEmpty.Join(empty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + + Assert.Same(AsyncEnumerable.Empty(), empty.Join(nonEmpty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), empty.Join(nonEmpty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + } + [Fact] public async Task VariousValues_MatchesEnumerable_String() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/LeftJoinTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/LeftJoinTests.cs index 334b8ba202b41a..759ae3ab1ed1d8 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/LeftJoinTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/LeftJoinTests.cs @@ -26,6 +26,22 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("resultSelector", () => AsyncEnumerable.LeftJoin(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), async (outer, ct) => outer, async (inner, ct) => inner, (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource("1", "2", "3"); + + Assert.Same(AsyncEnumerable.Empty(), empty.LeftJoin(empty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), empty.LeftJoin(empty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + + Assert.NotSame(AsyncEnumerable.Empty(), nonEmpty.LeftJoin(empty, s => s, s => s, (s1, s2) => s1)); + Assert.NotSame(AsyncEnumerable.Empty(), nonEmpty.LeftJoin(empty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + + Assert.Same(AsyncEnumerable.Empty(), empty.LeftJoin(nonEmpty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), empty.LeftJoin(nonEmpty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + } + #if NET [Fact] public async Task VariousValues_MatchesEnumerable_String() diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/OfTypeTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/OfTypeTests.cs index 6bf667b12699ca..87371dfcbc33ec 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/OfTypeTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/OfTypeTests.cs @@ -18,10 +18,10 @@ public void InvalidInputs_Throws() } [Fact] - public async Task Empty_ProducesEmpty() + public void Empty_ProducesEmpty() // validating an optimization / implementation detail { - await AssertEqual(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OfType()); - await AssertEqual(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OfType()); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OfType()); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OfType()); } [Fact] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/OrderByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/OrderByTests.cs index 558bf4c1dfaf67..af6b1d4d078fdb 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/OrderByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/OrderByTests.cs @@ -37,6 +37,22 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("keySelector", () => AsyncEnumerable.ThenByDescending(AsyncEnumerable.Empty().Order(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderBy(i => i)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderBy(async (i, ct) => i)); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderByDescending(i => i)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderByDescending(async (i, ct) => i)); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderBy(i => i).ThenBy(i => i)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderBy(async (i, ct) => i).ThenBy(async (i, ct) => i)); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderByDescending(i => i).ThenByDescending(i => i)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().OrderByDescending(async (i, ct) => i).ThenByDescending(async (i, ct) => i)); + } + [Fact] public async Task VariousValues_MatchesEnumerable_Int32() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/RangeTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/RangeTests.cs index 75413ef13fc7c4..7fcff0f06ae817 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/RangeTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/RangeTests.cs @@ -16,6 +16,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("count", () => AsyncEnumerable.Range(int.MaxValue - 1, 3)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Range(42, 0)); + } + [Fact] public async Task VariousValues_MatchesEnumerable() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/RepeatTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/RepeatTests.cs index f9bea5532d5b7f..d27a0526a478c7 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/RepeatTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/RepeatTests.cs @@ -14,6 +14,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("count", () => AsyncEnumerable.Repeat("a", -1)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Repeat("42", 0)); + } + [Fact] public async Task VariousValues_MatchesEnumerable() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ReverseTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ReverseTests.cs index 2c4678e3d3a95c..1b4e7c69906709 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ReverseTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ReverseTests.cs @@ -16,6 +16,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.Reverse(null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Reverse()); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 1 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/RightJoinTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/RightJoinTests.cs index 1c5c02eb9831e1..e7f123a464e463 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/RightJoinTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/RightJoinTests.cs @@ -26,6 +26,22 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("resultSelector", () => AsyncEnumerable.RightJoin(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), async (outer, ct) => outer, async (inner, ct) => inner, (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource("1", "2", "3"); + + Assert.Same(AsyncEnumerable.Empty(), empty.RightJoin(empty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), empty.RightJoin(empty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + + Assert.Same(AsyncEnumerable.Empty(), nonEmpty.RightJoin(empty, s => s, s => s, (s1, s2) => s1)); + Assert.Same(AsyncEnumerable.Empty(), nonEmpty.RightJoin(empty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + + Assert.NotSame(AsyncEnumerable.Empty(), empty.RightJoin(nonEmpty, s => s, s => s, (s1, s2) => s1)); + Assert.NotSame(AsyncEnumerable.Empty(), empty.RightJoin(nonEmpty, async (s, ct) => s, async (s, ct) => s, async (s1, s2, ct) => s1)); + } + #if NET [Fact] public async Task VariousValues_MatchesEnumerable_String() diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/SelectManyTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/SelectManyTests.cs index ffdb4c8593026a..a0e00053fd15c8 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/SelectManyTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/SelectManyTests.cs @@ -57,6 +57,27 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("resultSelector", () => AsyncEnumerable.SelectMany(AsyncEnumerable.Empty(), (i, index) => AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(s => s.ToCharArray())); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(async (s, ct) => (IEnumerable)s.ToCharArray())); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(s => s.ToAsyncEnumerable())); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany((s, i) => s.ToCharArray())); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(async (s, i, ct) => (IEnumerable)s.ToCharArray())); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany((s, i) => s.ToAsyncEnumerable())); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(s => s.ToCharArray(), (s, c) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(async (s, ct) => (IEnumerable)s.ToCharArray(), async (s, c, ct) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(s => s.ToAsyncEnumerable(), (s, c) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(s => s.ToAsyncEnumerable(), async (s, c, ct) => s)); + + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany((s, i) => s.ToCharArray(), (s, c) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany(async (s, i, ct) => (IEnumerable)s.ToCharArray(), async (s, c, ct) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SelectMany((s, i) => s.ToAsyncEnumerable(), async (s, c, ct) => s)); + } + [Fact] public async Task VariousValues_MatchesEnumerable() { diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/SelectTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/SelectTests.cs index 9e82c46e73cf98..5adf11d5a7b366 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/SelectTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/SelectTests.cs @@ -24,6 +24,15 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("selector", () => AsyncEnumerable.Select(AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Select(s => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Select((s, index) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Select(async (string s, CancellationToken ct) => s)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Select(async (string s, int index, CancellationToken ct) => s)); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ShuffleTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ShuffleTests.cs index 39130f280099df..6798ca81d80304 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ShuffleTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ShuffleTests.cs @@ -16,6 +16,12 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.Shuffle(null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Shuffle()); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 1 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/SkipLastTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/SkipLastTests.cs index 1da94e367d3d0e..5fb5a76f848a8e 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/SkipLastTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/SkipLastTests.cs @@ -16,6 +16,16 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.SkipLast((IAsyncEnumerable)null, 42)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SkipLast(42)); + + IAsyncEnumerable source = CreateSource(2, 4, 8, 16); + Assert.Same(source, source.SkipLast(0)); + Assert.Same(source, source.SkipLast(-1)); + } + #if NET [Theory] [InlineData(new int[0])] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/SkipTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/SkipTests.cs index e3f1b3d121b0d8..84c52f7ba10211 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/SkipTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/SkipTests.cs @@ -16,6 +16,16 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.Skip((IAsyncEnumerable)null, 42)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Skip(42)); + + IAsyncEnumerable source = CreateSource(2, 4, 8, 16); + Assert.Same(source, source.Skip(0)); + Assert.Same(source, source.Skip(-1)); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/SkipWhileTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/SkipWhileTests.cs index 1da93416d22a3e..60f7c7bca4c3b4 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/SkipWhileTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/SkipWhileTests.cs @@ -24,6 +24,15 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("predicate", () => AsyncEnumerable.SkipWhile(AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SkipWhile(i => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SkipWhile((i, index) => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SkipWhile(async (i, ct) => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().SkipWhile(async (i, index, ct) => true)); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/TakeLastTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/TakeLastTests.cs index 0b723c7268befc..ef8b3192d54a3a 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/TakeLastTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/TakeLastTests.cs @@ -16,6 +16,14 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.TakeLast((IAsyncEnumerable)null, 42)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().TakeLast(42)); + Assert.Same(AsyncEnumerable.Empty(), CreateSource(1, 2, 3).TakeLast(0)); + Assert.Same(AsyncEnumerable.Empty(), CreateSource(1, 2, 3).TakeLast(-1)); + } + #if NET [Theory] [InlineData(new int[0])] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/TakeTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/TakeTests.cs index 72582de10a2205..9341b1671a0899 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/TakeTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/TakeTests.cs @@ -18,8 +18,10 @@ public void InvalidInputs_Throws() } [Fact] - public void TakeNothing_ReturnsEmpty() + public void Empty_ProducesEmpty() // validating an optimization / implementation detail { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().TakeLast(42)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Take(new int[] { 1, 2, 3 }.ToAsyncEnumerable(), 0)); Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Take(new int[] { 1, 2, 3 }.ToAsyncEnumerable(), -1)); Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Take(new int[] { 1, 2, 3 }.ToAsyncEnumerable(), new Range(new(0), new(0)))); diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/TakeWhileTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/TakeWhileTests.cs index fbcb2a97f470e6..ac7fbdcd5e02a0 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/TakeWhileTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/TakeWhileTests.cs @@ -24,6 +24,15 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("predicate", () => AsyncEnumerable.TakeWhile(AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().TakeWhile(i => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().TakeWhile((i, index) => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().TakeWhile(async (i, ct) => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().TakeWhile(async (i, index, ct) => true)); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ToAsyncEnumerableTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ToAsyncEnumerableTests.cs index da851de0037d12..87808468229e8b 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ToAsyncEnumerableTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ToAsyncEnumerableTests.cs @@ -16,6 +16,18 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("source", () => AsyncEnumerable.ToAsyncEnumerable(null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), Enumerable.Empty().ToAsyncEnumerable()); + Assert.Same(AsyncEnumerable.Empty(), Array.Empty().ToAsyncEnumerable()); + Assert.Same(AsyncEnumerable.Empty(), new int[0].ToAsyncEnumerable()); + + Assert.NotSame(AsyncEnumerable.Empty(), new List().ToAsyncEnumerable()); + Assert.NotSame(AsyncEnumerable.Empty(), new HashSet().ToAsyncEnumerable()); + Assert.NotSame(AsyncEnumerable.Empty(), new ReadOnlyCollection([]).ToAsyncEnumerable()); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 1 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/UnionByTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/UnionByTests.cs index e363dda433c1e6..86ed66b086beb7 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/UnionByTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/UnionByTests.cs @@ -23,6 +23,23 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("keySelector", () => AsyncEnumerable.UnionBy(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource(1, 2, 3); + + Assert.Same(empty, empty.UnionBy(empty, i => i)); + Assert.NotSame(empty, empty.UnionBy(nonEmpty, i => i)); + Assert.NotSame(empty, nonEmpty.UnionBy(empty, i => i)); + Assert.NotSame(empty, nonEmpty.UnionBy(nonEmpty, i => i)); + + Assert.Same(empty, empty.UnionBy(empty, async (i, ct) => i)); + Assert.NotSame(empty, empty.UnionBy(nonEmpty, async (i, ct) => i)); + Assert.NotSame(empty, nonEmpty.UnionBy(empty, async (i, ct) => i)); + Assert.NotSame(empty, nonEmpty.UnionBy(nonEmpty, async (i, ct) => i)); + } + [Theory] [InlineData(new int[0], new int[0])] [InlineData(new int[0], new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/UnionTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/UnionTests.cs index 90219b88e62a4d..a27079ad87fce7 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/UnionTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/UnionTests.cs @@ -17,6 +17,18 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("second", () => AsyncEnumerable.Union(AsyncEnumerable.Empty(), null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource(1, 2, 3); + + Assert.Same(empty, empty.Union(empty)); + Assert.NotSame(empty, empty.Union(nonEmpty)); + Assert.NotSame(empty, nonEmpty.Union(empty)); + Assert.NotSame(empty, nonEmpty.Union(nonEmpty)); + } + [Theory] [InlineData(new int[0], new int[0])] [InlineData(new int[0], new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/WhereTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/WhereTests.cs index b64a149b253d64..31db1eafe8e5d6 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/WhereTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/WhereTests.cs @@ -24,6 +24,15 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("predicate", () => AsyncEnumerable.Where(AsyncEnumerable.Empty(), (Func>)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Where(i => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Where((i, index) => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Where(async (i, ct) => true)); + Assert.Same(AsyncEnumerable.Empty(), AsyncEnumerable.Empty().Where(async (i, index, ct) => true)); + } + [Theory] [InlineData(new int[0])] [InlineData(new int[] { 42 })] diff --git a/src/libraries/System.Linq.AsyncEnumerable/tests/ZipTests.cs b/src/libraries/System.Linq.AsyncEnumerable/tests/ZipTests.cs index b09730ad3d7e2d..3e4501b03fd942 100644 --- a/src/libraries/System.Linq.AsyncEnumerable/tests/ZipTests.cs +++ b/src/libraries/System.Linq.AsyncEnumerable/tests/ZipTests.cs @@ -29,6 +29,37 @@ public void InvalidInputs_Throws() AssertExtensions.Throws("third", () => AsyncEnumerable.Zip(AsyncEnumerable.Empty(), AsyncEnumerable.Empty(), (IAsyncEnumerable)null)); } + [Fact] + public void Empty_ProducesEmpty() // validating an optimization / implementation detail + { + IAsyncEnumerable empty = AsyncEnumerable.Empty(); + IAsyncEnumerable nonEmpty = CreateSource(1, 2, 3); + + Assert.Same(AsyncEnumerable.Empty<(int, int)>(), empty.Zip(empty)); + Assert.Same(AsyncEnumerable.Empty<(int, int)>(), empty.Zip(nonEmpty)); + Assert.Same(AsyncEnumerable.Empty<(int, int)>(), nonEmpty.Zip(empty)); + Assert.NotSame(AsyncEnumerable.Empty<(int, int)>(), nonEmpty.Zip(nonEmpty)); + + Assert.Same(AsyncEnumerable.Empty(), empty.Zip(empty, (i1, i2) => i1 + i2)); + Assert.Same(AsyncEnumerable.Empty(), nonEmpty.Zip(empty, (i1, i2) => i1 + i2)); + Assert.Same(AsyncEnumerable.Empty(), empty.Zip(nonEmpty, (i1, i2) => i1 + i2)); + Assert.NotSame(AsyncEnumerable.Empty(), nonEmpty.Zip(nonEmpty, (i1, i2) => i1 + i2)); + + Assert.Same(AsyncEnumerable.Empty(), empty.Zip(empty, async (i1, i2, ct) => i1 + i2)); + Assert.Same(AsyncEnumerable.Empty(), nonEmpty.Zip(empty, async (i1, i2, ct) => i1 + i2)); + Assert.Same(AsyncEnumerable.Empty(), empty.Zip(nonEmpty, async (i1, i2, ct) => i1 + i2)); + Assert.NotSame(AsyncEnumerable.Empty(), nonEmpty.Zip(nonEmpty, async (i1, i2, ct) => i1 + i2)); + + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), empty.Zip(empty, empty)); + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), nonEmpty.Zip(empty, empty)); + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), empty.Zip(nonEmpty, empty)); + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), empty.Zip(empty, nonEmpty)); + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), nonEmpty.Zip(nonEmpty, empty)); + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), nonEmpty.Zip(empty, nonEmpty)); + Assert.Same(AsyncEnumerable.Empty<(int, int, int)>(), empty.Zip(nonEmpty, nonEmpty)); + Assert.NotSame(AsyncEnumerable.Empty<(int, int, int)>(), nonEmpty.Zip(nonEmpty, nonEmpty)); + } + [Theory] [InlineData(new int[0], new int[0])] [InlineData(new int[0], new int[] { 42 })] diff --git a/src/libraries/System.Management/src/System/Management/ManagementObjectCollection.cs b/src/libraries/System.Management/src/System/Management/ManagementObjectCollection.cs index b6006925a02c5c..cffcbcd103cde0 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementObjectCollection.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementObjectCollection.cs @@ -367,7 +367,7 @@ IEnumerator IEnumerable.GetEnumerator() /// ManagementObjectCollection disks = diskClass.GetInstances(); /// ManagementObjectCollection.ManagementObjectEnumerator disksEnumerator = /// disks.GetEnumerator(); - /// while(disksEnumerator.MoveNext()) { + /// while (disksEnumerator.MoveNext()) { /// ManagementObject disk = (ManagementObject)disksEnumerator.Current; /// Console.WriteLine("Disk found: " + disk["deviceid"]); /// } diff --git a/src/libraries/System.Management/src/System/Management/MethodSet.cs b/src/libraries/System.Management/src/System/Management/MethodSet.cs index a87b28f46168fc..a6f5b36e2157e2 100644 --- a/src/libraries/System.Management/src/System/Management/MethodSet.cs +++ b/src/libraries/System.Management/src/System/Management/MethodSet.cs @@ -207,7 +207,7 @@ public MethodDataEnumerator GetEnumerator() /// ManagementClass diskClass = new ManagementClass("win32_logicaldisk"); /// MethodDataCollection.MethodDataEnumerator diskEnumerator = /// diskClass.Methods.GetEnumerator(); - /// while(diskEnumerator.MoveNext()) + /// while (diskEnumerator.MoveNext()) /// { /// MethodData method = diskEnumerator.Current; /// Console.WriteLine("Method = " + method.Name); diff --git a/src/libraries/System.Management/src/System/Management/PropertySet.cs b/src/libraries/System.Management/src/System/Management/PropertySet.cs index dbc3dc6ec88101..a4ca50720bea9e 100644 --- a/src/libraries/System.Management/src/System/Management/PropertySet.cs +++ b/src/libraries/System.Management/src/System/Management/PropertySet.cs @@ -214,7 +214,7 @@ public PropertyDataEnumerator GetEnumerator() /// public static int Main(string[] args) { /// ManagementObject disk = new ManagementObject("Win32_LogicalDisk.DeviceID='C:'"); /// PropertyDataCollection.PropertyDataEnumerator propertyEnumerator = disk.Properties.GetEnumerator(); - /// while(propertyEnumerator.MoveNext()) { + /// while (propertyEnumerator.MoveNext()) { /// PropertyData p = (PropertyData)propertyEnumerator.Current; /// Console.WriteLine("Property found: " + p.Name); /// } diff --git a/src/libraries/System.Management/src/System/Management/QualifierSet.cs b/src/libraries/System.Management/src/System/Management/QualifierSet.cs index a25f73559c3a79..190ce17ed2ba07 100644 --- a/src/libraries/System.Management/src/System/Management/QualifierSet.cs +++ b/src/libraries/System.Management/src/System/Management/QualifierSet.cs @@ -268,7 +268,7 @@ public QualifierDataEnumerator GetEnumerator() /// QualifierDataCollection diskQualifier = diskClass.Qualifiers; /// QualifierDataCollection.QualifierDataEnumerator /// qualifierEnumerator = diskQualifier.GetEnumerator(); - /// while(qualifierEnumerator.MoveNext()) { + /// while (qualifierEnumerator.MoveNext()) { /// Console.WriteLine(qualifierEnumerator.Current.Name + " = " + /// qualifierEnumerator.Current.Value); /// } 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 6b74d5d9cafed8..32d6c10206df95 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 @@ -254,7 +254,7 @@ public async Task CallFetch() var closePromise = BrowserHttpInterop.TransformStreamClose(_jsController); await BrowserHttpInterop.CancellationHelper(closePromise, _cancellationToken, _jsController).ConfigureAwait(false); } - catch(JSException jse) when (jse.Message.Contains("BrowserHttpWriteStream.Rejected", StringComparison.Ordinal)) + catch (JSException jse) when (jse.Message.Contains("BrowserHttpWriteStream.Rejected", StringComparison.Ordinal)) { // any error from pushing bytes will also appear in the fetch promise result } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttp.cs b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttp.cs index f527ad573b8f5d..3d63d27d73d9cf 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttp.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/WasiHttpHandler/WasiHttp.cs @@ -69,7 +69,7 @@ internal Err AsErr } internal class Option { - private static Option none = new (); + private static Option none = new(); private Option() { 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 88ee436cb8199e..bb961252205f8b 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 @@ -1073,7 +1073,7 @@ public static bool TryCompleteSendFile(SafeSocketHandle socket, SafeFileHandle h public static SocketError SetBlocking(SafeSocketHandle handle, bool shouldBlock, out bool willBlock) { - if(OperatingSystem.IsWasi() && shouldBlock) throw new PlatformNotSupportedException(); + if (OperatingSystem.IsWasi() && shouldBlock) throw new PlatformNotSupportedException(); handle.IsNonBlocking = !shouldBlock; willBlock = shouldBlock; 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 d21e80ba41fee5..f34c8ee6db72a0 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 @@ -588,7 +588,7 @@ private async Task CancellationHelper(Task promise, CancellationToken cancellati } if (ex is OperationCanceledException || cancellationToken.IsCancellationRequested || ex.Message == "Error: OperationCanceledException") { - if(state != WebSocketState.Closed) + if (state != WebSocketState.Closed) { FastState = WebSocketState.Aborted; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs index dcfeb47f9aab01..a71eaebac3822a 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorSpan.cs @@ -704,7 +704,7 @@ public TensorSpan Slice(params scoped ReadOnlySpan indexes) internal TensorSpan Slice(params scoped ReadOnlySpan lengths) { NRange[] ranges = new NRange[lengths.Length]; - for(int i = 0; i < lengths.Length; i++) + for (int i = 0; i < lengths.Length; i++) { ranges[i] = new NRange(0, lengths[i]); } 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 0c11aacc65896e..1fc285e1c3af6a 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 @@ -1489,9 +1489,6 @@ Common\System\Reflection\Metadata\TypeName.cs - - Common\System\Reflection\Metadata\TypeNameHelpers.cs - Common\System\Reflection\Metadata\TypeNameParser.cs 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 b234e6d96ea3ed..24a3a4485b7ebd 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 @@ -1672,7 +1672,7 @@ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, str // Register the provider with ETW Func eventSourceFactory = () => this; OverrideEventProvider? etwProvider = EventSourceInitHelper.TryGetPreregisteredEtwProvider(eventSourceGuid); - if(etwProvider == null) + if (etwProvider == null) { etwProvider = new OverrideEventProvider(eventSourceFactory, EventProviderType.ETW); etwProvider.Register(eventSourceGuid, eventSourceName); @@ -3879,7 +3879,7 @@ private bool SelfDescribingEvents #endif internal static void InitializeDefaultEventSources() { - if(!EventSource.IsSupported) + if (!EventSource.IsSupported) { return; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index a5adb8057f6a5e..fe89ca49146caf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -665,15 +665,12 @@ public static TInteger ConvertToInteger(double value) public static TInteger ConvertToIntegerNative(double value) where TInteger : IBinaryInteger { -#if !MONO if (typeof(TInteger).IsPrimitive) { // We need this to be recursive so indirect calls (delegates // for example) produce the same result as direct invocation return ConvertToIntegerNative(value); } -#endif - return TInteger.CreateSaturating(value); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.cs index 04ab8e7ee08598..fd9cb2ecd3e5e0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.cs @@ -80,7 +80,7 @@ internal partial struct TypeNameResolver current = typeName; while (current.IsNested) { - nestedTypeNames[--nestingDepth] = TypeNameHelpers.Unescape(current.Name); + nestedTypeNames[--nestingDepth] = TypeName.Unescape(current.Name); current = current.DeclaringType!; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index ef991d4aea3ea0..ce37952076f6b8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -660,15 +660,12 @@ public static TInteger ConvertToInteger(float value) public static TInteger ConvertToIntegerNative(float value) where TInteger : IBinaryInteger { -#if !MONO if (typeof(TInteger).IsPrimitive) { // We need this to be recursive so indirect calls (delegates // for example) produce the same result as direct invocation return ConvertToIntegerNative(value); } -#endif - return TInteger.CreateSaturating(value); } diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs index 1b465c66590efb..de4b6e97e427dd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs @@ -438,7 +438,7 @@ internal static void Fill(ref byte dest, byte value, nuint len) if (len >= (nuint)Vector.Count) { // We have enough data for at least one vectorized write. - Vector vector = new (value); + Vector vector = new(value); nuint stopLoopAtOffset = len & (nuint)(nint)(2 * (int)-Vector.Count); // intentional sign extension carries the negative bit nuint offset = 0; diff --git a/src/libraries/System.Private.CoreLib/src/System/String.cs b/src/libraries/System.Private.CoreLib/src/System/String.cs index b9a3a22130141d..01564cb1cb7aa6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.cs @@ -739,7 +739,7 @@ public char this[int index] // // This is an intrinsic function so that the JIT can recognise it specially // and eliminate checks on character fetches in a loop like: - // for(int i = 0; i < str.Length; i++) str[i] + // for (int i = 0; i < str.Length; i++) str[i] // The actual code generated for this will be one instruction and will be inlined. // public int Length 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 dfc8b6fe880df8..a08ac218adaa15 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 @@ -181,7 +181,7 @@ internal XmlSchemaSet InferSchema1(XmlReader instanceDocument, XmlSchemaSet sche schemas.Compile(); _schemaSet = schemas; //schemas = new Hashtable(); - //while(xtr.Read()) + //while (xtr.Read()) while (_xtr.NodeType != XmlNodeType.Element && _xtr.Read()) ; 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 b81299df890b12..a017539611908c 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 @@ -2949,14 +2949,14 @@ private void WriteArray(string source, string? arrayName, ArrayMapping arrayMapp if (isNullable) { - ilg.ExitScope(); // if(!ReadNull()) { ExitScope + ilg.ExitScope(); // if (!ReadNull()) { ExitScope ilg.Else(); // } else { EnterScope ilg.EnterScope(); member.IsNullable = true; WriteMemberBegin(members); WriteMemberEnd(members); } - ilg.ExitScope(); // if(!ReadNull())/else ExitScope + ilg.ExitScope(); // if (!ReadNull())/else ExitScope ilg.EndIf(); } diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs index 1ac507c16ee8b8..82416e72599516 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ILGeneratorImpl.cs @@ -57,7 +57,7 @@ internal ILGeneratorImpl(MethodBuilderImpl methodBuilder, int size) internal void AddExceptionBlocks() { - foreach(ExceptionHandlerInfo eb in _exceptionBlocks) + foreach (ExceptionHandlerInfo eb in _exceptionBlocks) { switch (eb.Kind) { diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs index 2ca4e426be999e..cad80467440521 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/TypeBuilderImpl.cs @@ -993,7 +993,7 @@ public override Type[] GetInterfaces() List interfaces = _interfaces ?? []; - if(_typeParent != null) + if (_typeParent != null) { interfaces.AddRange(_typeParent.GetInterfaces()); } diff --git a/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs b/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs index 5e4ccaed0166a9..3223b1e1a44b61 100644 --- a/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs +++ b/src/libraries/System.Reflection.Metadata/ref/System.Reflection.Metadata.cs @@ -2439,6 +2439,7 @@ internal TypeName() { } public bool IsSZArray { get { throw null; } } public bool IsVariableBoundArrayType { get { throw null; } } public string Name { get { throw null; } } + public string Namespace { get { throw null; } } public int GetArrayRank() { throw null; } public System.Reflection.Metadata.TypeName GetElementType() { throw null; } public System.Collections.Immutable.ImmutableArray GetGenericArguments() { throw null; } @@ -2451,6 +2452,7 @@ internal TypeName() { } public System.Reflection.Metadata.TypeName MakeSZArrayTypeName() { throw null; } public static System.Reflection.Metadata.TypeName Parse(System.ReadOnlySpan typeName, System.Reflection.Metadata.TypeNameParseOptions? options = null) { throw null; } public static bool TryParse(System.ReadOnlySpan typeName, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Reflection.Metadata.TypeName? result, System.Reflection.Metadata.TypeNameParseOptions? options = null) { throw null; } + public static string Unescape(string name) { throw null; } public System.Reflection.Metadata.TypeName WithAssemblyName(System.Reflection.Metadata.AssemblyNameInfo? assemblyName) { throw null; } } public sealed partial class TypeNameParseOptions diff --git a/src/libraries/System.Reflection.Metadata/src/Resources/Strings.resx b/src/libraries/System.Reflection.Metadata/src/Resources/Strings.resx index 21bab756dd6a89..b832902510c241 100644 --- a/src/libraries/System.Reflection.Metadata/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Metadata/src/Resources/Strings.resx @@ -429,6 +429,9 @@ This operation is only valid on arrays, pointers and references. + + Cannot retrieve the namespace of a nested type. + The given assembly name was invalid. diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs index a631576843f90d..783f90c5475acc 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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; @@ -39,7 +39,7 @@ sealed class TypeName #else private readonly ImmutableArray _genericArguments; #endif - private string? _name, _fullName, _assemblyQualifiedName; + private string? _name, _namespace, _fullName, _assemblyQualifiedName; internal TypeName(string? fullName, AssemblyNameInfo? assemblyName, @@ -217,6 +217,7 @@ public string FullName /// This is because determining whether a type truly is a generic type requires loading the type /// and performing a runtime check. /// + [MemberNotNullWhen(false, nameof(_elementOrGenericType))] public bool IsSimple => _elementOrGenericType is null; /// @@ -229,6 +230,7 @@ public string FullName /// Returns true if this is a nested type (e.g., "Namespace.Declaring+Nested"). /// For nested types returns their declaring type. /// + [MemberNotNullWhen(true, nameof(_declaringType))] public bool IsNested => _declaringType is not null; /// @@ -262,7 +264,7 @@ public string Name { if (IsConstructedGenericType) { - _name = TypeNameParserHelpers.GetName(GetGenericTypeDefinition().FullName.AsSpan()).ToString(); + _name = GetGenericTypeDefinition().Name; } else if (IsPointer || IsByRef || IsArray) { @@ -270,13 +272,27 @@ public string Name builder.Append(GetElementType().Name); _name = TypeNameParserHelpers.GetRankOrModifierStringRepresentation(_rankOrModifier, ref builder); } - else if (_nestedNameLength > 0 && _fullName is not null) - { - _name = TypeNameParserHelpers.GetName(_fullName.AsSpan(0, _nestedNameLength)).ToString(); - } else { - _name = TypeNameParserHelpers.GetName(FullName.AsSpan()).ToString(); + // _fullName can be null only in constructed generic or modified types, which we handled above. + Debug.Assert(_fullName is not null); + ReadOnlySpan name = _fullName.AsSpan(); + if (_nestedNameLength > 0) + { + name = name.Slice(0, _nestedNameLength); + } + if (IsNested) + { + // If the type is nested, we know the length of the declaring type's full name. + // Get the characters after that plus one for the '+' separator. + name = name.Slice(_declaringType._nestedNameLength + 1); + } + else if (TypeNameParserHelpers.IndexOfNamespaceDelimiter(name) is int idx && idx >= 0) + { + // If the type is not nested, find the namespace delimiter in the full name and return the substring after it. + name = name.Slice(idx + 1); + } + _name = name.ToString(); } } @@ -284,6 +300,53 @@ public string Name } } + /// + /// The namespace of this type; e.g., "System". + /// + /// This instance is a nested type. + public string Namespace + { + get + { + if (_namespace is null) + { + TypeName rootTypeName = this; + while (!rootTypeName.IsSimple) + { + rootTypeName = rootTypeName._elementOrGenericType; + } + + if (rootTypeName.IsNested) + { + TypeNameParserHelpers.ThrowInvalidOperation_NestedTypeNamespace(); + } + + // By setting the namespace field at the root type name, we avoid recomputing it for all derived names. + if (rootTypeName._namespace is null) + { + // At this point the type does not have a modifier applied to it, so it should have its full name set. + Debug.Assert(rootTypeName._fullName is not null); + ReadOnlySpan rootFullName = rootTypeName._fullName.AsSpan(); + if (rootTypeName._nestedNameLength > 0) + { + rootFullName = rootFullName.Slice(0, rootTypeName._nestedNameLength); + } + if (TypeNameParserHelpers.IndexOfNamespaceDelimiter(rootFullName) is int idx && idx >= 0) + { + rootTypeName._namespace = rootFullName.Slice(0, idx).ToString(); + } + else + { + rootTypeName._namespace = string.Empty; + } + } + _namespace = rootTypeName._namespace; + } + + return _namespace; + } + } + /// /// Represents the total number of instances that are used to describe /// this instance, including any generic arguments or underlying types. @@ -401,6 +464,25 @@ public static bool TryParse(ReadOnlySpan typeName, [NotNullWhen(true)] out return result is not null; } + /// + /// Converts any escaped characters in the input type name or namespace. + /// + /// The input string containing the name to convert. + /// A string of characters with any escaped characters converted to their unescaped form. + /// + /// The unescaped string can be used for looking up the type name or namespace in metadata. + /// This method removes escape characters even if they precede a character that does not require escaping. + /// + public static string Unescape(string name) + { + if (name is null) + { + TypeNameParserHelpers.ThrowArgumentNullException(nameof(name)); + } + + return TypeNameParserHelpers.Unescape(name); + } + /// /// Gets the number of dimensions in an array. /// diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs index 7cafd746b7d176..c44ad7920a94a2 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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; @@ -102,36 +102,56 @@ static int GetUnescapedOffset(ReadOnlySpan input, int startOffset) static bool NeedsEscaping(char c) => c is '[' or ']' or '&' or '*' or ',' or '+' or EscapeCharacter; } - internal static ReadOnlySpan GetName(ReadOnlySpan fullName) + internal static int IndexOfNamespaceDelimiter(ReadOnlySpan fullName) { - // The two-value form of MemoryExtensions.LastIndexOfAny does not suffer - // from the behavior mentioned in the comment at the top of GetFullTypeNameLength. - // It always takes O(m * i) worst-case time and is safe to use here. + // Matches algorithm from ns::FindSep in src\coreclr\utilcode\namespaceutil.cpp + // This could result in the type name beginning with a '.' character. + int index = fullName.LastIndexOf('.'); - int offset = fullName.LastIndexOfAny('.', '+'); + if (index > 0 && fullName[index - 1] == '.') + { + index--; + } + + return index; + } - if (offset > 0 && fullName[offset - 1] == EscapeCharacter) // this should be very rare (IL Emit & pure IL) + internal static string Unescape(string input) + { + int indexOfEscapeCharacter = input.IndexOf(EscapeCharacter); + if (indexOfEscapeCharacter < 0) { - offset = GetUnescapedOffset(fullName, startIndex: offset); + // Nothing to escape, just return the original value. + return input; } - return offset < 0 ? fullName : fullName.Slice(offset + 1); + return UnescapeToBuilder(input, indexOfEscapeCharacter); - static int GetUnescapedOffset(ReadOnlySpan fullName, int startIndex) + static string UnescapeToBuilder(string name, int indexOfEscapeCharacter) { - int offset = startIndex; - for (; offset >= 0; offset--) + // This code path is executed very rarely (IL Emit or pure IL with chars not allowed in C# or F#). + var sb = new ValueStringBuilder(stackalloc char[64]); + sb.EnsureCapacity(name.Length); + sb.Append(name.AsSpan(0, indexOfEscapeCharacter)); + + for (int i = indexOfEscapeCharacter; i < name.Length;) { - if (fullName[offset] is '.' or '+') + char c = name[i++]; + + if (c != EscapeCharacter || i == name.Length) { - if (offset == 0 || fullName[offset - 1] != EscapeCharacter) - { - break; - } - offset--; // skip the escaping character + sb.Append(c); + } + else if (name[i] == EscapeCharacter) // escaped escape character ;) + { + sb.Append(c); + // Consume the escaped escape character, it's important for edge cases + // like escaped escape character followed by another escaped char (example: "\\\\\\+") + i++; } } - return offset; + + return sb.ToString(); } } @@ -350,6 +370,12 @@ internal static bool TryStripFirstCharAndTrailingSpaces(ref ReadOnlySpan s return false; } + [DoesNotReturn] + internal static void ThrowArgumentNullException(string paramName) + { + throw new ArgumentNullException(paramName); + } + [DoesNotReturn] internal static void ThrowArgumentException_InvalidTypeName(int errorIndex) { @@ -411,6 +437,17 @@ internal static void ThrowInvalidOperation_HasToBeArrayClass() #endif } + [DoesNotReturn] + internal static void ThrowInvalidOperation_NestedTypeNamespace() + { +#if SYSTEM_REFLECTION_METADATA + throw new InvalidOperationException(SR.InvalidOperation_NestedTypeNamespace); +#else + Debug.Fail("Expected to be unreachable"); + throw new InvalidOperationException(); +#endif + } + internal static bool IsMaxDepthExceeded(TypeNameParseOptions options, int depth) #if SYSTEM_PRIVATE_CORELIB => false; // CoreLib does not enforce any limits diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserHelpersTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserHelpersTests.cs index 01ec45b50cc3e1..080510a3eb83a8 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserHelpersTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameParserHelpersTests.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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; @@ -38,16 +38,14 @@ public void GetFullTypeNameLengthReturnsExpectedValue(string input, int expected } [Theory] - [InlineData("JustTypeName", "JustTypeName")] - [InlineData("Namespace.TypeName", "TypeName")] - [InlineData("Namespace1.Namespace2.TypeName", "TypeName")] - [InlineData("Namespace.NotNamespace\\.TypeName", "NotNamespace\\.TypeName")] - [InlineData("Namespace1.Namespace2.Containing+Nested", "Nested")] - [InlineData("Namespace1.Namespace2.Not\\+Nested", "Not\\+Nested")] - [InlineData("NotNamespace1\\.NotNamespace2\\.TypeName", "NotNamespace1\\.NotNamespace2\\.TypeName")] - [InlineData("NotNamespace1\\.NotNamespace2\\.Not\\+Nested", "NotNamespace1\\.NotNamespace2\\.Not\\+Nested")] - public void GetNameReturnsJustName(string fullName, string expected) - => Assert.Equal(expected, TypeNameParserHelpers.GetName(fullName.AsSpan()).ToString()); + [InlineData("JustTypeName", -1)] + [InlineData("Namespace.TypeName", 9)] + [InlineData("Namespace1.Namespace2.TypeName", 21)] + [InlineData("Namespace..Name", 9)] + [InlineData("Namespace...Name", 10)] + [InlineData("Namespace..Name.", 15)] + public void IndexOfNamespaceDelimiter(string fullName, int expected) + => Assert.Equal(expected, TypeNameParserHelpers.IndexOfNamespaceDelimiter(fullName.AsSpan())); [Theory] [InlineData("simple", "simple")] diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs index dcf46d54d2d958..abd0ec87059f7d 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs @@ -13,13 +13,21 @@ namespace System.Reflection.Metadata.Tests public class TypeNameTests { [Theory] - [InlineData(" System.Int32", "System.Int32", "Int32")] - [InlineData(" MyNamespace.MyType+NestedType", "MyNamespace.MyType+NestedType", "NestedType")] - public void SpacesAtTheBeginningAreOK(string input, string expectedFullName, string expectedName) + [InlineData(" System.Int32", "System.Int32", "System", "Int32")] + [InlineData(" MyNamespace.MyType+NestedType", "MyNamespace.MyType+NestedType", null, "NestedType")] + public void SpacesAtTheBeginningAreOK(string input, string expectedFullName, string? expectedNamespace, string expectedName) { TypeName parsed = TypeName.Parse(input.AsSpan()); Assert.Equal(expectedName, parsed.Name); + if (expectedNamespace is null) + { + Assert.Throws(() => parsed.Namespace); + } + else + { + Assert.Equal(expectedNamespace, parsed.Namespace); + } Assert.Equal(expectedFullName, parsed.FullName); Assert.Equal(expectedFullName, parsed.AssemblyQualifiedName); } @@ -32,6 +40,7 @@ public void LeadingDotIsNotConsumedForFullTypeNamesWithoutNamespace() TypeName parsed = TypeName.Parse(".NoNamespace".AsSpan()); Assert.Equal("NoNamespace", parsed.Name); + Assert.Empty(parsed.Namespace); Assert.Equal(".NoNamespace", parsed.FullName); Assert.Equal(".NoNamespace", parsed.AssemblyQualifiedName); } @@ -87,6 +96,44 @@ public void ParserIsNotEnforcingRuntimeSpecificRules(string input) } } + [Theory] + [InlineData("Type+InnerType", (string[])["Type", "InnerType"])] + [InlineData("Type+InnerType+InnermostType", (string[])["Type", "InnerType", "InnermostType"])] + [InlineData("NotNested\\+Name", (string[])["NotNested\\+Name"])] + [InlineData("NameEndingInBackSlash\\\\+NestedName", (string[])["NameEndingInBackSlash\\\\", "NestedName"])] + public void NestedName(string input, string[] expectedNames) + { + TypeName parsed = TypeName.Parse(input.AsSpan()); + int i = expectedNames.Length - 1; + while (true) + { + Assert.Equal(expectedNames[i], parsed.Name); + // Caling FullName trims the _fullName value of the instance to just this type's full name. + // Test calling Name again to ensure it's still correct. + _ = parsed.FullName; + Assert.Equal(expectedNames[i], parsed.Name); + if (!parsed.IsNested) + { + break; + } + parsed = parsed.DeclaringType; + i--; + } + Assert.Equal(0, i); + Assert.Equal(0, i); + } + + [Theory] + [InlineData("Int32", "Int32")] + [InlineData("System.Int32", "System.Int32")] + [InlineData("System.Int32[]", "System.Int32[]")] + [InlineData("System.Int32\\[\\]", "System.Int32[]")] + [InlineData("System.Int32\\", "System.Int32\\")] + [InlineData("System.Int32\\\\[]", "System.Int32\\[]")] + [InlineData("System\\.Int32", "System.Int32")] + public void Unescape(string input, string expectedUnescaped) + => Assert.Equal(expectedUnescaped, TypeName.Unescape(input)); + [Theory] [InlineData("Namespace.Kość", "Namespace.Kość")] public void UnicodeCharactersAreAllowedByDefault(string input, string expectedFullName) @@ -136,7 +183,7 @@ private static void EnsureMaxNodesIsRespected(string typeName, int expectedNodeC Assert.Equal(expectedNodeCount, parsed.GetNodeCount()); validate(parsed); - // Specified MaxNodes is less than the actual node count + // Specified MaxNodes is less than the actual node count TypeNameParseOptions less = new() { MaxNodes = expectedNodeCount - 1 @@ -698,6 +745,14 @@ private static void VerifyNestedNames(TypeName parsed, TypeName made, AssemblyNa while (true) { Assert.Equal(parsed.Name, made.Name); + if (parsed.IsNested) + { + Assert.Throws(() => made.Namespace); + } + else + { + Assert.Equal(parsed.Namespace, made.Namespace); + } Assert.Equal(parsed.FullName, made.FullName); Assert.Equal(assemblyName, made.AssemblyName); Assert.NotEqual(parsed.AssemblyQualifiedName, made.AssemblyQualifiedName); @@ -792,6 +847,14 @@ public void ParsedNamesMatchSystemTypeNames(Type type) Type genericType = type.GetGenericTypeDefinition(); TypeName genericTypeName = parsed.GetGenericTypeDefinition(); Assert.Equal(genericType.Name, genericTypeName.Name); + if (genericType.IsNested) + { + Assert.Throws(() => genericTypeName.Namespace); + } + else + { + Assert.Equal(genericType.Namespace ?? "", genericTypeName.Namespace); + } Assert.Equal(genericType.FullName, genericTypeName.FullName); Assert.Equal(genericType.AssemblyQualifiedName, genericTypeName.AssemblyQualifiedName); } @@ -900,7 +963,7 @@ static void Verify(Type type, TypeName typeName, bool ignoreCase) { return Make(GetType(typeName.GetGenericTypeDefinition(), throwOnError, ignoreCase)); } - else if(typeName.IsArray || typeName.IsPointer || typeName.IsByRef) + else if (typeName.IsArray || typeName.IsPointer || typeName.IsByRef) { return Make(GetType(typeName.GetElementType(), throwOnError, ignoreCase)); } @@ -965,6 +1028,14 @@ private static void EnsureBasicMatch(TypeName typeName, Type type) { Assert.Equal(type.AssemblyQualifiedName, typeName.AssemblyQualifiedName); Assert.Equal(type.FullName, typeName.FullName); + if (GetSimpleAncestor(typeName).IsNested) + { + Assert.Throws(() => typeName.Namespace); + } + else + { + Assert.Equal(type.Namespace ?? "", typeName.Namespace); + } Assert.Equal(type.Name, typeName.Name); #if NET @@ -975,6 +1046,15 @@ private static void EnsureBasicMatch(TypeName typeName, Type type) Assert.Equal(type.IsByRef, typeName.IsByRef); Assert.Equal(type.IsConstructedGenericType, typeName.IsConstructedGenericType); Assert.Equal(type.IsNested, typeName.IsNested); + + static TypeName GetSimpleAncestor(TypeName typeName) + { + while (!typeName.IsSimple) + { + typeName = typeName.IsConstructedGenericType ? typeName.GetGenericTypeDefinition() : typeName.GetElementType(); + } + return typeName; + } } public class NestedNonGeneric_0 diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index 569b8318dbba3e..75da6680a5f449 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -134,7 +134,7 @@ public unsafe void ToManaged(out Task? value, ArgumentToManagedCallback else { marshaler(ref arg_3, out T result); - if(!tcs.TrySetResult(result)) + if (!tcs.TrySetResult(result)) { Environment.FailFast("Failed to set result to TaskCompletionSource (marshaler type is none)"); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Utils/SyntaxFactoryExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Utils/SyntaxFactoryExtensions.cs index 81f316e8c452b5..bb0d13c1906809 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Utils/SyntaxFactoryExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Utils/SyntaxFactoryExtensions.cs @@ -107,13 +107,13 @@ public static ExpressionStatementSyntax AssignmentStatement(ExpressionSyntax lef /// /// Returns a for loop with no body: /// - /// for(int ; < ; ++) + /// for (int ; < ; ++) /// ; /// /// public static ForStatementSyntax ForLoop(string indexerIdentifier, ExpressionSyntax lengthExpression) { - // for(int = 0; < ; ++) + // for (int = 0; < ; ++) // ; return ForStatement(EmptyStatement()) .WithDeclaration( diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs index 4344f5dc55b8d8..221b03f1050ead 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs @@ -503,33 +503,12 @@ public void MakePointerType_ByRef_ThrowsTypeLoadException() [InlineData("Outside[][]", typeof(Outside[][]))] [InlineData("Outside`1[System.Nullable`1[System.Boolean]]", typeof(Outside))] [InlineData(".Outside`1", typeof(Outside<>))] - [InlineData(".Outside`1+.Inside`1", typeof(Outside<>.Inside<>))] public void GetTypeByName_ValidType_ReturnsExpected(string typeName, Type expectedType) { Assert.Equal(expectedType, Type.GetType(typeName, throwOnError: false, ignoreCase: false)); Assert.Equal(expectedType, Type.GetType(typeName.ToLower(), throwOnError: false, ignoreCase: true)); } - public static IEnumerable GetTypeByName_InvalidElementType() - { - Type expectedException = PlatformDetection.IsMonoRuntime - ? typeof(ArgumentException) // https://github.com/dotnet/runtime/issues/45033 - : typeof(TypeLoadException); - - yield return new object[] { "System.Int32&&", expectedException, true }; - yield return new object[] { "System.Int32&*", expectedException, true }; - yield return new object[] { "System.Int32&[]", expectedException, true }; - yield return new object[] { "System.Int32&[*]", expectedException, true }; - yield return new object[] { "System.Int32&[,]", expectedException, true }; - - // https://github.com/dotnet/runtime/issues/45033 - if (!PlatformDetection.IsMonoRuntime) - { - yield return new object[] { "System.Void[]", expectedException, true }; - yield return new object[] { "System.TypedReference[]", expectedException, true }; - } - } - [Theory] [InlineData("system.nullable`1[system.int32]", typeof(TypeLoadException), false)] [InlineData("System.NonExistingType", typeof(TypeLoadException), false)] @@ -539,7 +518,14 @@ public static IEnumerable GetTypeByName_InvalidElementType() [InlineData("Outside`1[System.Boolean, System.Int32]", typeof(ArgumentException), true)] [InlineData(".System.Int32", typeof(TypeLoadException), false)] [InlineData("..Outside`1", typeof(TypeLoadException), false)] - [MemberData(nameof(GetTypeByName_InvalidElementType))] + [InlineData(".Outside`1+.Inside`1", typeof(TypeLoadException), false)] + [InlineData("System.Int32&&", typeof(TypeLoadException), true)] + [InlineData("System.Int32&*", typeof(TypeLoadException), true)] + [InlineData("System.Int32&[]", typeof(TypeLoadException), true)] + [InlineData("System.Int32&[*]", typeof(TypeLoadException), true)] + [InlineData("System.Int32&[,]", typeof(TypeLoadException), true)] + [InlineData("System.Void[]", typeof(TypeLoadException), true)] + [InlineData("System.TypedReference[]", typeof(TypeLoadException), true)] public void GetTypeByName_Invalid(string typeName, Type expectedException, bool alwaysThrowsException) { if (!alwaysThrowsException) diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.Mono.cs index 9825299577c395..252ff469956a85 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.Mono.cs @@ -18,6 +18,7 @@ internal unsafe ref partial struct TypeNameResolver private Func? _typeResolver; private bool _throwOnError; private bool _ignoreCase; + private bool _extensibleParser; private ref StackCrawlMark _stackMark; [RequiresUnreferencedCode("The type might be removed")] @@ -27,6 +28,7 @@ internal unsafe ref partial struct TypeNameResolver Func? typeResolver, bool throwOnError, bool ignoreCase, + bool extensibleParser, ref StackCrawlMark stackMark) { ArgumentNullException.ThrowIfNull(typeName); @@ -52,6 +54,7 @@ internal unsafe ref partial struct TypeNameResolver _typeResolver = typeResolver, _throwOnError = throwOnError, _ignoreCase = ignoreCase, + _extensibleParser = extensibleParser, _stackMark = ref stackMark }.Resolve(parsed); } @@ -112,7 +115,8 @@ internal unsafe ref partial struct TypeNameResolver { throw new TypeLoadException(assembly is null ? SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : - SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); + SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName), + typeName: escapedTypeName); } return null; } @@ -140,14 +144,24 @@ internal unsafe ref partial struct TypeNameResolver if (_ignoreCase) bindingFlags |= BindingFlags.IgnoreCase; - type = type.GetNestedType(nestedTypeNames[i], bindingFlags); + if (type is RuntimeType rt) + { + // Compat: Non-extensible parser allows ambiguous matches with ignore case lookup + bool ignoreAmbiguousMatch = !_extensibleParser && _ignoreCase; + type = rt.GetNestedType(nestedTypeNames[i], bindingFlags, ignoreAmbiguousMatch); + } + else + { + type = type.GetNestedType(nestedTypeNames[i], bindingFlags); + } if (type is null) { if (_throwOnError) { throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType, - nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : TypeNameHelpers.Unescape(escapedTypeName))); + nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : TypeName.Unescape(escapedTypeName)), + typeName: escapedTypeName); } return null; } diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 348125a734fb26..b27924bcb1c7bd 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; +using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -175,22 +176,17 @@ private static void SplitName(string? fullname, out string? name, out string? ns return; // Get namespace - int nsDelimiter = fullname.LastIndexOf('.'); - if (nsDelimiter != -1) + int nsDelimiter = TypeNameParserHelpers.IndexOfNamespaceDelimiter(fullname); + if (nsDelimiter > 0) { ns = fullname.Substring(0, nsDelimiter); - int nameLength = fullname.Length - ns.Length - 1; - if (nameLength != 0) - name = fullname.Substring(nsDelimiter + 1, nameLength); - else - name = ""; + name = fullname.Substring(nsDelimiter + 1); Debug.Assert(fullname.Equals(ns + "." + name)); } else { name = fullname; } - } #endregion @@ -683,21 +679,17 @@ private ListBuilder GetFieldCandidates(string? name, BindingFlags bin return candidates; } - private ListBuilder GetNestedTypeCandidates(string? fullname, BindingFlags bindingAttr, bool allowPrefixLookup) + private ListBuilder GetNestedTypeCandidates(string? name, BindingFlags bindingAttr, bool allowPrefixLookup) { - bool prefixLookup; bindingAttr &= ~BindingFlags.Static; - string? name, ns; - MemberListType listType; - SplitName(fullname, out name, out ns); - FilterHelper(bindingAttr, ref name, allowPrefixLookup, out prefixLookup, out _, out listType); + FilterHelper(bindingAttr, ref name, allowPrefixLookup, out bool prefixLookup, out _, out MemberListType listType); RuntimeType[] cache = GetNestedTypes_internal(name, bindingAttr, listType); ListBuilder candidates = new ListBuilder(cache.Length); for (int i = 0; i < cache.Length; i++) { RuntimeType nestedClass = cache[i]; - if (FilterApplyType(nestedClass, bindingAttr, name, prefixLookup, ns)) + if (FilterApplyType(nestedClass, bindingAttr, name, prefixLookup, null)) { candidates.Add(nestedClass); } @@ -1008,33 +1000,39 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) } [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] - public override Type? GetNestedType(string fullname, BindingFlags bindingAttr) + internal Type? GetNestedType([MaybeNull] string name, BindingFlags bindingAttr, bool ignoreAmbiguousMatch) { - ArgumentNullException.ThrowIfNull(fullname); + ArgumentNullException.ThrowIfNull(name); bindingAttr &= ~BindingFlags.Static; - string? name, ns; - MemberListType listType; - SplitName(fullname, out name, out ns); - FilterHelper(bindingAttr, ref name, out _, out listType); + FilterHelper(bindingAttr, ref name, out _, out MemberListType listType); RuntimeType[] cache = GetNestedTypes_internal(name, bindingAttr, listType); RuntimeType? match = null; for (int i = 0; i < cache.Length; i++) { RuntimeType nestedType = cache[i]; - if (FilterApplyType(nestedType, bindingAttr, name, false, ns)) + if (FilterApplyType(nestedType, bindingAttr, name, false, null)) { if (match != null) throw ThrowHelper.GetAmbiguousMatchException(match); match = nestedType; + + if (ignoreAmbiguousMatch) + break; } } return match; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] + public override Type? GetNestedType(string name, BindingFlags bindingAttr) + { + return GetNestedType(name, bindingAttr, ignoreAmbiguousMatch: false); + } + [DynamicallyAccessedMembers(GetAllMembers)] public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) { @@ -1683,7 +1681,8 @@ internal void CallDefaultStructConstructor(ref byte value) [Flags] // Types of entries cached in TypeCache - private enum TypeCacheEntries { + private enum TypeCacheEntries + { IsGenericTypeDef = 1, IsDelegate = 2, IsValueType = 4, 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 3fc4f6082c9b28..5740704b764651 100644 --- a/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Type.Mono.cs @@ -30,7 +30,7 @@ internal IntPtr GetUnderlyingNativeHandle() public static Type? GetType(string typeName, bool throwOnError, bool ignoreCase) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RuntimeType.GetType(typeName, throwOnError, ignoreCase, ref stackMark); + return GetType(typeName, null, null, throwOnError, ignoreCase, false, ref stackMark); } [RequiresUnreferencedCode("The type might be removed")] @@ -38,7 +38,7 @@ internal IntPtr GetUnderlyingNativeHandle() public static Type? GetType(string typeName, bool throwOnError) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RuntimeType.GetType(typeName, throwOnError, false, ref stackMark); + return GetType(typeName, null, null, throwOnError, false, false, ref stackMark); } [RequiresUnreferencedCode("The type might be removed")] @@ -46,7 +46,7 @@ internal IntPtr GetUnderlyingNativeHandle() public static Type? GetType(string typeName) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return RuntimeType.GetType(typeName, false, false, ref stackMark); + return GetType(typeName, null, null, false, false, false, ref stackMark); } [RequiresUnreferencedCode("The type might be removed")] @@ -54,7 +54,7 @@ internal IntPtr GetUnderlyingNativeHandle() public static Type? GetType(string typeName, Func? assemblyResolver, Func? typeResolver) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return GetType(typeName, assemblyResolver, typeResolver, false, false, ref stackMark); + return GetType(typeName, assemblyResolver, typeResolver, false, false, true, ref stackMark); } [RequiresUnreferencedCode("The type might be removed")] @@ -62,7 +62,7 @@ internal IntPtr GetUnderlyingNativeHandle() public static Type? GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return GetType(typeName, assemblyResolver, typeResolver, throwOnError, false, ref stackMark); + return GetType(typeName, assemblyResolver, typeResolver, throwOnError, false, true, ref stackMark); } [RequiresUnreferencedCode("The type might be removed")] @@ -70,13 +70,13 @@ internal IntPtr GetUnderlyingNativeHandle() public static Type? GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase) { StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; - return GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark); + return GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, true, ref stackMark); } [RequiresUnreferencedCode("The type might be removed")] - private static Type? GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark) + private static Type? GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, ref StackCrawlMark stackMark) { - return TypeNameResolver.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark); + return TypeNameResolver.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, extensibleParser, ref stackMark); } public static Type? GetTypeFromHandle(RuntimeTypeHandle handle) diff --git a/src/mono/System.Private.CoreLib/src/System/TypeLoadException.Mono.cs b/src/mono/System.Private.CoreLib/src/System/TypeLoadException.Mono.cs index 9f4f4ced458b04..78670d58c3237c 100644 --- a/src/mono/System.Private.CoreLib/src/System/TypeLoadException.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/TypeLoadException.Mono.cs @@ -1,18 +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.CodeAnalysis; + namespace System { public partial class TypeLoadException { - // Called by runtime - internal TypeLoadException(string className, string assemblyName) + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Called by runtime")] + private TypeLoadException(string className, string assemblyName) : this(null) { _className = className; _assemblyName = assemblyName; } + // Because the Mono runtime has a dependency on (string, string) constructors of exception types, + // we add a dummy parameter with a default value. + // In order to use this overload, callers should specify the typeName parameter by name. + internal TypeLoadException(string message, string typeName, bool _ = true) + : base(message) + { + HResult = HResults.COR_E_TYPELOAD; + _className = typeName; + } + private void SetMessageField() { if (_message != null) diff --git a/src/mono/browser/debugger/BrowserDebugProxy/DevToolsHelper.cs b/src/mono/browser/debugger/BrowserDebugProxy/DevToolsHelper.cs index 99a4185c5c3162..1c258588ef873f 100644 --- a/src/mono/browser/debugger/BrowserDebugProxy/DevToolsHelper.cs +++ b/src/mono/browser/debugger/BrowserDebugProxy/DevToolsHelper.cs @@ -518,7 +518,7 @@ public PerScopeCache() internal sealed class ConcurrentExecutionContextDictionary { - private ConcurrentDictionary> contexts = new (); + private ConcurrentDictionary> contexts = new(); public ExecutionContext GetCurrentContext(SessionId sessionId) => TryGetCurrentExecutionContextValue(sessionId, out ExecutionContext context) ? context diff --git a/src/mono/browser/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/browser/debugger/BrowserDebugProxy/EvaluateExpression.cs index fc35a907385754..1f00048cf2548a 100644 --- a/src/mono/browser/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/browser/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -53,7 +53,7 @@ private sealed partial class ExpressionSyntaxReplacer : CSharpSyntaxWalker public bool hasMethodCalls; public bool hasElementAccesses; public bool hasStringExpressionStatement; - internal List variableDefinitions = new (); + internal List variableDefinitions = new(); public void VisitInternal(SyntaxNode node) { @@ -227,7 +227,7 @@ void AddLocalVariableWithValue(string idName, JObject value) if (localsSet.Contains(idName)) return; localsSet.Add(idName); - variableDefinitions.Add(new (idName, value, ConvertJSToCSharpLocalVariableAssignment(idName, value))); + variableDefinitions.Add(new(idName, value, ConvertJSToCSharpLocalVariableAssignment(idName, value))); } } } diff --git a/src/mono/browser/debugger/BrowserDebugProxy/MemberReferenceResolver.cs b/src/mono/browser/debugger/BrowserDebugProxy/MemberReferenceResolver.cs index 0498dd07cfd6fb..9798fd400d06d3 100644 --- a/src/mono/browser/debugger/BrowserDebugProxy/MemberReferenceResolver.cs +++ b/src/mono/browser/debugger/BrowserDebugProxy/MemberReferenceResolver.cs @@ -434,7 +434,7 @@ public async Task Resolve( if (!isMultidimensional && type == "string") { var eaExpressionFormatted = elementAccessStrExpression.Replace('.', '_'); // instance_str - variableDefinitions.Add(new (eaExpressionFormatted, rootObject, ExpressionEvaluator.ConvertJSToCSharpLocalVariableAssignment(eaExpressionFormatted, rootObject))); + variableDefinitions.Add(new(eaExpressionFormatted, rootObject, ExpressionEvaluator.ConvertJSToCSharpLocalVariableAssignment(eaExpressionFormatted, rootObject))); var eaFormatted = elementAccessStr.Replace('.', '_'); // instance_str[1] var variableDef = await ExpressionEvaluator.GetVariableDefinitions(this, variableDefinitions, invokeToStringInObject: false, token); return await ExpressionEvaluator.EvaluateSimpleExpression(this, eaFormatted, elementAccessStr, variableDef, logger, token); diff --git a/src/mono/browser/debugger/BrowserDebugProxy/MonoProxy.cs b/src/mono/browser/debugger/BrowserDebugProxy/MonoProxy.cs index 6437b97217fc82..08d34d0e35d93a 100644 --- a/src/mono/browser/debugger/BrowserDebugProxy/MonoProxy.cs +++ b/src/mono/browser/debugger/BrowserDebugProxy/MonoProxy.cs @@ -25,7 +25,7 @@ internal class MonoProxy : DevToolsProxy internal string CachePathSymbolServer { get; private set; } private readonly HashSet sessions = new HashSet(); private static readonly string[] s_executionContextIndependentCDPCommandNames = { "DotnetDebugger.setDebuggerProperty", "DotnetDebugger.runTests" }; - internal ConcurrentExecutionContextDictionary Contexts = new (); + internal ConcurrentExecutionContextDictionary Contexts = new(); public static HttpClient HttpClient => new HttpClient(); @@ -1468,7 +1468,7 @@ private Result AddCallStackInfoToException(Result _error, ExecutionContext conte { try { var retStackTrace = new JArray(); - foreach(var call in context.CallStack) + foreach (var call in context.CallStack) { if (call.Id < scopeId) continue; diff --git a/src/mono/browser/debugger/BrowserDebugProxy/MonoSDBHelper.cs b/src/mono/browser/debugger/BrowserDebugProxy/MonoSDBHelper.cs index e8e8b7916e9817..f249812a39bb0c 100644 --- a/src/mono/browser/debugger/BrowserDebugProxy/MonoSDBHelper.cs +++ b/src/mono/browser/debugger/BrowserDebugProxy/MonoSDBHelper.cs @@ -973,7 +973,7 @@ public void ResetStore(DebugStore store) } public void ResetTypes() { - this.types = new (); + this.types = new(); } public async Task GetAssemblyInfo(int assemblyId, CancellationToken token) diff --git a/src/mono/browser/debugger/DebuggerTestSuite/DevToolsClient.cs b/src/mono/browser/debugger/DebuggerTestSuite/DevToolsClient.cs index 64bf879715cc20..27633d74c49897 100644 --- a/src/mono/browser/debugger/DebuggerTestSuite/DevToolsClient.cs +++ b/src/mono/browser/debugger/DebuggerTestSuite/DevToolsClient.cs @@ -51,7 +51,7 @@ public Task ShutdownAsync(CancellationToken cancellationToken) protected async Task ConnectToWebServer(Uri uri, CancellationToken token) { // connects to the webserver to start the proxy - ClientWebSocket clientSocket = new (); + ClientWebSocket clientSocket = new(); clientSocket.Options.KeepAliveInterval = Timeout.InfiniteTimeSpan; logger.LogDebug("Client connecting to {0}", uri); await clientSocket.ConnectAsync(uri, token); diff --git a/src/mono/browser/debugger/DebuggerTestSuite/ExceptionTests.cs b/src/mono/browser/debugger/DebuggerTestSuite/ExceptionTests.cs index 184cbaae6fb9cf..f20d7d08349ea1 100644 --- a/src/mono/browser/debugger/DebuggerTestSuite/ExceptionTests.cs +++ b/src/mono/browser/debugger/DebuggerTestSuite/ExceptionTests.cs @@ -283,7 +283,7 @@ public async Task ExceptionTestAllWithReload(string entry_method_name, string cl // Hit resume to skip int count = 0; var taskWait = insp.WaitFor(Inspector.APP_READY); - while(true) + while (true) { await cli.SendCommand("Debugger.resume", null, token); count++; diff --git a/src/mono/browser/debugger/DebuggerTestSuite/Inspector.cs b/src/mono/browser/debugger/DebuggerTestSuite/Inspector.cs index 8ee9d62f9a2a0e..6a333cdd851bbe 100644 --- a/src/mono/browser/debugger/DebuggerTestSuite/Inspector.cs +++ b/src/mono/browser/debugger/DebuggerTestSuite/Inspector.cs @@ -24,9 +24,9 @@ class Inspector // https://console.spec.whatwg.org/#formatting-specifiers private static Regex _consoleArgsRegex = new(@"(%[sdifoOc])", RegexOptions.Compiled); - ConcurrentDictionary> notifications = new (); - ConcurrentDictionary>> eventListeners = new (); - ConcurrentQueue<(string, JObject)> nextNotifications = new (); //in a multithreaded runtime we can receive more than one pause at same time + ConcurrentDictionary> notifications = new(); + ConcurrentDictionary>> eventListeners = new(); + ConcurrentQueue<(string, JObject)> nextNotifications = new(); //in a multithreaded runtime we can receive more than one pause at same time public const string PAUSE = "pause"; public const string APP_READY = "app-ready"; public CancellationToken Token { get; set; } diff --git a/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs b/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs index 3b0d795dfe5f4d..555c23a79b6d3f 100644 --- a/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs +++ b/src/mono/browser/debugger/DebuggerTestSuite/MiscTests.cs @@ -1166,7 +1166,7 @@ await StepAndCheck(StepKind.Resume, "dotnet://debugger-test.dll/debugger-test.cs public static TheoryData CountToTen() { var data = new TheoryData(); - for(int i=0;i<10;i++) + for (int i=0;i<10;i++) { data.Add(i); } diff --git a/src/mono/mono/metadata/monitor.c b/src/mono/mono/metadata/monitor.c index 6a72695e9fbedc..2e08f94d3361e7 100644 --- a/src/mono/mono/metadata/monitor.c +++ b/src/mono/mono/metadata/monitor.c @@ -82,9 +82,9 @@ struct _MonitorArray { MonoThreadsSync monitors [MONO_ZERO_LEN_ARRAY]; }; -#define mono_monitor_allocator_lock() mono_os_mutex_lock (&monitor_mutex) -#define mono_monitor_allocator_unlock() mono_os_mutex_unlock (&monitor_mutex) -static mono_mutex_t monitor_mutex; +#define mono_monitor_allocator_lock() mono_coop_mutex_lock (&monitor_mutex) +#define mono_monitor_allocator_unlock() mono_coop_mutex_unlock (&monitor_mutex) +static MonoCoopMutex monitor_mutex; static MonoThreadsSync *monitor_freelist; static MonitorArray *monitor_allocated; static int array_size = 16; @@ -255,7 +255,7 @@ lock_word_new_flat (gint32 owner) void mono_monitor_init (void) { - mono_os_mutex_init_recursive (&monitor_mutex); + mono_coop_mutex_init_recursive (&monitor_mutex); } static int diff --git a/src/mono/mono/mini/arrays.cs b/src/mono/mono/mini/arrays.cs index 2358acb0e7cb89..8f18682cc0957b 100644 --- a/src/mono/mono/mini/arrays.cs +++ b/src/mono/mono/mini/arrays.cs @@ -470,7 +470,7 @@ public static int test_100_4_dimensional_arrays () { public static int test_0_bug_71454 () { int[,] a = new int[4,4]; int[,] b = new int[4,4]; - for(int i = 0; i < 4; ++i) { + for (int i = 0; i < 4; ++i) { b[0,0] = a[0,i % 4]; } return 0; @@ -551,7 +551,7 @@ public static int test_0_invalid_new_array_size () { public static int test_0_multidym_array_with_negative_lower_bound () { int[,] x = (int[,]) Array.CreateInstance(typeof (int), new int[] { 2, 2 }, new int[] { -2, -3 }); - if(x.GetLowerBound (0) != -2) + if (x.GetLowerBound (0) != -2) return 1; if (x.GetLowerBound (1) != -3) return 2; @@ -762,7 +762,7 @@ public static int test_0_long_indices () { public static int test_0_ldelema_2_64bit () { bool[,] test = new bool[201,201]; int x,y; - for(x=-100;x<100;x++) for(y=-100;y<100;y++){ + for (x=-100;x<100;x++) for (y=-100;y<100;y++){ test[x+100,y+100] = true; } return 0; diff --git a/src/mono/mono/mini/bench.cs b/src/mono/mono/mini/bench.cs index 15cd67d60c6990..93648440e10f07 100644 --- a/src/mono/mono/mini/bench.cs +++ b/src/mono/mono/mini/bench.cs @@ -82,8 +82,8 @@ public static int test_0_logic_run () bool flag13 = true; // First set of tests. - for(iter = 0; iter < 2000000; ++iter) { - if((flag1 || flag2) && (flag3 || flag4) && + for (iter = 0; iter < 2000000; ++iter) { + if ((flag1 || flag2) && (flag3 || flag4) && (flag5 || flag6 || flag7)) { flag8 = !flag8; @@ -212,25 +212,25 @@ private int test_0_blur(int size) { int iterations = 1; - while(iterations-- > 0) { + while (iterations-- > 0) { // Draw fake picture - for(int i = 0; i < size; i++) { - for(int j = 0; j < size; j++) { + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { arr1[i, j] = (byte) (i%255); } } - for(int n = 0; n < num; n++) { // num rounds of blurring - for(int i = 3; i < size-3; i++) // vertical blur arr1 -> arr2 - for(int j = 0; j < size; j++) + for (int n = 0; n < num; n++) { // num rounds of blurring + for (int i = 3; i < size-3; i++) // vertical blur arr1 -> arr2 + for (int j = 0; j < size; j++) arr2[i, j] = (byte)((arr1[i-3, j] + arr1[i+3, j] + 6*(arr1[i-2, j]+arr1[i+2, j]) + 15*(arr1[i-1, j]+arr1[i+1, j]) + 20*arr1[i, j] + 32)>>6); - for(int j = 3; j < size-3; j++) // horizontal blur arr1 -> arr2 - for(int i = 0; i < size; i++) + for (int j = 3; j < size-3; j++) // horizontal blur arr1 -> arr2 + for (int i = 0; i < size; i++) arr1[i, j] = (byte)((arr2[i, j-3] + arr2[i, j+3] + 6*(arr2[i, j-2]+arr2[i, j+2]) + 15*(arr2[i, j-1]+arr2[i, j+1]) diff --git a/src/mono/mono/mini/devirtualization.cs b/src/mono/mono/mini/devirtualization.cs index 20ca3bdb70adf1..de11baa414c223 100644 --- a/src/mono/mono/mini/devirtualization.cs +++ b/src/mono/mono/mini/devirtualization.cs @@ -161,35 +161,35 @@ static public int test_0_npe_still_happens() { try { y.method1(); return 1; - } catch(NullReferenceException e) { + } catch (NullReferenceException e) { ;//ok } try { y.method2(); return 2; - } catch(NullReferenceException e) { + } catch (NullReferenceException e) { ;//ok } try { y.method3(); return 3; - } catch(NullReferenceException e) { + } catch (NullReferenceException e) { ;//ok } try { x.method4(); return 4; - } catch(NullReferenceException e) { + } catch (NullReferenceException e) { ;//ok } try { x.method5(); return 5; - } catch(NullReferenceException e) { + } catch (NullReferenceException e) { ;//ok } diff --git a/src/mono/mono/mini/generics.cs b/src/mono/mono/mini/generics.cs index 0c6346c63d48da..fe68180f715e59 100644 --- a/src/mono/mono/mini/generics.cs +++ b/src/mono/mono/mini/generics.cs @@ -496,7 +496,7 @@ public static int test_0_inline_infinite_polymorphic_recursion () { } private static void f(int i) { - if(i==42) f>(i); + if (i==42) f>(i); } // This cannot be made to work with full-aot, since there it is impossible to diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 53ca6d2a10804a..04bf096566f838 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -2676,7 +2676,54 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas !strncmp ("Vector", klass_name, 6) && !strcmp (tm, "get_IsHardwareAccelerated"))) { *op = MINT_LDC_I4_0; - } + } else if ((target_method->klass == mono_defaults.double_class) || (target_method->klass == mono_defaults.single_class)) { + MonoGenericContext *method_context = mono_method_get_context (target_method); + bool isDouble = target_method->klass == mono_defaults.double_class; + if (!strcmp (tm, "ConvertToIntegerNative") && + method_context != NULL && + method_context->method_inst->type_argc == 1) { + MonoTypeEnum tto_type = method_context->method_inst->type_argv [0]->type; + switch (tto_type) { + case MONO_TYPE_I1: + *op = isDouble ? MINT_CONV_I1_R8 : MINT_CONV_I1_R4; + break; + case MONO_TYPE_I2: + *op = isDouble ? MINT_CONV_I2_R8 : MINT_CONV_I2_R4; + break; +#if TARGET_SIZEOF_VOID_P == 4 + case MONO_TYPE_I: +#endif + case MONO_TYPE_I4: + *op = isDouble ? MINT_CONV_I4_R8 : MINT_CONV_I4_R4; + break; +#if TARGET_SIZEOF_VOID_P == 8 + case MONO_TYPE_I: +#endif + case MONO_TYPE_I8: + *op = isDouble ? MINT_CONV_I8_R8 : MINT_CONV_I8_R4; + break; + case MONO_TYPE_U1: + *op = isDouble ? MINT_CONV_U1_R8 : MINT_CONV_U1_R4; + break; + case MONO_TYPE_U2: + *op = isDouble ? MINT_CONV_U2_R8 : MINT_CONV_U2_R4; + break; +#if TARGET_SIZEOF_VOID_P == 4 + case MONO_TYPE_U: +#endif + case MONO_TYPE_U4: + *op = isDouble ? MINT_CONV_U4_R8 : MINT_CONV_U4_R4; + break; +#if TARGET_SIZEOF_VOID_P == 8 + case MONO_TYPE_U: +#endif + case MONO_TYPE_U8: + *op = isDouble ? MINT_CONV_U8_R8 : MINT_CONV_U8_R4; + break; + default: return FALSE; + } + } + } return FALSE; } diff --git a/src/mono/mono/mini/intrinsics.c b/src/mono/mono/mini/intrinsics.c index 41c5be9c82022c..1b2c85c64bf744 100644 --- a/src/mono/mono/mini/intrinsics.c +++ b/src/mono/mono/mini/intrinsics.c @@ -2322,6 +2322,64 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign return ins; } } + } else if ((cmethod->klass == mono_defaults.double_class) || (cmethod->klass == mono_defaults.single_class)) { + MonoGenericContext *method_context = mono_method_get_context (cmethod); + bool isDouble = cmethod->klass == mono_defaults.double_class; + if (!strcmp (cmethod->name, "ConvertToIntegerNative") && + method_context != NULL && + method_context->method_inst->type_argc == 1) { + int opcode = 0; + MonoTypeEnum tto_type = method_context->method_inst->type_argv [0]->type; + MonoStackType tto_stack = STACK_I4; + switch (tto_type) { + case MONO_TYPE_I1: + opcode = isDouble ? OP_FCONV_TO_I1 : OP_RCONV_TO_I1; + break; + case MONO_TYPE_I2: + opcode = isDouble ? OP_FCONV_TO_I2 : OP_RCONV_TO_I2; + break; +#if TARGET_SIZEOF_VOID_P == 4 + case MONO_TYPE_I: +#endif + case MONO_TYPE_I4: + opcode = isDouble ? OP_FCONV_TO_I4 : OP_RCONV_TO_I4; + break; +#if TARGET_SIZEOF_VOID_P == 8 + case MONO_TYPE_I: +#endif + case MONO_TYPE_I8: + opcode = isDouble ? OP_FCONV_TO_I8 : OP_RCONV_TO_I8; + tto_stack = STACK_I8; + break; + case MONO_TYPE_U1: + opcode = isDouble ? OP_FCONV_TO_U1 : OP_RCONV_TO_U1; + break; + case MONO_TYPE_U2: + opcode = isDouble ? OP_FCONV_TO_U2 : OP_RCONV_TO_U2; + break; +#if TARGET_SIZEOF_VOID_P == 4 + case MONO_TYPE_U: +#endif + case MONO_TYPE_U4: + opcode = isDouble ? OP_FCONV_TO_U4 : OP_RCONV_TO_U4; + break; +#if TARGET_SIZEOF_VOID_P == 8 + case MONO_TYPE_U: +#endif + case MONO_TYPE_U8: + opcode = isDouble ? OP_FCONV_TO_U8 : OP_RCONV_TO_U8; + tto_stack = STACK_I8; + break; + default: return NULL; + } + + if (opcode != 0) { + int ireg = mono_alloc_ireg (cfg); + EMIT_NEW_UNALU (cfg, ins, opcode, ireg, args [0]->dreg); + ins->type = tto_stack; + return mono_decompose_opcode(cfg, ins); + } + } } ins = mono_emit_simd_intrinsics (cfg, cmethod, fsig, args); diff --git a/src/mono/sample/wasm/browser-logprofile/Program.cs b/src/mono/sample/wasm/browser-logprofile/Program.cs index 6bebd913c0592b..ab40a9a1d366b8 100644 --- a/src/mono/sample/wasm/browser-logprofile/Program.cs +++ b/src/mono/sample/wasm/browser-logprofile/Program.cs @@ -21,7 +21,7 @@ public static void TakeHeapshot() { } [JSExport] public static int TestMeaning() { - for(int i=0; i<100; i++){ + for (int i=0; i<100; i++){ var r = new int[1000]; } diff --git a/src/mono/wasi/Wasi.Build.Tests/InvariantTests.cs b/src/mono/wasi/Wasi.Build.Tests/InvariantTests.cs index a7a2785e65bfdd..f41172255e1d11 100644 --- a/src/mono/wasi/Wasi.Build.Tests/InvariantTests.cs +++ b/src/mono/wasi/Wasi.Build.Tests/InvariantTests.cs @@ -24,7 +24,7 @@ public void ConsolePublishAndRunForSingleFileBundle_InvariantTimeZone(string con { string extraProperties = invariantTimezone ? "true" : ""; CommandResult res = ConsolePublishAndRunForSingleFileBundleInternal(config, "InvariantTimezones.cs", aot, extraProperties: extraProperties); - if(invariantTimezone) + if (invariantTimezone) Assert.Contains("Could not find Asia/Tokyo", res.Output); else Assert.Contains("Asia/Tokyo BaseUtcOffset is 09:00:00", res.Output); diff --git a/src/mono/wasi/Wasi.Build.Tests/PInvokeTableGeneratorTests.cs b/src/mono/wasi/Wasi.Build.Tests/PInvokeTableGeneratorTests.cs index 1405ec20bb7157..709fad553ca3bc 100644 --- a/src/mono/wasi/Wasi.Build.Tests/PInvokeTableGeneratorTests.cs +++ b/src/mono/wasi/Wasi.Build.Tests/PInvokeTableGeneratorTests.cs @@ -21,7 +21,7 @@ public PInvokeTableGeneratorTests(ITestOutputHelper output, SharedBuildPerTestCl [MemberData(nameof(TestDataForConsolePublishAndRunRelease))] public void InteropSupportForUnmanagedEntryPointWithoutDelegate(bool singleFileBundle, bool aot) { - if(aot) + if (aot) { // Active issue https://github.com/dotnet/runtime/issues/101276 return; diff --git a/src/mono/wasm/Wasm.Build.Tests/IcuTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/IcuTestsBase.cs index 1bf7e100314e5b..daf9b7ea597176 100644 --- a/src/mono/wasm/Wasm.Build.Tests/IcuTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/IcuTestsBase.cs @@ -83,7 +83,7 @@ protected string GetProgramText(string testedLocales, bool onlyPredefinedCulture continue; }} }} - catch(CultureNotFoundException cnfe) when (ctorShouldFail && cnfe.Message.Contains($""{{testLocale.Code}} is an invalid culture identifier."")) + catch (CultureNotFoundException cnfe) when (ctorShouldFail && cnfe.Message.Contains($""{{testLocale.Code}} is an invalid culture identifier."")) {{ Console.WriteLine($""{{testLocale.Code}}: Success. .ctor failed as expected.""); continue; @@ -130,7 +130,7 @@ protected ProjectInfo CreateIcuProject( string programText = GetProgramText(testedLocales, onlyPredefinedCultures); File.WriteAllText(programPath, programText); _testOutput.WriteLine($"----- Program: -----{Environment.NewLine}{programText}{Environment.NewLine}-------"); - + UpdateBrowserMainJs(); return info; } @@ -160,7 +160,7 @@ protected async Task PublishAndRunIcuTest( RunResult runOutput = await RunForPublishWithWebServer(runOptions); return $"{buildOutput}\n{runOutput.TestOutput}"; } - catch(Exception ex) + catch (Exception ex) { Console.WriteLine($"Exception: {ex}; _testOutput={_testOutput}"); throw; diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index 752f6ebf8a4d80..3b70408b34732d 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -35,7 +35,7 @@ public WasmTemplateTestsBase(ITestOutputHelper output, SharedBuildPerTestClassFi private Dictionary browserProgramReplacements = new Dictionary { - { "while(true)", $"int i = 0;{Environment.NewLine}while(i++ < 0)" }, // the test has to be fast, skip the loop + { "while (true)", $"int i = 0;{Environment.NewLine}while (i++ < 0)" }, // the test has to be fast, skip the loop { "partial class StopwatchSample", $"return 42;{Environment.NewLine}partial class StopwatchSample" }, { "Hello, Browser!", "TestOutput -> Hello, Browser!" } }; @@ -219,7 +219,7 @@ protected void ReplaceFile(string pathRelativeToProjectDir, string pathWithNewCo protected void DeleteFile(string pathRelativeToProjectDir) { var deletedFilePath = Path.Combine(_projectDir, pathRelativeToProjectDir); - if(File.Exists(deletedFilePath)) + if (File.Exists(deletedFilePath)) { File.Delete(deletedFilePath); } diff --git a/src/mono/wasm/templates/templates/browser/Program.cs b/src/mono/wasm/templates/templates/browser/Program.cs index a9726e67fd6aca..563b061eb60d7c 100644 --- a/src/mono/wasm/templates/templates/browser/Program.cs +++ b/src/mono/wasm/templates/templates/browser/Program.cs @@ -8,7 +8,7 @@ if (args.Length == 1 && args[0] == "start") StopwatchSample.Start(); -while(true) +while (true) { StopwatchSample.Render(); await Task.Delay(1000); diff --git a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs index f2602ce5e1e6ca..7655335bec0f9b 100644 --- a/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs +++ b/src/native/managed/cdacreader/Microsoft.Diagnostics.DataContractReader/CachingContractRegistry.cs @@ -24,7 +24,7 @@ public CachingContractRegistry(Target target, TryGetContractVersionDelegate tryG { _target = target; _tryGetContractVersion = tryGetContractVersion; - _factories = new () { + _factories = new() { [typeof(IException)] = new ExceptionFactory(), [typeof(ILoader)] = new LoaderFactory(), [typeof(IEcmaMetadata)] = new EcmaMetadataFactory(), diff --git a/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs b/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs index 5ed2083de4cf24..1688f34db24d15 100644 --- a/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs +++ b/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs @@ -327,7 +327,7 @@ private static void AppendTypeCore(ref TypeNameBuilder tnb, Contracts.TypeHandle // The following flags in the FormatFlags argument are significant: FormatNamespace FormatFullInst FormatAssembly FormatNoVersion private static void AppendInst(Target target, StringBuilder stringBuilder, ReadOnlySpan inst, TypeNameFormat format) { - TypeNameBuilder tnb = new (stringBuilder, target, format, initialStateIsName: true); + TypeNameBuilder tnb = new(stringBuilder, target, format, initialStateIsName: true); AppendInst(ref tnb, inst, format); } diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs index 7ae3255fac5bee..23f51f761ae630 100644 --- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs +++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs @@ -547,7 +547,7 @@ private bool ExecuteInternal() if (be9 is not null) allowedParallelism = be9.RequestCores(allowedParallelism); } - catch(NotImplementedException) + catch (NotImplementedException) { // RequestCores is not implemented in TaskHostFactory be9 = null; diff --git a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs index 2c1304b0dc3600..e13a84c02f6b22 100644 --- a/src/tasks/AppleAppBuilder/AppleAppBuilder.cs +++ b/src/tasks/AppleAppBuilder/AppleAppBuilder.cs @@ -316,7 +316,7 @@ public override bool Execute() } List extraLinkerArgs = new List(); - foreach(ITaskItem item in ExtraLinkerArguments) + foreach (ITaskItem item in ExtraLinkerArguments) { extraLinkerArgs.Add(item.ItemSpec); } diff --git a/src/tasks/LibraryBuilder/LibraryBuilder.cs b/src/tasks/LibraryBuilder/LibraryBuilder.cs index 1ad0a3cbae7f37..c9e621a13f702f 100644 --- a/src/tasks/LibraryBuilder/LibraryBuilder.cs +++ b/src/tasks/LibraryBuilder/LibraryBuilder.cs @@ -289,7 +289,7 @@ private void WriteAutoInitializationFromTemplate() { string dataSymbol = "NULL"; string dataLenSymbol = "0"; - StringBuilder externBundledResourcesSymbols = new ("#if defined(BUNDLED_RESOURCES)\nextern void mono_register_resources_bundle (void);"); + StringBuilder externBundledResourcesSymbols = new("#if defined(BUNDLED_RESOURCES)\nextern void mono_register_resources_bundle (void);"); if (BundledRuntimeConfig?.ItemSpec != null) { dataSymbol = BundledRuntimeConfig.GetMetadata("DataSymbol"); diff --git a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs index a71c39add20b02..c1935a6be12e14 100644 --- a/src/tasks/MobileBuildTasks/Android/AndroidProject.cs +++ b/src/tasks/MobileBuildTasks/Android/AndroidProject.cs @@ -95,30 +95,30 @@ private static string BuildClangArgs(ClangBuildOptions buildOptions) { StringBuilder ret = new StringBuilder(); - foreach(string compilerArg in buildOptions.CompilerArguments) + foreach (string compilerArg in buildOptions.CompilerArguments) { ret.Append(compilerArg); ret.Append(' '); } - foreach(string includeDir in buildOptions.IncludePaths) + foreach (string includeDir in buildOptions.IncludePaths) { ret.Append($"-I {includeDir} "); } - foreach(string linkerArg in buildOptions.LinkerArguments) + foreach (string linkerArg in buildOptions.LinkerArguments) { ret.Append($"-Xlinker {linkerArg} "); } - foreach(string source in buildOptions.Sources) + foreach (string source in buildOptions.Sources) { ret.Append(source); ret.Append(' '); } HashSet libDirs = new HashSet(); - foreach(string lib in buildOptions.NativeLibraryPaths) + foreach (string lib in buildOptions.NativeLibraryPaths) { string rootPath = Path.GetDirectoryName(lib)!; string libName = Path.GetFileName(lib); diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs b/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs index b0e242353cc90c..3828448dcaef77 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/Ndk.cs @@ -48,7 +48,7 @@ private static string GetNdkPath(IEnumerable probingPaths) { string ret = ""; - foreach(string path in probingPaths) + foreach (string path in probingPaths) { if (Directory.Exists(path)) { @@ -104,7 +104,7 @@ private static NdkVersion ReadVersion() var splitChars = new char[] {'='}; string? ver = null; - foreach(string l in File.ReadAllLines(sourcePropertiesPath)) + foreach (string l in File.ReadAllLines(sourcePropertiesPath)) { string line = l.Trim (); if (!line.StartsWith("Pkg.Revision", StringComparison.Ordinal)) diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs index 0741e92dfe8116..862afffacfdeec 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkTools.cs @@ -119,7 +119,7 @@ private void ValidateRequiredProps(string hostOS) { string host = validHosts[hostOS]; } - catch(KeyNotFoundException) + catch (KeyNotFoundException) { throw new Exception("An invalid HostOS value was supplied. Only windows, osx, and linux are supported."); } diff --git a/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs b/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs index d45e88d2d16892..cc1243d6fbd3bc 100644 --- a/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs +++ b/src/tasks/MobileBuildTasks/Android/Ndk/NdkVersion.cs @@ -17,7 +17,7 @@ public NdkVersion(string? version) string? ver = version?.Trim(); if (string.IsNullOrEmpty(ver)) { - throw new ArgumentException ("must be a non-empty string", nameof (version)); + throw new ArgumentException ("must be a non-empty string", nameof(version)); } int tagIdx = ver!.IndexOf('-'); diff --git a/src/tasks/MobileBuildTasks/Apple/AppleProject.cs b/src/tasks/MobileBuildTasks/Apple/AppleProject.cs index 6a0f61c1968c41..4f3219a8fc6829 100644 --- a/src/tasks/MobileBuildTasks/Apple/AppleProject.cs +++ b/src/tasks/MobileBuildTasks/Apple/AppleProject.cs @@ -82,23 +82,23 @@ private string BuildClangArgs(ClangBuildOptions buildOptions, string minOSVersio buildOptions.CompilerArguments.Add($"-arch {targetAbi}"); } - foreach(string compilerArg in buildOptions.CompilerArguments) + foreach (string compilerArg in buildOptions.CompilerArguments) { ret.Append(compilerArg); ret.Append(' '); } - foreach(string includeDir in buildOptions.IncludePaths) + foreach (string includeDir in buildOptions.IncludePaths) { ret.Append($"-I {includeDir} "); } - foreach(string linkerArg in buildOptions.LinkerArguments) + foreach (string linkerArg in buildOptions.LinkerArguments) { ret.Append($"{linkerArg} "); } - foreach(string source in buildOptions.Sources) + foreach (string source in buildOptions.Sources) { string ext = Path.GetExtension(source); @@ -115,7 +115,7 @@ private string BuildClangArgs(ClangBuildOptions buildOptions, string minOSVersio } HashSet libDirs = new HashSet(); - foreach(string lib in buildOptions.NativeLibraryPaths) + foreach (string lib in buildOptions.NativeLibraryPaths) { string rootPath = Path.GetDirectoryName(lib)!; string libName = Path.GetFileName(lib); diff --git a/src/tasks/MonoTargetsTasks/EmitBundleTask/EmitBundleBase.cs b/src/tasks/MonoTargetsTasks/EmitBundleTask/EmitBundleBase.cs index 40aac49edb70bf..c2c813c495d362 100644 --- a/src/tasks/MonoTargetsTasks/EmitBundleTask/EmitBundleBase.cs +++ b/src/tasks/MonoTargetsTasks/EmitBundleTask/EmitBundleBase.cs @@ -160,7 +160,7 @@ public override bool Execute() if (be9 is not null) allowedParallelism = be9.RequestCores(allowedParallelism); } - catch(NotImplementedException) + catch (NotImplementedException) { // RequestCores is not implemented in TaskHostFactory be9 = null; @@ -289,8 +289,8 @@ private static byte[] InitLookupTable() private string GatherUniqueExportedResourceDataSymbols(List uniqueDestinationFiles) { - StringBuilder resourceSymbols = new (); - HashSet resourcesAdded = new (); // Different Timezone resources may have the same contents + StringBuilder resourceSymbols = new(); + HashSet resourcesAdded = new(); // Different Timezone resources may have the same contents foreach (var uniqueDestinationFile in uniqueDestinationFiles) { string registeredName = uniqueDestinationFile.GetMetadata(RegisteredName); @@ -308,16 +308,16 @@ private string GatherUniqueExportedResourceDataSymbols(List uniqueDes private static void GenerateBundledResourcePreallocationAndRegistration(string resourceSymbols, string bundleRegistrationFunctionName, ICollection<(string resourceType, string registeredName, string resourceName, string resourceDataSymbol, string culture, string? resourceSymbolName)> files, StreamWriter outputUtf8Writer) { - List preallocatedSource = new (); + List preallocatedSource = new(); string assemblyTemplate = Utils.GetEmbeddedResource("mono-bundled-assembly.template"); string satelliteAssemblyTemplate = Utils.GetEmbeddedResource("mono-bundled-satellite-assembly.template"); string symbolDataTemplate = Utils.GetEmbeddedResource("mono-bundled-data.template"); var preallocatedResources = new StringBuilder(); - List preallocatedAssemblies = new (); - List preallocatedSatelliteAssemblies = new (); - List preallocatedData = new (); + List preallocatedAssemblies = new(); + List preallocatedSatelliteAssemblies = new(); + List preallocatedData = new(); int assembliesCount = 0; int satelliteAssembliesCount = 0; int dataCount = 0; @@ -377,7 +377,7 @@ private static void GenerateBundledResourcePreallocationAndRegistration(string r .Replace("%Len%", $"{resourceDataSymbol}_data_len_val")); } - List addPreallocatedResources = new (); + List addPreallocatedResources = new(); if (assembliesCount != 0) { preallocatedResources.AppendLine($"MonoBundledResource *{bundleRegistrationFunctionName}_assembly_resources[] = {{\n{string.Join(",\n", preallocatedAssemblies)}\n}};"); addPreallocatedResources.Add($" mono_bundled_resources_add ({bundleRegistrationFunctionName}_assembly_resources, {assembliesCount});"); diff --git a/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs b/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs index 288c7ef3711c8a..9976b737b56463 100644 --- a/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs +++ b/src/tasks/MonoTargetsTasks/ILStrip/ILStrip.cs @@ -80,7 +80,7 @@ public override bool Execute() if (be9 is not null) allowedParallelism = be9.RequestCores(allowedParallelism); } - catch(NotImplementedException) + catch (NotImplementedException) { // RequestCores is not implemented in TaskHostFactory be9 = null; diff --git a/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MarshalingPInvokeScanner.cs b/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MarshalingPInvokeScanner.cs index 31cb896cf68fa2..dd77ae97fcd135 100644 --- a/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MarshalingPInvokeScanner.cs +++ b/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MarshalingPInvokeScanner.cs @@ -82,7 +82,7 @@ private void ResolveInconclusiveTypes(HashSet incompatible, string assyP MetadataReader mdtReader = peReader.GetMetadataReader(); string assyName = mdtReader.GetString(mdtReader.GetAssemblyDefinition().Name); HashSet inconclusiveTypes = mmtcp.GetInconclusiveTypesForAssembly(assyName); - if(inconclusiveTypes.Count == 0) + if (inconclusiveTypes.Count == 0) return; SignatureDecoder decoder = new(mmtcp, mdtReader, null!); @@ -113,18 +113,18 @@ private bool IsAssemblyIncompatible(string assyPath, MinimalMarshalingTypeCompat MetadataReader mdtReader = peReader.GetMetadataReader(); - foreach(CustomAttributeHandle attrHandle in mdtReader.CustomAttributes) + foreach (CustomAttributeHandle attrHandle in mdtReader.CustomAttributes) { CustomAttribute attr = mdtReader.GetCustomAttribute(attrHandle); - if(attr.Constructor.Kind == HandleKind.MethodDefinition) + if (attr.Constructor.Kind == HandleKind.MethodDefinition) { MethodDefinitionHandle mdh = (MethodDefinitionHandle)attr.Constructor; MethodDefinition md = mdtReader.GetMethodDefinition(mdh); TypeDefinitionHandle tdh = md.GetDeclaringType(); TypeDefinition td = mdtReader.GetTypeDefinition(tdh); - if(mdtReader.GetString(td.Namespace) == "System.Runtime.CompilerServices" && + if (mdtReader.GetString(td.Namespace) == "System.Runtime.CompilerServices" && mdtReader.GetString(td.Name) == "DisableRuntimeMarshallingAttribute") return false; } @@ -136,17 +136,17 @@ private bool IsAssemblyIncompatible(string assyPath, MinimalMarshalingTypeCompat string ns = mdtReader.GetString(typeDef.Namespace); string name = mdtReader.GetString(typeDef.Name); - foreach(MethodDefinitionHandle mthDefHandle in typeDef.GetMethods()) + foreach (MethodDefinitionHandle mthDefHandle in typeDef.GetMethods()) { MethodDefinition mthDef = mdtReader.GetMethodDefinition(mthDefHandle); - if(!mthDef.Attributes.HasFlag(MethodAttributes.PinvokeImpl)) + if (!mthDef.Attributes.HasFlag(MethodAttributes.PinvokeImpl)) continue; BlobReader sgnBlobReader = mdtReader.GetBlobReader(mthDef.Signature); SignatureDecoder decoder = new(mmtcp, mdtReader, null!); MethodSignature sgn = decoder.DecodeMethodSignature(ref sgnBlobReader); - if(sgn.ReturnType == Compatibility.Incompatible || sgn.ParameterTypes.Any(p => p == Compatibility.Incompatible)) + if (sgn.ReturnType == Compatibility.Incompatible || sgn.ParameterTypes.Any(p => p == Compatibility.Incompatible)) { Log.LogMessage(MessageImportance.Low, string.Format("Assembly {0} requires marshal-ilgen for method {1}.{2}:{3} (first pass).", assyPath, ns, name, mdtReader.GetString(mthDef.Name))); diff --git a/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MinimalMarshalingTypeCompatibilityProvider.cs b/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MinimalMarshalingTypeCompatibilityProvider.cs index 8916c1d674c138..3e06e646c9805f 100644 --- a/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MinimalMarshalingTypeCompatibilityProvider.cs +++ b/src/tasks/MonoTargetsTasks/MarshalingPInvokeScanner/MinimalMarshalingTypeCompatibilityProvider.cs @@ -40,7 +40,7 @@ public void Add(string assyName, string namespaceName, string typeName) { HashSet? incAssyTypes; - if(!_data.TryGetValue(assyName, out incAssyTypes)) + if (!_data.TryGetValue(assyName, out incAssyTypes)) { incAssyTypes = new(); _data.Add(assyName, incAssyTypes); @@ -51,7 +51,7 @@ public void Add(string assyName, string namespaceName, string typeName) public HashSet EnumerateForAssembly(string assyName) { - if(_data.TryGetValue(assyName, out HashSet? incAssyTypes)) + if (_data.TryGetValue(assyName, out HashSet? incAssyTypes)) return incAssyTypes!; return new HashSet(); @@ -124,7 +124,7 @@ public Compatibility GetTypeFromDefinition(MetadataReader reader, TypeDefinition return GetTypeFromDefinition(reader, handleInner, rawTypeKind); } } - catch(BadImageFormatException ex) + catch (BadImageFormatException ex) { _log.LogMessage(MessageImportance.Low, ex.Message); } diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs index 52a06ed6f306e9..e93e77203ae30b 100644 --- a/src/tasks/WasmAppBuilder/EmccCompile.cs +++ b/src/tasks/WasmAppBuilder/EmccCompile.cs @@ -137,7 +137,7 @@ private bool ExecuteActual() if (be9 is not null) allowedParallelism = be9.RequestCores(allowedParallelism); } - catch(NotImplementedException) + catch (NotImplementedException) { // RequestCores is not implemented in TaskHostFactory be9 = null; diff --git a/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs b/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs index ad8a6a7df38745..4890b2b708d308 100644 --- a/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/InterpToNativeGenerator.cs @@ -54,7 +54,7 @@ static IEnumerable Args (string signature) } static (bool isVoid, string nativeType) Result (string signature) - => new (SignatureMapper.IsVoidSignature(signature), SignatureMapper.CharToNativeType(signature[0])); + => new(SignatureMapper.IsVoidSignature(signature), SignatureMapper.CharToNativeType(signature[0])); w.Write( """ diff --git a/src/tasks/WasmAppBuilder/PInvokeCollector.cs b/src/tasks/WasmAppBuilder/PInvokeCollector.cs index c8dcf571fad147..ad6bc414adc056 100644 --- a/src/tasks/WasmAppBuilder/PInvokeCollector.cs +++ b/src/tasks/WasmAppBuilder/PInvokeCollector.cs @@ -246,7 +246,7 @@ public PInvokeCallback(MethodInfo method) { if (attr.AttributeType.Name == "UnmanagedCallersOnlyAttribute") { - foreach(var arg in attr.NamedArguments) + foreach (var arg in attr.NamedArguments) { if (arg.MemberName == "EntryPoint") { diff --git a/src/tools/illink/.editorconfig b/src/tools/illink/.editorconfig index caefe7822fe779..22971f0d78c1e1 100644 --- a/src/tools/illink/.editorconfig +++ b/src/tools/illink/.editorconfig @@ -39,6 +39,9 @@ file_header_template = Copyright (c) .NET Foundation and contributors. All right ### Code Style Analyzers +# Spacing around keywords +dotnet_diagnostic.SA1000.severity = none + # Access modifier must be declared dotnet_diagnostic.SA1400.severity = none