diff --git a/.config/CredScanSuppressions.json b/.config/CredScanSuppressions.json index 2cd0c186716bc..2f274268f63fe 100644 --- a/.config/CredScanSuppressions.json +++ b/.config/CredScanSuppressions.json @@ -2,13 +2,7 @@ "tool": "Credential Scanner", "suppressions": [ { - "_justification": "Unit test containing connection strings under the test.", - "file": [ - "src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs" - ] - }, - { - "_justification": "Private key for testing purpose.", + "_justification": "Suppression approved. Private key for testing purpose.", "file": [ "src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs", "src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", @@ -19,58 +13,6 @@ "-----BEGIN PRIVATE KEY-----", "-----BEGIN * PRIVATE KEY-----" ] - }, - { - "_justification": "Test credential for Uri testing", - "file": [ - "src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs", - "src/libraries/System.Private.Uri/tests/ExtendedFunctionalTests/UriRelativeResolutionTest.cs", - "src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderRefreshTest.cs", - "src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs", - "src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs", - "src/libraries/System.Runtime/tests/System/Uri.CreateStringTests.cs" - ], - "placeholder": [ - "//*:;&$=123USERINFO@", - "//*:bar@", - "//*:bar1@", - "//*:password1@", - "//*:psw@", - "//*:userinfo2@" - ] - }, - { - "_justification": "Generic test password.", - "file": [ - "src/libraries/Common/tests/System/Net/Configuration.Certificates.cs", - "src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs", - "src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs", - "src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs", - "src/libraries/Common/tests/System/Net/Http/PostScenarioTest.cs", - "src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_certificates.ps1", - "src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs", - "src/libraries/System.Net.Http/tests/UnitTests/DigestAuthenticationTests.cs", - "src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs", - "src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs", - "src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs", - "src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs", - "src/libraries/System.Security.Cryptography.Csp/tests/PasswordDeriveBytesTests.cs", - "src/libraries/System.Security.Cryptography.Csp/tests/TrimmingTests/PasswordDeriveBytesTest.cs", - "src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs" - ], - "placeholder": [ - "\"anotherpassword\"", - "\"bar\"", - "\"mono\"", - "\"password1\"", - "\"rightpassword\"", - "\"testcertificate\"", - "\"unused\"", - "\"wrongpassword\"", - "\"PasswordGoesHere\"", - "\"FakePasswordsAreHard\"", - "\"tired\"" - ] } ] } diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d647127724425..8c48a20e744d1 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,13 +15,13 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.21154.2", + "version": "1.0.0-prerelease.21201.1", "commands": [ "xharness" ] }, "microsoft.visualstudio.slngen.tool": { - "version": "4.4.2", + "version": "5.0.5", "commands": [ "slngen" ] diff --git a/.dockerignore b/.dockerignore index 8bf5252822b1d..95e3c99537956 100644 --- a/.dockerignore +++ b/.dockerignore @@ -292,7 +292,7 @@ !**/src/pal/prebuilt/idl/*_i.c # Valid 'debug' folder, that contains CLR debugging code -!**/src/debug +!**/src/**/debug # Ignore folders created by the CLR test build **/TestWrappers_x64_[d|D]ebug diff --git a/.editorconfig b/.editorconfig index 05c161c83ec6e..827c77d4a8532 100644 --- a/.editorconfig +++ b/.editorconfig @@ -158,9 +158,10 @@ csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Analyzers -dotnet_code_quality.ca1802.api_surface = private, internal -dotnet_code_quality.ca1822.api_surface = private, internal -dotnet_code_quality.ca2208.api_surface = public +dotnet_code_quality.CA1052.api_surface = private, internal +dotnet_code_quality.CA1802.api_surface = private, internal +dotnet_code_quality.CA1822.api_surface = private, internal +dotnet_code_quality.CA2208.api_surface = public # License header file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license. diff --git a/.gitignore b/.gitignore index 6b660fb7ffd79..58f85ad15086c 100644 --- a/.gitignore +++ b/.gitignore @@ -294,11 +294,8 @@ parts/ prime/ stage/ -# CLR prebuilt generated files -!pal/prebuilt/idl/*_i.c - # Valid 'debug' folder, that contains CLR debugging code -!debug +!src/coreclr/debug # Ignore folders created by the CLR test build TestWrappers_x64_[d|D]ebug @@ -352,9 +349,6 @@ linker src/coreclr/System.Private.CoreLib/shared src/coreclr/System.Private.CoreLib/common -# The debug directory should not be ignored -!src/coreclr/debug - # Exceptions to the exclusions !src/coreclr/.nuget/_.pdb !src/coreclr/inc/obj/ diff --git a/Directory.Build.props b/Directory.Build.props index 5736fce3341c2..be3d42e18ba93 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -17,7 +17,7 @@ Solaris Linux windows - true + true @@ -60,6 +60,7 @@ $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'tools-local')) $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'src', 'tasks')) $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'ibc')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'mibc')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'docs')) $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.private.intellisense', '$(MicrosoftPrivateIntellisenseVersion)', 'IntellisenseFiles', 'net')) $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', 'docs')) @@ -70,6 +71,7 @@ $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmAppBuilder', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'WasmBuildTasks', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'MonoAOTCompiler', 'Debug', '$(NetCoreAppToolCurrent)')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'RuntimeConfigParser', 'Debug', '$(NetCoreAppToolCurrent)', 'publish')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', '$(NetCoreAppToolCurrent)', 'installer.tasks.dll')) $([MSBuild]::NormalizePath('$(ArtifactsBinDir)', 'installer.tasks', 'Debug', 'net461', 'installer.tasks.dll')) @@ -78,6 +80,7 @@ $([MSBuild]::NormalizePath('$(WasmAppBuilderDir)', 'WasmAppBuilder.dll')) $([MSBuild]::NormalizePath('$(WasmBuildTasksDir)', 'WasmBuildTasks.dll')) $([MSBuild]::NormalizePath('$(MonoAOTCompilerDir)', 'MonoAOTCompiler.dll')) + $([MSBuild]::NormalizePath('$(RuntimeConfigParserDir)', 'RuntimeConfigParser.dll')) @@ -121,7 +124,9 @@ <_portableOS Condition="'$(_runtimeOS)' == 'Browser'">browser <_portableOS Condition="'$(_runtimeOS)' == 'maccatalyst'">maccatalyst <_portableOS Condition="'$(_runtimeOS)' == 'ios'">ios + <_portableOS Condition="'$(_runtimeOS)' == 'iOSSimulator'">iossimulator <_portableOS Condition="'$(_runtimeOS)' == 'tvos'">tvos + <_portableOS Condition="'$(_runtimeOS)' == 'tvOSSimulator'">tvossimulator <_portableOS Condition="'$(_runtimeOS)' == 'android'">android <_runtimeOS Condition="$(_runtimeOS.StartsWith('tizen'))">linux @@ -148,7 +153,7 @@ <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'android' and $([MSBuild]::IsOSPlatform('OSX'))">osx-x64 - <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'maccatalyst' or '$(_runtimeOS)' == 'ios' or '$(_runtimeOS)' == 'tvos'">osx-x64 + <_toolRuntimeRID Condition="'$(_runtimeOS)' == 'maccatalyst' or '$(_runtimeOS)' == 'ios' or '$(_runtimeOS)' == 'iOSSimulator' or '$(_runtimeOS)' == 'tvos' or '$(_runtimeOS)' == 'tvOSSimulator'">osx-x64 $(_toolRuntimeRID) @@ -165,7 +170,9 @@ <_outputRID Condition="'$(TargetOS)' == 'Solaris'">solaris-$(TargetArchitecture) <_outputRID Condition="'$(TargetOS)' == 'MacCatalyst'">maccatalyst-$(TargetArchitecture) <_outputRID Condition="'$(TargetOS)' == 'iOS'">ios-$(TargetArchitecture) + <_outputRID Condition="'$(TargetOS)' == 'iOSSimulator'">iossimulator-$(TargetArchitecture) <_outputRID Condition="'$(TargetOS)' == 'tvOS'">tvos-$(TargetArchitecture) + <_outputRID Condition="'$(TargetOS)' == 'tvOSSimulator'">tvossimulator-$(TargetArchitecture) <_outputRID Condition="'$(TargetOS)' == 'Android'">android-$(TargetArchitecture) <_outputRID Condition="'$(TargetOS)' == 'Browser'">browser-$(TargetArchitecture) @@ -181,10 +188,10 @@ true true true - true - true - true - true + true + true + true + true true true true @@ -220,10 +227,8 @@ runtime https://github.com/dotnet/$(GitHubRepositoryName) https://dot.net - - $(PackageProjectUrl) microsoft,dotnetframework - $([MSBuild]::NormalizePath('$(LibrariesProjectRoot)', 'Microsoft.NETCore.Platforms', 'pkg', 'runtime.json')) + $([MSBuild]::NormalizePath('$(LibrariesProjectRoot)', 'Microsoft.NETCore.Platforms', 'src', 'runtime.json')) $(MSBuildThisFileDirectory)LICENSE.TXT MIT $(CopyrightNetFoundation) diff --git a/README.md b/README.md index 861ff943990df..1444df567a8e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Runtime - [![Build Status](https://dnceng.visualstudio.com/public/_apis/build/status/dotnet/runtime/runtime?branchName=main)](https://dnceng.visualstudio.com/public/_build/latest?definitionId=686&branchName=main) +[![Help Wanted](https://img.shields.io/github/issues/dotnet/runtime/up-for-grabs?style=flat-square&color=%232EA043&label=help%20wanted)](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3A%22up-for-grabs%22) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dotnet/runtime) [![Discord](https://img.shields.io/discord/732297728826277939?style=flat-square&label=Discord&logo=discord&logoColor=white&color=7289DA)](https://aka.ms/dotnet-discord) diff --git a/docs/area-owners.md b/docs/area-owners.md index ced5b5a1c31b1..c84d23408dd8d 100644 --- a/docs/area-owners.md +++ b/docs/area-owners.md @@ -8,7 +8,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | Area | Lead | Owners (area experts to tag in PR's and issues) | Notes | |------------------------------------------------|---------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| area-AssemblyLoader-coreclr | @agocke | @agocke @vitek-karas | | +| area-AssemblyLoader-coreclr | @agocke | @agocke @vitek-karas @vsadov | | | area-AssemblyLoader-mono | @SamMonoRT | @CoffeeFlux | | | area-Build-mono | @steveisok | @akoeplinger | | | area-Codegen-AOT-mono | @SamMonoRT | @vargaz | | @@ -35,7 +35,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-Extensions-Primitives | @ericstj | @maryamariyan @michaelgsharp @safern @tarekgh | Consultants: @eerhardt | | area-GC-coreclr | @mangod9 | @Maoni0 | | | area-GC-mono | @SamMonoRT | @BrzVlad | | -| area-Host | @agocke | @jeffschwMSFT @vitek-karas | Issues with dotnet.exe including bootstrapping, framework detection, hostfxr.dll and hostpolicy.dll | +| area-Host | @agocke | @jeffschwMSFT @vitek-karas @vsadov | Issues with dotnet.exe including bootstrapping, framework detection, hostfxr.dll and hostpolicy.dll | | area-HostModel | @agocke | @vitek-karas | | | area-ILTools-coreclr | @JulieLeeMSFT | @BruceForstall @dotnet/jit-contrib | | | area-ILVerification | @JulieLeeMSFT | @BruceForstall @dotnet/jit-contrib | | @@ -57,7 +57,7 @@ Note: Editing this file doesn't update the mapping used by the `@msftbot` issue | area-ReadyToRun-coreclr | @mangod9 | @nattress | | | area-Serialization | @HongGit | @StephenMolloy @HongGit | Packages: Excluded: | | area-Setup | @dleeapho | @NikolaMilosavljevic @dleeapho | Distro-specific (Linux, Mac and Windows) setup packages and msi files | -| area-Single-File | @agocke | @vitek-karas | | +| area-Single-File | @agocke | @vitek-karas @vsadov | | | area-Snap | @dleeapho | @dleeapho @leecow @MichaelSimons | | | area-System.Buffers | @jeffhandley | @pgovind @tannergooding | Consultants: @GrabYourPitchforks | | area-System.CodeDom | @jeffhandley | @buyaa-n @joperezr @krwq | | diff --git a/docs/coding-guidelines/coding-style.md b/docs/coding-guidelines/coding-style.md index 38cc58c620aa7..856ce8f5bb4ea 100644 --- a/docs/coding-guidelines/coding-style.md +++ b/docs/coding-guidelines/coding-style.md @@ -23,7 +23,7 @@ The general rule we follow is "use Visual Studio defaults". Consider enabling "View White Space (Ctrl+R, Ctrl+W)" or "Edit -> Advanced -> View White Space" if using Visual Studio to aid detection. 9. If a file happens to differ in style from these guidelines (e.g. private members are named `m_member` rather than `_member`), the existing style in that file takes precedence. -10. We only use `var` when it's obvious what the variable type is (e.g. `var stream = new FileStream(...)` not `var stream = OpenStandardInput()`). +10. We only use `var` when the type is explicitly named on the right-hand side, typically due to either `new` or an explicit cast, e.g. `var stream = new FileStream(...)` not `var stream = OpenStandardInput()`. 11. We use language keywords instead of BCL types (e.g. `int, string, float` instead of `Int32, String, Single`, etc) for both type references as well as method calls (e.g. `int.Parse` instead of `Int32.Parse`). See issue [#13976](https://github.com/dotnet/runtime/issues/13976) for examples. 12. We use PascalCasing to name all our constant local variables and fields. The only exception is for interop code where the constant value should exactly match the name and value of the code you are calling via interop. 13. We use PascalCasing for all method names, including local functions. @@ -35,6 +35,7 @@ The general rule we follow is "use Visual Studio defaults". - Never use single-line form (for example: `if (source == null) throw new ArgumentNullException("source");`) - Using braces is always accepted, and required if any block of an `if`/`else if`/.../`else` compound statement uses braces or if a single statement body spans multiple lines. - Braces may be omitted only if the body of *every* block associated with an `if`/`else if`/.../`else` compound statement is placed on a single line. +19. Make all internal and private types static or sealed unless derivation from them is required. As with any implementation detail, they can be changed if/when derivation is required in the future. An [EditorConfig](https://editorconfig.org "EditorConfig homepage") file (`.editorconfig`) has been provided at the root of the runtime repository, enabling C# auto-formatting conforming to the above guidelines. diff --git a/docs/coding-guidelines/project-guidelines.md b/docs/coding-guidelines/project-guidelines.md index 556c9ac6d9ae7..c0a79248d01c5 100644 --- a/docs/coding-guidelines/project-guidelines.md +++ b/docs/coding-guidelines/project-guidelines.md @@ -142,6 +142,9 @@ All libraries should use `` for all their references Other target frameworks than .NETCoreApp latest (i.e. `netstandard2.0`, `net461`, `netcoreapp3.0`) should use ProjectReference items to reference dependencies. +### src\ILLink +Contains the files used to direct the trimming tool. See [ILLink files](../workflow/trimming/ILLink-files.md). + ### src output All src outputs are under diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 665e532c6d216..f8774aa2cb9c0 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -5,6 +5,7 @@ Revisions: * 1.1 - [Jan Kotas](https://github.com/jkotas) - 2015 * 3.1 - [Tomas Rylek](https://github.com/trylek) - 2019 * 4.1 - [Tomas Rylek](https://github.com/trylek) - 2020 +* 5.3 - [Tomas Rylek](https://github.com/trylek) - 2021 # Introduction @@ -161,6 +162,8 @@ The following section types are defined and described later in this document: | InliningInfo2 | 114 | Image (added in V4.1) | ComponentAssemblies | 115 | Image (added in V4.1) | OwnerCompositeExecutable | 116 | Image (added in V4.1) +| PgoInstrumentationData | 117 | Image (added in V5.2) +| ManifestAssemblyMvids | 118 | Image (added in V5.3) ## ReadyToRunSectionType.CompilerIdentifier @@ -540,6 +543,17 @@ the `OwnerCompositeExecutable` section that contains a UTF-8 string encoding the composite R2R executable this MSIL belongs to with extension (without path). Runtime uses this information to locate the composite R2R executable with the compiled native code when loading the MSIL. +## ReadyToRunSectionType.PgoInstrumentationData (v5.2+) + +**TODO**: document PGO instrumentation data + +## ReadyToRunSectionType.ManifestAssemblyMvids (v5.3+) + +This section is a binary array of 16-byte MVID records, one for each assembly in the manifest metadata. +Number of assemblies stored in the manifest metadata is equal to the number of MVID records in the array. +MVID records are used at runtime to verify that the assemblies loaded match those referenced by the +manifest metadata representing the versioning bubble. + # Native Format Native format is set of encoding patterns that allow persisting type system data in a binary format that is diff --git a/docs/design/features/default-interface-methods.md b/docs/design/features/default-interface-methods.md index 0e9e761ef1095..f5e2de0dfcd14 100644 --- a/docs/design/features/default-interface-methods.md +++ b/docs/design/features/default-interface-methods.md @@ -1,53 +1 @@ -# Default interface methods - -Default interface methods is a runtime feature designed to support the [default interface methods](https://github.com/dotnet/csharplang/blob/21dc9561aeffc87a31da44588ce7ed6930ee3333/proposals/default-interface-methods.md) C# language feature posed for C# 8.0. - -The major changes are: - -* Interfaces are now allowed to have instance methods (both virtual and non-virtual). Previously we only allowed abstract virtual methods. - * Interfaces obviously still can't have instance fields. -* Interface methods are allowed to MethodImpl other interface methods the interface _requires_ (but we require the `MethodImpl`s to be final to keep things simple) - i.e. an interface is allowed to provide (or override) an implementation of another interface's method - -This speclet attempts to formalize the major places within the [ECMA-335 specification](https://www.ecma-international.org/publications/standards/Ecma-335.htm) that would need updating, should we rev the spec in the future. - -It doesn't attempt to be an exhaustive list - there are many places within the spec that mention interfaces being just contracts that don't define implementation. This list should be complete enough to list places where interesting _implementation differences_ happen. - -## Changes to the ECMA-335 specification - -**Section** "I.8.5.3.2 Accessibility of members and nested types" is extended so that the definition of "referents that support the same type" includes "an exact type and all of the types that inherit from it, or implement it as an interface (either explicitly or implicitly)". - -Examples: -`class Base : IFoo {}` / `class Derived : Base {}`: `Base` can access protected members of `IFoo`. `Derived` can also access protected members of `IFoo` because it inherits the interface. -`class Outer : IFoo { class Nested { } }`: `Nested` can access protected members of `IFoo`. -`interface IBar : IFoo { }`: `IBar` can access protected members of `IFoo` (same rules as for classes) - -TODO: since we now allow protected/internal members on interfaces, do we need to adjust the existing interface method resolution algorithm to do accessibility checks (can a method in a class that can't access the interface method implement the method)? CoreCLR seems to let us do things like override internal methods from a different assembly so this doesn't seem to be enforced for classes either. - -**Section** "I.8.9.5 Class type definition" [Note: the section on type initializers within the spec only seems to apply to object types and value types, not to interfaces, but the CLR has historically supported running .cctors when accessing static members of interfaces and the spec does mention interface type initializers as well. We might want to move the part about type initializers out of the section. End note.] The semantics of when and what triggers the execution of type initialization methods will be updated so that we support the strict semantic of type initializers when executing instance methods on interfaces (strict semantic currently only covers accessing static methods on interfaces): -Bullet 4 "If not marked BeforeFieldInit", item "c" is amended to include instance methods on interfaces, in addition to the existing value types. - -**Section** "II.12 Semantics of interfaces" is extended to allow instance methods on interfaces. - -**Section** "II.12.1 Implementing interfaces" is extended to say all virtual instance methods defined on an interface must be abstract, be marked with newslot and not have an associated MethodImpl which uses the method as its Impl, or final without newslot and with a MethodImpl that uses the method as its Impl entry. - -**Section** "II.12.2 Implementing virtual methods on interfaces" is extended by an additional mechanism to provide interface method implementation - through inheritance of an existing implementation from an implemented interface. - -[The general gist of the implementation is that default interface methods (either the slot defining method, or a MethodImpl for the interface method on another interface type) is always used as a fallback - only if the "old rules" didn't find an implementation, we apply the new rules and try to find an implementation on one of the interfaces.] -The algorithm is amended as follows: -* The existing algorithm to build interface table on the open type is left intact up to the last step "If the current class is not abstract and there are any interface methods that still have empty slots (i.e. slots with empty lists) for this class and all classes in its inheritance chain, then the program is invalid.". This is amended to become "If the current class is not abstract and there are any interface methods that still have empty slots (i.e. slots with empty lists) and the slot defining method is not abstract and there is no MethodImpl for the slot within the interfaces of the type's implicit or explicit interfaces, then the program is invalid." [Note: the default interface method resolution is disconnected from the interface table on the open type, as defined by the spec. The purpose of this change is not to fail loading at this stage.] -* The runtime resolution algorithm "When an interface method is invoked" is amended: - * The original step 4 is moved after the following steps: - * Create an empty list of candidate implementations of the interface method. - * If the interface method itself is not abstract, add it to the list. - * Apply all MethodImpls specified in the list of interfaces implicitly implemented by the runtime class of the instance through which the interface method is invoked and add the methods to the list. - * Go over the owning types of each of the candidate methods in the list. If the owning type is less concrete than some other type in the list (there is another method in the list whose owning type requires the less concrete type), remove it from the list. - * If there's more than one method in the list, throw AmbiguousImplementationException - * If there's exactly one method in the list and the method is not abstract, call that method - * If there's exactly one method in the list but the method is abstract, throw `EntryPointNotFoundException`. - * If there's no method in the list and the interface is variant, repeat the above algorithm, looking for a variant match. Return the first variant match provided by a most specific interface. - -**Section** "III.2.1 constrained. prefix" the paragraph starting with "This last case can only occur when method was defined on `System.Object`, `System.ValueType`, or `System.Enum`" is extended to also cover default interface method implementation. In the case the interface method implementation is provided by an interface, the implicit boxing becomes _observable_ to the program. - -**Section** "III.4.2 callvirt" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract. - -**Section** "III.4.18 ldvirtftn" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract. +The details previously held in this document have been moved to [Ecma-335-Augments](../specs/Ecma-335-Augments.md) diff --git a/docs/design/features/dotnet-pgo.md b/docs/design/features/dotnet-pgo.md index 04774cd58cde5..6eac92a7c3283 100644 --- a/docs/design/features/dotnet-pgo.md +++ b/docs/design/features/dotnet-pgo.md @@ -44,7 +44,7 @@ set COMPLUS_TC_QuickJitForLoops= set COMPLUS_TC_CallCountThreshold= set COMPLUS_ReadyToRun= -dotnet-pgo --trace trace.nettrace --output trace.mibc +dotnet-pgo create-mibc --trace trace.nettrace --output trace.mibc dotnet publish --runtime win-x64 -p:PublishReadyToRun=true -p:ReadyToRunOptimizationData=trace.mibc ``` diff --git a/docs/design/specs/Ecma-335-Augments.md b/docs/design/specs/Ecma-335-Augments.md index df4e429912a11..3ba4d98e7fe38 100644 --- a/docs/design/specs/Ecma-335-Augments.md +++ b/docs/design/specs/Ecma-335-Augments.md @@ -366,3 +366,57 @@ In addition to the guarantees that apply to all type initializers, the CLI shall 2. A module initializer shall run exactly once for any given module unless explicitly called by user code. 3. No method other than those called directly or indirectly from the module initializer will be able to access the types, methods, or data in a module before its initializer completes execution. + +## Default Interface Methods + +We propose to allow default implementations of interface methods. + +Default interface methods is a runtime feature designed to support the [default interface methods](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-8.0/default-interface-methods.md) C# 8.0 language feature. + +The major changes are: + +* Interfaces are now allowed to have instance methods (both virtual and non-virtual). Previously we only allowed abstract virtual methods. + * Interfaces obviously still can't have instance fields. +* Interface methods are allowed to MethodImpl other interface methods the interface _requires_ (but we require the `MethodImpl`s to be final to keep things simple) - i.e. an interface is allowed to provide (or override) an implementation of another interface's method + +This list of changes to the specification doesn't attempt to be an exhaustive list - there are many places within the spec that mention interfaces being just contracts that don't define implementation. This list should be complete enough to list places where interesting _implementation differences_ happen. + +#### Proposed specification change + +**Section** "I.8.5.3.2 Accessibility of members and nested types" is extended so that the definition of "referents that support the same type" includes "an exact type and all of the types that inherit from it, or implement it as an interface (either explicitly or implicitly)". + +Examples: +`class Base : IFoo {}` / `class Derived : Base {}`: `Base` can access protected members of `IFoo`. `Derived` can also access protected members of `IFoo` because it inherits the interface. +`class Outer : IFoo { class Nested { } }`: `Nested` can access protected members of `IFoo`. +`interface IBar : IFoo { }`: `IBar` can access protected members of `IFoo` (same rules as for classes) + +TODO: since we now allow protected/internal members on interfaces, do we need to adjust the existing interface method resolution algorithm to do accessibility checks (can a method in a class that can't access the interface method implement the method)? CoreCLR seems to let us do things like override internal methods from a different assembly so this doesn't seem to be enforced for classes either. + +**Section** "I.8.9.5 Class type definition" [Note: the section on type initializers within the spec only seems to apply to object types and value types, not to interfaces, but the CLR has historically supported running .cctors when accessing static members of interfaces and the spec does mention interface type initializers as well. We might want to move the part about type initializers out of the section. End note.] The semantics of when and what triggers the execution of type initialization methods will be updated so that we support the strict semantic of type initializers when executing instance methods on interfaces (strict semantic currently only covers accessing static methods on interfaces): +Bullet 4 "If not marked BeforeFieldInit", item "c" is amended to include instance methods on interfaces, in addition to the existing value types. + +**Section** "II.12 Semantics of interfaces" is extended to allow instance methods on interfaces. + +**Section** "II.12.1 Implementing interfaces" is extended to say all virtual instance methods defined on an interface must be abstract, be marked with newslot and not have an associated MethodImpl which uses the method as its Impl, or final without newslot and with a MethodImpl that uses the method as its Impl entry. + +**Section** "II.12.2 Implementing virtual methods on interfaces" is extended by an additional mechanism to provide interface method implementation - through inheritance of an existing implementation from an implemented interface. + +[The general gist of the implementation is that default interface methods (either the slot defining method, or a MethodImpl for the interface method on another interface type) is always used as a fallback - only if the "old rules" didn't find an implementation, we apply the new rules and try to find an implementation on one of the interfaces.] +The algorithm is amended as follows: +* The existing algorithm to build interface table on the open type is left intact up to the last step "If the current class is not abstract and there are any interface methods that still have empty slots (i.e. slots with empty lists) for this class and all classes in its inheritance chain, then the program is invalid.". This is amended to become "If the current class is not abstract and there are any interface methods that still have empty slots (i.e. slots with empty lists) and the slot defining method is not abstract and there is no MethodImpl for the slot within the interfaces of the type's implicit or explicit interfaces, then the program is invalid." [Note: the default interface method resolution is disconnected from the interface table on the open type, as defined by the spec. The purpose of this change is not to fail loading at this stage.] +* The runtime resolution algorithm "When an interface method is invoked" is amended: + * The original step 4 is moved after the following steps: + * Create an empty list of candidate implementations of the interface method. + * If the interface method itself is not abstract, add it to the list. + * Apply all MethodImpls specified in the list of interfaces implicitly implemented by the runtime class of the instance through which the interface method is invoked and add the methods to the list. + * Go over the owning types of each of the candidate methods in the list. If the owning type is less concrete than some other type in the list (there is another method in the list whose owning type requires the less concrete type), remove it from the list. + * If there's more than one method in the list, throw AmbiguousImplementationException + * If there's exactly one method in the list and the method is not abstract, call that method + * If there's exactly one method in the list but the method is abstract, throw `EntryPointNotFoundException`. + * If there's no method in the list and the interface is variant, repeat the above algorithm, looking for a variant match. Return the first variant match provided by a most specific interface. + +**Section** "III.2.1 constrained. prefix" the paragraph starting with "This last case can only occur when method was defined on `System.Object`, `System.ValueType`, or `System.Enum`" is extended to also cover default interface method implementation. In the case the interface method implementation is provided by an interface, the implicit boxing becomes _observable_ to the program. + +**Section** "III.4.2 callvirt" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract. + +**Section** "III.4.18 ldvirtftn" is extended to allow throwing `AmbiguousImplementationException` if the implementation of the interface method resolves at runtime to more than one default interface method. It's also extended to specify throwing `EntryPointNotFoundException` if the default interface implementation is abstract. diff --git a/docs/infra/buildtriage.md b/docs/infra/buildtriage.md index 53b51bb8affba..6ae47e20f953b 100644 --- a/docs/infra/buildtriage.md +++ b/docs/infra/buildtriage.md @@ -16,31 +16,13 @@ Please make sure that you are part of the following groups before you start the Unfortunately, the teams channel's members need to be listed individually. Ping @ViktorHofer if you need access. -## Tracking Build Failures -All the CI failures can be tracked through the CI Council dashboards i.e. [Public](https://dev.azure.com/dnceng/public/_dashboards/dashboard/40ac4990-3498-4b3a-85dd-2ffde961d672), [Internal](https://dev.azure.com/dnceng/internal/_dashboards/dashboard/e1bb572d-a2b0-488f-a58a-54c73a547f0d). -We have different dashboards for public (Rolling & PR Builds) and internal builds. - -In addition to the dashboards, official build failure notifications are sent to the internal runtime infrastructure email alias. - -For each of these mail notifications, a matching issue should exist (either in the dotnet/runtime repository or in dotnet/core-eng or dotnet/arcade). The person triaging build failures should reply to the email with a link to the issue to let everyone know it is triaged. This guarantees that we are following-up on infrastructure issues immediately. If a build failure's cause isn't trivial to identify, consider looping in dnceng. - -Tests are not run during the internal builds. Publishing and signing steps are run only during internal builds. Rolling builds run tests for the full matrix. - -Any consistently failing test where the fix is not in pipeline should be promptly disabled on the CI. Don't leave tests failing in CI while you investigate; it's too disruptive for the rest of the team. - -For new issues, try to provide a [runfo](https://runfo.azurewebsites.net/) search which will make it easy to isolate repeated instances of that failure. - -Contact @chcosta if you are having any trouble accessing the dashboards. -Contact @Chrisboh if you don't have the calendar invite for the CI Council meeting. -Contact @jaredpar if you are having any trouble with runfo, site or utility. - -## Ongoing Issues - -All the issues causing the builds to fail should be marked with [`blocking-clean-ci`](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3Ablocking-clean-ci) label. -Any issues causing build breaks in the official build should be marked with [`blocking-clean-official`](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3Ablocking-clean-official). -It helps in tracking issues effectively. - -The main meta-bug linking to currently tracked issues is [here](https://github.com/dotnet/runtime/issues/702). +## Responsibilities +- Official build failure notifications are sent to the runtime infrastructure mail alias. For each of these notifications, a matching issue should exist (either in the dotnet/runtime repository or in dotnet/core-eng or dotnet/arcade). The person triaging build failures should reply to the email with a link to the issue to let everyone know it is triaged. This guarantees that we are following-up on infrastructure issues immediately. If a build failure's cause isn't trivial to identify, consider looping in dnceng. +- The CI Council dashboards should be used as well to track CI failures, i.e. [Public](https://dev.azure.com/dnceng/public/_dashboards/dashboard/40ac4990-3498-4b3a-85dd-2ffde961d672), [Internal](https://dev.azure.com/dnceng/internal/_dashboards/dashboard/e1bb572d-a2b0-488f-a58a-54c73a547f0d). There are different dashboards for public (batched rolling) and internal builds. Tests are not run as part of internal builds whereas publishing and signing steps are exclusively run as part of internal builds. Public rolling builds run tests for the full matrix of supported configurations. +- Any consistently failing test where the fix is not in pipeline should be promptly disabled on the CI. Don't leave tests failing in CI while you investigate; it's too disruptive for the rest of the team! +- All the issues causing the builds to fail should be marked with [`blocking-clean-ci`](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3Ablocking-clean-ci) label. Any issues causing build breaks in the official build should be marked with [`blocking-clean-official`](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3Ablocking-clean-official). For new issues, try to provide a [runfo](https://runfo.azurewebsites.net/) search which will make it easy to isolate repeated instances of that failure. It helps in tracking issues effectively. +- Issues listed in the [Infrastructure - Status/Health](https://github.com/dotnet/runtime/issues/702) meta-issue (which is automatically updated based on the blocking-clean-* labels) should be triaged at least once per week to make sure that it only lists issues which are currently affecting CI. Triaging refers to either closing the issue if it doesn't apply anymore or removing the blocking-clean-* label in case tests are disabled. +- Attend the weekly CI Council meeting and represent the dotnet/runtime repository. ## Some helpful resources - [runfo Website](https://runfo.azurewebsites.net/) @@ -50,14 +32,6 @@ The main meta-bug linking to currently tracked issues is [here](https://github.c - [Public Build Definition](https://dev.azure.com/dnceng/public/_build?definitionId=686) - [Runtime dependency status](https://maestro-prod.westus2.cloudapp.azure.com/1296/https:%2F%2Fgithub.com%2Fdotnet%2Fruntime/latest/graph) -## Build Rotation for upcoming months - -| Month | Alias | -|-------|-----------| -| September 2020 | @directhex | -| October 2020 | @jkoritzinsky | -| November 2020 | @aik-jahoda | -| December 2020 | @akoeplinger | -| January 2021 | @hoyosjs | -| February 2021 | @anipik | -| March 2021 | @directhex | +Contact @chcosta if you are having any trouble accessing the dashboards. +Contact @Chrisboh if you don't have the calendar invite for the CI Council meeting. +Contact @jaredpar if you are having any trouble with runfo, site or utility. diff --git a/docs/project/dogfooding.md b/docs/project/dogfooding.md index ed3e2f741cb0a..c01466b2f42ec 100644 --- a/docs/project/dogfooding.md +++ b/docs/project/dogfooding.md @@ -27,7 +27,7 @@ To use nightly builds of the entire runtime, follow the steps given in the rest ## Install prerequisites -1. Acquire the latest nightly .NET SDK by downloading and extracting a zip/tarball or using an installer from the [installers and binaries table in dotnet/installer](https://github.com/dotnet/installer#installers-and-binaries) (for example, https://aka.ms/dotnet/net6/dev/Sdk/dotnet-sdk-win-x64.zip). +1. Acquire the latest nightly .NET SDK by downloading and extracting a zip/tarball or using an installer from the [installers and binaries table in dotnet/installer](https://github.com/dotnet/installer#installers-and-binaries) (for example, https://aka.ms/dotnet/6.0/daily/dotnet-sdk-win-x64.zip). 2. By default, the dotnet CLI will use the globally installed SDK if it matches the major/minor version you request and has a higher revision. To force it to use a locally installed SDK, you must set an environment variable `DOTNET_MULTILEVEL_LOOKUP=0` in your shell. You can use `dotnet --info` to verify what version of the Shared Framework it is using. @@ -161,23 +161,23 @@ $ bin\Debug\net6.0\win-x64\publish\App.exe | Platform | Main | | --- | :---: | -| **Windows (x64)** | [![][win-x64-badge-6.0.X]][win-x64-version-6.0.X]
[Installer][win-x64-installer-6.0.X] ([Checksum][win-x64-installer-checksum-6.0.X])
[zip][win-x64-zip-6.0.X] ([Checksum][win-x64-zip-checksum-6.0.X])
[NetHost (zip)][win-x64-nethost-zip-6.0.X]
[Symbols (zip)][win-x64-symbols-zip-6.0.X] | -| **Windows (x86)** | [![][win-x86-badge-6.0.X]][win-x86-version-6.0.X]
[Installer][win-x86-installer-6.0.X] ([Checksum][win-x86-installer-checksum-6.0.X])
[zip][win-x86-zip-6.0.X] ([Checksum][win-x86-zip-checksum-6.0.X])
[NetHost (zip)][win-x86-nethost-zip-6.0.X]
[Symbols (zip)][win-x86-symbols-zip-6.0.X] | -| **Windows (arm64)** | [![][win-arm64-badge-6.0.X]][win-arm64-version-6.0.X]
[Installer][win-arm64-installer-6.0.X] ([Checksum][win-arm64-installer-checksum-6.0.X])
[zip][win-arm64-zip-6.0.X] ([Checksum][win-arm64-zip-checksum-6.0.X])
[NetHost (zip)][win-arm64-nethost-zip-6.0.X]
[Symbols (zip)][win-arm64-symbols-zip-6.0.X] | -| **macOS (x64)** | [![][osx-x64-badge-6.0.X]][osx-x64-version-6.0.X]
[Installer][osx-x64-installer-6.0.X] ([Checksum][osx-x64-installer-checksum-6.0.X])
[tar.gz][osx-x64-targz-6.0.X] ([Checksum][osx-x64-targz-checksum-6.0.X])
[NetHost (tar.gz)][osx-x64-nethost-targz-6.0.X]
[Symbols (tar.gz)][osx-x64-symbols-targz-6.0.X] | -| **macOS (arm64)** | [![][osx-arm64-badge-6.0.X]][osx-arm64-version-6.0.X]
[Installer][osx-arm64-installer-6.0.X] ([Checksum][osx-arm64-installer-checksum-6.0.X])
[tar.gz][osx-arm64-targz-6.0.X] ([Checksum][osx-arm64-targz-checksum-6.0.X])
[NetHost (tar.gz)][osx-arm64-nethost-targz-6.0.X]
[Symbols (tar.gz)][osx-arm64-symbols-targz-6.0.X] | -| **Linux (x64)** (for glibc based OS) | [![][linux-x64-badge-6.0.X]][linux-x64-version-6.0.X]
[tar.gz][linux-x64-targz-6.0.X] ([Checksum][linux-x64-targz-checksum-6.0.X])
[NetHost (tar.gz)][linux-x64-nethost-targz-6.0.X]
[Symbols (tar.gz)][linux-x64-symbols-targz-6.0.X] | -| **Linux (armhf)** (for glibc based OS) | [![][linux-arm-badge-6.0.X]][linux-arm-version-6.0.X]
[tar.gz][linux-arm-targz-6.0.X] ([Checksum][linux-arm-targz-checksum-6.0.X])
[NetHost (tar.gz)][linux-arm-nethost-targz-6.0.X]
[Symbols (tar.gz)][linux-arm-symbols-targz-6.0.X] | -| **Linux (arm64)** (for glibc based OS) | [![][linux-arm64-badge-6.0.X]][linux-arm64-version-6.0.X]
[tar.gz][linux-arm64-targz-6.0.X] ([Checksum][linux-arm64-targz-checksum-6.0.X])
[NetHost (tar.gz)][linux-arm64-nethost-targz-6.0.X]
[Symbols (tar.gz)][linux-arm64-symbols-targz-6.0.X] | -| **Linux-musl (x64)** | [![][linux-musl-x64-badge-6.0.X]][linux-musl-x64-version-6.0.X]
[tar.gz][linux-musl-x64-targz-6.0.X] ([Checksum][linux-musl-x64-targz-checksum-6.0.X])
[NetHost (tar.gz)][linux-musl-x64-nethost-targz-6.0.X]
[Symbols (tar.gz)][linux-musl-x64-symbols-targz-6.0.X] | -| **Linux-musl (arm)** | [![][linux-musl-arm-badge-6.0.X]][linux-musl-arm-version-6.0.X]
[tar.gz][linux-musl-arm-targz-6.0.X] ([Checksum][linux-musl-arm-targz-checksum-6.0.X])
[NetHost (tar.gz)][linux-musl-arm-nethost-targz-6.0.X]
[Symbols (tar.gz)][linux-musl-arm-symbols-targz-6.0.X] | -| **Linux-musl (arm64)** | [![][linux-musl-arm64-badge-6.0.X]][linux-musl-arm64-version-6.0.X]
[tar.gz][linux-musl-arm64-targz-6.0.X] ([Checksum][linux-musl-arm64-targz-checksum-6.0.X])
[NetHost (tar.gz)][linux-musl-arm64-nethost-targz-6.0.X]
[Symbols (tar.gz)][linux-musl-arm64-symbols-targz-6.0.X] | -| **Dpkg Based Systems (x64)** | [![][deb-badge-6.0.X]][deb-version-6.0.X]
[Runtime-Deps][deb-runtime-deps-6.0.X] ([Checksum][deb-runtime-deps-checksum-6.0.X])
[Host][deb-host-6.0.X] ([Checksum][deb-host-checksum-6.0.X])
[App Hosts][deb-apphost-pack-6.0.X] ([Checksum][deb-apphost-pack-checksum-6.0.X])
[Host FX Resolver][deb-hostfxr-6.0.X] ([Checksum][deb-hostfxr-checksum-6.0.X])
[Targeting Pack][deb-targeting-pack-6.0.X] ([Checksum][deb-targeting-pack-checksum-6.0.X])
[Shared Framework][deb-sharedfx-6.0.X] ([Checksum][deb-sharedfx-checksum-6.0.X]) | -| **CentOS 7 (x64)** | [![][centos-7-badge-6.0.X]][centos-7-version-6.0.X]
[Runtime-Deps][centos-7-runtime-deps-6.0.X] ([Checksum][centos-7-runtime-deps-checksum-6.0.X])
[Host][centos-7-host-6.0.X] ([Checksum][centos-7-host-checksum-6.0.X])
[App Hosts][centos-7-apphost-pack-6.0.X] ([Checksum][centos-7-apphost-pack-checksum-6.0.X])
[Host FX Resolver][centos-7-hostfxr-6.0.X] ([Checksum][centos-7-hostfxr-checksum-6.0.X])
[Targeting Pack][centos-7-targeting-pack-6.0.X] ([Checksum][centos-7-targeting-pack-checksum-6.0.X])
[Shared Framework][centos-7-sharedfx-6.0.X] ([Checksum][centos-7-sharedfx-checksum-6.0.X]) | -| **RHEL 7.2 (x64)** | [![][rhel7-badge-6.0.X]][rhel7-version-6.0.X]
[Host][rhel7-host-6.0.X] ([Checksum][rhel7-host-checksum-6.0.X])
[App Hosts][rhel7-apphost-pack-6.0.X] ([Checksum][rhel7-apphost-pack-checksum-6.0.X])
[Host FX Resolver][rhel7-hostfxr-6.0.X] ([Checksum][rhel7-hostfxr-checksum-6.0.X])
[Targeting Pack][rhel7-targeting-pack-6.0.X] ([Checksum][rhel7-targeting-pack-checksum-6.0.X])
[Shared Framework][rhel7-sharedfx-6.0.X] ([Checksum][rhel7-sharedfx-checksum-6.0.X]) | -| **Fedora 27 (x64)** | [![][fedora-27-badge-6.0.X]][fedora-27-version-6.0.X]
[Runtime-Deps][fedora-27-runtime-deps-6.0.X] ([Checksum][fedora-27-runtime-deps-checksum-6.0.X])
[Host][fedora-27-host-6.0.X] ([Checksum][fedora-27-host-checksum-6.0.X])
[App Hosts][fedora-27-apphost-pack-6.0.X] ([Checksum][fedora-27-apphost-pack-checksum-6.0.X])
[Host FX Resolver][fedora-27-hostfxr-6.0.X] ([Checksum][fedora-27-hostfxr-checksum-6.0.X])
[Targeting Pack][fedora-27-targeting-pack-6.0.X] ([Checksum][fedora-27-targeting-pack-checksum-6.0.X])
[Shared Framework][fedora-27-sharedfx-6.0.X] ([Checksum][fedora-27-sharedfx-checksum-6.0.X]) | -| **SLES 12 (x64)** | [![][sles-12-badge-6.0.X]][sles-12-version-6.0.X]
[Runtime-Deps][sles-12-runtime-deps-6.0.X] ([Checksum][sles-12-runtime-deps-checksum-6.0.X])
[Host][sles-12-host-6.0.X] ([Checksum][sles-12-host-checksum-6.0.X])
[App Hosts][sles-12-apphost-pack-6.0.X] ([Checksum][sles-12-apphost-pack-checksum-6.0.X])
[Host FX Resolver][sles-12-hostfxr-6.0.X] ([Checksum][sles-12-hostfxr-checksum-6.0.X])
[Targeting Pack][sles-12-targeting-pack-6.0.X] ([Checksum][sles-12-targeting-pack-checksum-6.0.X])
[Shared Framework][sles-12-sharedfx-6.0.X] ([Checksum][sles-12-sharedfx-checksum-6.0.X]) | -| **OpenSUSE 42 (x64)** | [![][OpenSUSE-42-badge-6.0.X]][OpenSUSE-42-version-6.0.X]
[Runtime-Deps][OpenSUSE-42-runtime-deps-6.0.X] ([Checksum][OpenSUSE-42-runtime-deps-checksum-6.0.X])
[Host][OpenSUSE-42-host-6.0.X] ([Checksum][OpenSUSE-42-host-checksum-6.0.X])
[App Hosts][OpenSUSE-42-apphost-pack-6.0.X] ([Checksum][OpenSUSE-42-apphost-pack-checksum-6.0.X])
[Host FX Resolver][OpenSUSE-42-hostfxr-6.0.X] ([Checksum][OpenSUSE-42-hostfxr-checksum-6.0.X])
[Targeting Pack][OpenSUSE-42-targeting-pack-6.0.X] ([Checksum][OpenSUSE-42-targeting-pack-checksum-6.0.X])
[Shared Framework][OpenSUSE-42-sharedfx-6.0.X] ([Checksum][OpenSUSE-42-sharedfx-checksum-6.0.X]) | +| **Windows (x64)** |
[Installer][win-x64-installer-6.0.X] ([Checksum][win-x64-installer-checksum-6.0.X])
[zip][win-x64-zip-6.0.X] ([Checksum][win-x64-zip-checksum-6.0.X]) | +| **Windows (x86)** |
[Installer][win-x86-installer-6.0.X] ([Checksum][win-x86-installer-checksum-6.0.X])
[zip][win-x86-zip-6.0.X] ([Checksum][win-x86-zip-checksum-6.0.X]) | +| **Windows (arm64)** |
[Installer][win-arm64-installer-6.0.X] ([Checksum][win-arm64-installer-checksum-6.0.X])
[zip][win-arm64-zip-6.0.X] ([Checksum][win-arm64-zip-checksum-6.0.X]) | +| **macOS (x64)** |
[Installer][osx-x64-installer-6.0.X] ([Checksum][osx-x64-installer-checksum-6.0.X])
[tar.gz][osx-x64-targz-6.0.X] ([Checksum][osx-x64-targz-checksum-6.0.X]) | +| **macOS (arm64)** |
[Installer][osx-arm64-installer-6.0.X] ([Checksum][osx-arm64-installer-checksum-6.0.X])
[tar.gz][osx-arm64-targz-6.0.X] ([Checksum][osx-arm64-targz-checksum-6.0.X]) | +| **Linux (x64)** (for glibc based OS) |
[tar.gz][linux-x64-targz-6.0.X] ([Checksum][linux-x64-targz-checksum-6.0.X]) | +| **Linux (armhf)** (for glibc based OS) |
[tar.gz][linux-arm-targz-6.0.X] ([Checksum][linux-arm-targz-checksum-6.0.X]) | +| **Linux (arm64)** (for glibc based OS) |
[tar.gz][linux-arm64-targz-6.0.X] ([Checksum][linux-arm64-targz-checksum-6.0.X]) | +| **Linux-musl (x64)** |
[tar.gz][linux-musl-x64-targz-6.0.X] ([Checksum][linux-musl-x64-targz-checksum-6.0.X]) | +| **Linux-musl (arm)** |
[tar.gz][linux-musl-arm-targz-6.0.X] ([Checksum][linux-musl-arm-targz-checksum-6.0.X]) | +| **Linux-musl (arm64)** |
[tar.gz][linux-musl-arm64-targz-6.0.X] ([Checksum][linux-musl-arm64-targz-checksum-6.0.X]) | +| **Dpkg Based Systems (x64)** |
[Runtime-Deps][deb-runtime-deps-6.0.X] ([Checksum][deb-runtime-deps-checksum-6.0.X])
[Host][deb-host-6.0.X] ([Checksum][deb-host-checksum-6.0.X])
[App Hosts][deb-apphost-pack-6.0.X] ([Checksum][deb-apphost-pack-checksum-6.0.X])
[Host FX Resolver][deb-hostfxr-6.0.X] ([Checksum][deb-hostfxr-checksum-6.0.X])
[Targeting Pack][deb-targeting-pack-6.0.X] ([Checksum][deb-targeting-pack-checksum-6.0.X])
[Shared Framework][deb-sharedfx-6.0.X] ([Checksum][deb-sharedfx-checksum-6.0.X]) | +| **CentOS 7 (x64)** |
[Runtime-Deps][centos-7-runtime-deps-6.0.X] ([Checksum][centos-7-runtime-deps-checksum-6.0.X])
[Host][centos-7-host-6.0.X] ([Checksum][centos-7-host-checksum-6.0.X])
[App Hosts][centos-7-apphost-pack-6.0.X] ([Checksum][centos-7-apphost-pack-checksum-6.0.X])
[Host FX Resolver][centos-7-hostfxr-6.0.X] ([Checksum][centos-7-hostfxr-checksum-6.0.X])
[Targeting Pack][centos-7-targeting-pack-6.0.X] ([Checksum][centos-7-targeting-pack-checksum-6.0.X])
[Shared Framework][centos-7-sharedfx-6.0.X] ([Checksum][centos-7-sharedfx-checksum-6.0.X]) | +| **RHEL 7.2 (x64)** |
[Host][rhel7-host-6.0.X] ([Checksum][rhel7-host-checksum-6.0.X])
[App Hosts][rhel7-apphost-pack-6.0.X] ([Checksum][rhel7-apphost-pack-checksum-6.0.X])
[Host FX Resolver][rhel7-hostfxr-6.0.X] ([Checksum][rhel7-hostfxr-checksum-6.0.X])
[Targeting Pack][rhel7-targeting-pack-6.0.X] ([Checksum][rhel7-targeting-pack-checksum-6.0.X])
[Shared Framework][rhel7-sharedfx-6.0.X] ([Checksum][rhel7-sharedfx-checksum-6.0.X]) | +| **Fedora 27 (x64)** |
[Runtime-Deps][fedora-27-runtime-deps-6.0.X] ([Checksum][fedora-27-runtime-deps-checksum-6.0.X])
[Host][fedora-27-host-6.0.X] ([Checksum][fedora-27-host-checksum-6.0.X])
[App Hosts][fedora-27-apphost-pack-6.0.X] ([Checksum][fedora-27-apphost-pack-checksum-6.0.X])
[Host FX Resolver][fedora-27-hostfxr-6.0.X] ([Checksum][fedora-27-hostfxr-checksum-6.0.X])
[Targeting Pack][fedora-27-targeting-pack-6.0.X] ([Checksum][fedora-27-targeting-pack-checksum-6.0.X])
[Shared Framework][fedora-27-sharedfx-6.0.X] ([Checksum][fedora-27-sharedfx-checksum-6.0.X]) | +| **SLES 12 (x64)** |
[Runtime-Deps][sles-12-runtime-deps-6.0.X] ([Checksum][sles-12-runtime-deps-checksum-6.0.X])
[Host][sles-12-host-6.0.X] ([Checksum][sles-12-host-checksum-6.0.X])
[App Hosts][sles-12-apphost-pack-6.0.X] ([Checksum][sles-12-apphost-pack-checksum-6.0.X])
[Host FX Resolver][sles-12-hostfxr-6.0.X] ([Checksum][sles-12-hostfxr-checksum-6.0.X])
[Targeting Pack][sles-12-targeting-pack-6.0.X] ([Checksum][sles-12-targeting-pack-checksum-6.0.X])
[Shared Framework][sles-12-sharedfx-6.0.X] ([Checksum][sles-12-sharedfx-checksum-6.0.X]) | +| **OpenSUSE 42 (x64)** |
[Runtime-Deps][OpenSUSE-42-runtime-deps-6.0.X] ([Checksum][OpenSUSE-42-runtime-deps-checksum-6.0.X])
[Host][OpenSUSE-42-host-6.0.X] ([Checksum][OpenSUSE-42-host-checksum-6.0.X])
[App Hosts][OpenSUSE-42-apphost-pack-6.0.X] ([Checksum][OpenSUSE-42-apphost-pack-checksum-6.0.X])
[Host FX Resolver][OpenSUSE-42-hostfxr-6.0.X] ([Checksum][OpenSUSE-42-hostfxr-checksum-6.0.X])
[Targeting Pack][OpenSUSE-42-targeting-pack-6.0.X] ([Checksum][OpenSUSE-42-targeting-pack-checksum-6.0.X])
[Shared Framework][OpenSUSE-42-sharedfx-6.0.X] ([Checksum][OpenSUSE-42-sharedfx-checksum-6.0.X]) | @@ -185,181 +185,181 @@ $ bin\Debug\net6.0\win-x64\publish\App.exe -[win-x64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_win-x64_Release_version_badge.svg?no-cache -[win-x64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[win-x64-installer-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x64.exe -[win-x64-installer-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x64.exe.sha512 -[win-x64-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x64.zip -[win-x64-zip-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x64.zip.sha512 -[win-x64-nethost-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-win-x64.zip -[win-x64-symbols-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-win-x64.zip - -[win-x86-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_win-x86_Release_version_badge.svg?no-cache -[win-x86-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[win-x86-installer-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x86.exe -[win-x86-installer-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x86.exe.sha512 -[win-x86-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x86.zip -[win-x86-zip-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-x86.zip.sha512 -[win-x86-nethost-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-win-x86.zip -[win-x86-symbols-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-win-x86.zip - -[win-arm64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_win-arm64_Release_version_badge.svg?no-cache -[win-arm64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[win-arm64-installer-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-arm64.exe -[win-arm64-installer-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-arm64.exe.sha512 -[win-arm64-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-arm64.zip -[win-arm64-zip-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-win-arm64.zip.sha512 -[win-arm64-nethost-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-win-arm64.zip -[win-arm64-symbols-zip-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-win-arm64.zip - -[osx-x64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_osx-x64_Release_version_badge.svg?no-cache -[osx-x64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[osx-x64-installer-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-x64.pkg -[osx-x64-installer-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-x64.pkg.sha512 -[osx-x64-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-x64.tar.gz -[osx-x64-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-x64.tar.gz.sha512 -[osx-x64-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-osx-x64.tar.gz -[osx-x64-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-osx-x64.tar.gz - -[osx-arm64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_osx-arm64_Release_version_badge.svg?no-cache -[osx-arm64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[osx-arm64-installer-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-arm64.pkg -[osx-arm64-installer-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-arm64.pkg.sha512 -[osx-arm64-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-arm64.tar.gz -[osx-arm64-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-osx-arm64.tar.gz.sha512 -[osx-arm64-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-osx-arm64.tar.gz -[osx-arm64-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-osx-arm64.tar.gz - -[linux-x64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_linux-x64_Release_version_badge.svg?no-cache -[linux-x64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[linux-x64-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-x64.tar.gz -[linux-x64-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-x64.tar.gz.sha512 -[linux-x64-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-linux-x64.tar.gz -[linux-x64-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-linux-x64.tar.gz - -[linux-arm-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_linux-arm_Release_version_badge.svg?no-cache -[linux-arm-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[linux-arm-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-arm.tar.gz -[linux-arm-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-arm.tar.gz.sha512 -[linux-arm-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-linux-arm.tar.gz -[linux-arm-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-linux-arm.tar.gz - -[linux-arm64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_linux-arm64_Release_version_badge.svg?no-cache -[linux-arm64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[linux-arm64-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-arm64.tar.gz -[linux-arm64-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-arm64.tar.gz.sha512 -[linux-arm64-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-linux-arm64.tar.gz -[linux-arm64-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-linux-arm64.tar.gz - -[deb-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_ubuntu.14.04-x64_Release_version_badge.svg?no-cache -[deb-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[deb-apphost-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.deb -[deb-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.deb.sha512 -[deb-host-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.deb -[deb-runtime-deps-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-x64.deb -[deb-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-x64.deb.sha512 -[deb-host-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.deb.sha512 -[deb-hostfxr-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.deb -[deb-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.deb.sha512 -[deb-sharedfx-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.deb -[deb-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.deb.sha512 -[deb-targeting-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.deb -[deb-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.deb.sha512 - -[rhel7-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_rhel.7-x64_Release_version_badge.svg?no-cache -[rhel7-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[rhel7-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-centos.7-x64.rpm -[rhel7-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-centos.7-x64.rpm.sha512 -[rhel7-apphost-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm -[rhel7-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm.sha512 -[rhel7-host-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm -[rhel7-host-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm.sha512 -[rhel7-hostfxr-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm -[rhel7-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm.sha512 -[rhel7-sharedfx-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm -[rhel7-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm.sha512 -[rhel7-targeting-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm -[rhel7-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm.sha512 - -[centos-7-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_centos.7-x64_Release_version_badge.svg?no-cache -[centos-7-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[centos-7-runtime-deps-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-centos.7-x64.rpm -[centos-7-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-centos.7-x64.rpm.sha512 -[centos-7-apphost-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm -[centos-7-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm.sha512 -[centos-7-host-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm -[centos-7-host-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm.sha512 -[centos-7-hostfxr-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm -[centos-7-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm.sha512 -[centos-7-sharedfx-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm -[centos-7-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm.sha512 -[centos-7-targeting-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm -[centos-7-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm.sha512 - -[fedora-27-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_fedora.27-x64_Release_version_badge.svg?no-cache -[fedora-27-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[fedora-27-runtime-deps-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-fedora.27-x64.rpm -[fedora-27-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-fedora.27-x64.rpm.sha512 -[fedora-27-apphost-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm -[fedora-27-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm.sha512 -[fedora-27-host-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm -[fedora-27-host-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm.sha512 -[fedora-27-hostfxr-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm -[fedora-27-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm.sha512 -[fedora-27-sharedfx-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm -[fedora-27-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm.sha512 -[fedora-27-targeting-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm -[fedora-27-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm.sha512 - -[sles-12-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_sles.12-x64_Release_version_badge.svg?no-cache -[sles-12-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[sles-12-runtime-deps-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-sles.12-x64.rpm -[sles-12-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-sles.12-x64.rpm.sha512 -[sles-12-apphost-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm -[sles-12-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm.sha512 -[sles-12-host-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm -[sles-12-host-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm.sha512 -[sles-12-hostfxr-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm -[sles-12-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm.sha512 -[sles-12-sharedfx-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm -[sles-12-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm.sha512 -[sles-12-targeting-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm -[sles-12-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm.sha512 - -[OpenSUSE-42-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_opensuse.42-x64_Release_version_badge.svg?no-cache -[OpenSUSE-42-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[OpenSUSE-42-runtime-deps-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-opensuse.42-x64.rpm -[OpenSUSE-42-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-deps-opensuse.42-x64.rpm.sha512 -[OpenSUSE-42-apphost-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm -[OpenSUSE-42-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-apphost-pack-x64.rpm.sha512 -[OpenSUSE-42-host-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm -[OpenSUSE-42-host-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-host-x64.rpm.sha512 -[OpenSUSE-42-hostfxr-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm -[OpenSUSE-42-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-hostfxr-x64.rpm.sha512 -[OpenSUSE-42-sharedfx-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm -[OpenSUSE-42-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-x64.rpm.sha512 -[OpenSUSE-42-targeting-pack-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm -[OpenSUSE-42-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-targeting-pack-x64.rpm.sha512 - -[linux-musl-x64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_linux-musl-x64_Release_version_badge.svg?no-cache -[linux-musl-x64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[linux-musl-x64-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-musl-x64.tar.gz -[linux-musl-x64-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-musl-x64.tar.gz.sha512 -[linux-musl-x64-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-linux-musl-x64.tar.gz -[linux-musl-x64-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-linux-musl-x64.tar.gz - -[linux-musl-arm-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_linux-musl-arm_Release_version_badge.svg?no-cache -[linux-musl-arm-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[linux-musl-arm-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-musl-arm.tar.gz -[linux-musl-arm-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-musl-arm.tar.gz.sha512 -[linux-musl-arm-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-linux-musl-arm.tar.gz -[linux-musl-arm-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-linux-musl-arm.tar.gz - -[linux-musl-arm64-badge-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/sharedfx_linux-musl-arm64_Release_version_badge.svg?no-cache -[linux-musl-arm64-version-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/productVersion.txt -[linux-musl-arm64-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-musl-arm64.tar.gz -[linux-musl-arm64-targz-checksum-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-linux-musl-arm64.tar.gz.sha512 -[linux-musl-arm64-nethost-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-nethost-linux-musl-arm64.tar.gz -[linux-musl-arm64-symbols-targz-6.0.X]: https://aka.ms/dotnet/net6/dev/Runtime/dotnet-runtime-symbols-linux-musl-arm64.tar.gz +[win-x64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_win-x64_Release_version_badge.svg?no-cache +[win-x64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[win-x64-installer-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x64.exe +[win-x64-installer-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x64.exe.sha512 +[win-x64-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x64.zip +[win-x64-zip-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x64.zip.sha512 +[win-x64-nethost-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-win-x64.zip +[win-x64-symbols-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-win-x64.zip + +[win-x86-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_win-x86_Release_version_badge.svg?no-cache +[win-x86-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[win-x86-installer-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x86.exe +[win-x86-installer-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x86.exe.sha512 +[win-x86-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x86.zip +[win-x86-zip-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-x86.zip.sha512 +[win-x86-nethost-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-win-x86.zip +[win-x86-symbols-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-win-x86.zip + +[win-arm64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_win-arm64_Release_version_badge.svg?no-cache +[win-arm64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[win-arm64-installer-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-arm64.exe +[win-arm64-installer-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-arm64.exe.sha512 +[win-arm64-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-arm64.zip +[win-arm64-zip-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-win-arm64.zip.sha512 +[win-arm64-nethost-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-win-arm64.zip +[win-arm64-symbols-zip-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-win-arm64.zip + +[osx-x64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_osx-x64_Release_version_badge.svg?no-cache +[osx-x64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[osx-x64-installer-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-x64.pkg +[osx-x64-installer-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-x64.pkg.sha512 +[osx-x64-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-x64.tar.gz +[osx-x64-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-x64.tar.gz.sha512 +[osx-x64-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-osx-x64.tar.gz +[osx-x64-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-osx-x64.tar.gz + +[osx-arm64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_osx-arm64_Release_version_badge.svg?no-cache +[osx-arm64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[osx-arm64-installer-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-arm64.pkg +[osx-arm64-installer-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-arm64.pkg.sha512 +[osx-arm64-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-arm64.tar.gz +[osx-arm64-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-osx-arm64.tar.gz.sha512 +[osx-arm64-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-osx-arm64.tar.gz +[osx-arm64-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-osx-arm64.tar.gz + +[linux-x64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_linux-x64_Release_version_badge.svg?no-cache +[linux-x64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[linux-x64-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-x64.tar.gz +[linux-x64-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-x64.tar.gz.sha512 +[linux-x64-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-linux-x64.tar.gz +[linux-x64-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-linux-x64.tar.gz + +[linux-arm-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_linux-arm_Release_version_badge.svg?no-cache +[linux-arm-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[linux-arm-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-arm.tar.gz +[linux-arm-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-arm.tar.gz.sha512 +[linux-arm-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-linux-arm.tar.gz +[linux-arm-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-linux-arm.tar.gz + +[linux-arm64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_linux-arm64_Release_version_badge.svg?no-cache +[linux-arm64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[linux-arm64-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-arm64.tar.gz +[linux-arm64-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-arm64.tar.gz.sha512 +[linux-arm64-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-linux-arm64.tar.gz +[linux-arm64-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-linux-arm64.tar.gz + +[deb-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_ubuntu.14.04-x64_Release_version_badge.svg?no-cache +[deb-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[deb-apphost-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.deb +[deb-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.deb.sha512 +[deb-host-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.deb +[deb-runtime-deps-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-x64.deb +[deb-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-x64.deb.sha512 +[deb-host-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.deb.sha512 +[deb-hostfxr-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.deb +[deb-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.deb.sha512 +[deb-sharedfx-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.deb +[deb-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.deb.sha512 +[deb-targeting-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.deb +[deb-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.deb.sha512 + +[rhel7-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_rhel.7-x64_Release_version_badge.svg?no-cache +[rhel7-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[rhel7-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-centos.7-x64.rpm +[rhel7-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-centos.7-x64.rpm.sha512 +[rhel7-apphost-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm +[rhel7-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm.sha512 +[rhel7-host-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm +[rhel7-host-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm.sha512 +[rhel7-hostfxr-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm +[rhel7-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm.sha512 +[rhel7-sharedfx-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm +[rhel7-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm.sha512 +[rhel7-targeting-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm +[rhel7-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm.sha512 + +[centos-7-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_centos.7-x64_Release_version_badge.svg?no-cache +[centos-7-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[centos-7-runtime-deps-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-centos.7-x64.rpm +[centos-7-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-centos.7-x64.rpm.sha512 +[centos-7-apphost-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm +[centos-7-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm.sha512 +[centos-7-host-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm +[centos-7-host-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm.sha512 +[centos-7-hostfxr-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm +[centos-7-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm.sha512 +[centos-7-sharedfx-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm +[centos-7-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm.sha512 +[centos-7-targeting-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm +[centos-7-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm.sha512 + +[fedora-27-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_fedora.27-x64_Release_version_badge.svg?no-cache +[fedora-27-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[fedora-27-runtime-deps-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-fedora.27-x64.rpm +[fedora-27-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-fedora.27-x64.rpm.sha512 +[fedora-27-apphost-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm +[fedora-27-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm.sha512 +[fedora-27-host-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm +[fedora-27-host-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm.sha512 +[fedora-27-hostfxr-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm +[fedora-27-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm.sha512 +[fedora-27-sharedfx-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm +[fedora-27-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm.sha512 +[fedora-27-targeting-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm +[fedora-27-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm.sha512 + +[sles-12-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_sles.12-x64_Release_version_badge.svg?no-cache +[sles-12-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[sles-12-runtime-deps-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-sles.12-x64.rpm +[sles-12-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-sles.12-x64.rpm.sha512 +[sles-12-apphost-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm +[sles-12-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm.sha512 +[sles-12-host-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm +[sles-12-host-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm.sha512 +[sles-12-hostfxr-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm +[sles-12-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm.sha512 +[sles-12-sharedfx-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm +[sles-12-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm.sha512 +[sles-12-targeting-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm +[sles-12-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm.sha512 + +[OpenSUSE-42-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_opensuse.42-x64_Release_version_badge.svg?no-cache +[OpenSUSE-42-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[OpenSUSE-42-runtime-deps-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-opensuse.42-x64.rpm +[OpenSUSE-42-runtime-deps-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-deps-opensuse.42-x64.rpm.sha512 +[OpenSUSE-42-apphost-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm +[OpenSUSE-42-apphost-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-apphost-pack-x64.rpm.sha512 +[OpenSUSE-42-host-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm +[OpenSUSE-42-host-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-host-x64.rpm.sha512 +[OpenSUSE-42-hostfxr-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm +[OpenSUSE-42-hostfxr-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-hostfxr-x64.rpm.sha512 +[OpenSUSE-42-sharedfx-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm +[OpenSUSE-42-sharedfx-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-x64.rpm.sha512 +[OpenSUSE-42-targeting-pack-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm +[OpenSUSE-42-targeting-pack-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-targeting-pack-x64.rpm.sha512 + +[linux-musl-x64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_linux-musl-x64_Release_version_badge.svg?no-cache +[linux-musl-x64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[linux-musl-x64-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-musl-x64.tar.gz +[linux-musl-x64-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-musl-x64.tar.gz.sha512 +[linux-musl-x64-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-linux-musl-x64.tar.gz +[linux-musl-x64-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-linux-musl-x64.tar.gz + +[linux-musl-arm-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_linux-musl-arm_Release_version_badge.svg?no-cache +[linux-musl-arm-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[linux-musl-arm-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-musl-arm.tar.gz +[linux-musl-arm-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-musl-arm.tar.gz.sha512 +[linux-musl-arm-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-linux-musl-arm.tar.gz +[linux-musl-arm-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-linux-musl-arm.tar.gz + +[linux-musl-arm64-badge-6.0.X]: https://aka.ms/dotnet/6.0/daily/sharedfx_linux-musl-arm64_Release_version_badge.svg?no-cache +[linux-musl-arm64-version-6.0.X]: https://aka.ms/dotnet/6.0/daily/runtime-productVersion.txt +[linux-musl-arm64-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-musl-arm64.tar.gz +[linux-musl-arm64-targz-checksum-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-linux-musl-arm64.tar.gz.sha512 +[linux-musl-arm64-nethost-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-nethost-linux-musl-arm64.tar.gz +[linux-musl-arm64-symbols-targz-6.0.X]: https://aka.ms/dotnet/6.0/daily/dotnet-runtime-symbols-linux-musl-arm64.tar.gz diff --git a/docs/project/list-of-obsoletions.md b/docs/project/list-of-obsoletions.md index 7a6c5b1d2b97f..cb094673111ec 100644 --- a/docs/project/list-of-obsoletions.md +++ b/docs/project/list-of-obsoletions.md @@ -26,3 +26,4 @@ Currently the identifiers `SYSLIB0001` through `SYSLIB0999` are carved out for o | __`SYSLIB0011`__ | `BinaryFormatter` serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for recommended alternatives. | | __`SYSLIB0012`__ | Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead. | | __`SYSLIB0013`__ | Uri.EscapeUriString can corrupt the Uri string in some cases. Consider using Uri.EscapeDataString for query string components instead. | +| __`SYSLIB0015`__ | DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications. | \ No newline at end of file diff --git a/docs/workflow/building/coreclr/README.md b/docs/workflow/building/coreclr/README.md index 06e7eba0fba75..bed097f5a010f 100644 --- a/docs/workflow/building/coreclr/README.md +++ b/docs/workflow/building/coreclr/README.md @@ -23,11 +23,13 @@ CoreCLR also supports a 'checked' build type which has asserts enabled like 'deb ./build.sh -subset clr -configuration checked ``` -If you want to use Ninja to drive the native build instead of Visual Studio MSBuild (on Windows) or Make (on non-Windows), you can pass the `-ninja` flag to the build script as follows: +If you want to use Ninja to drive the native build instead of Make on non-Windows platforms, you can pass the `-ninja` flag to the build script as follows: ``` ./build.cmd -subset clr -ninja ``` +If you want to use Visual Studio's MSBuild to drive the native build on Windows, you can pass the `-msbuild` flag to the build script similarly to the `-ninja` flag. + We recommend using Ninja for building the project on Windows since it more efficiently uses the build machine's resources for the native runtime build in comparison to Visual Studio's MSBuild. To pass extra compiler/linker flags to the coreclr build, set the environment variables `EXTRA_CFLAGS`, `EXTRA_CXXFLAGS` and `EXTRA_LDFLAGS` as needed. Don't set `CFLAGS`/`CXXFLAGS`/`LDFLAGS` directly as that might lead to configure-time tests failing. diff --git a/docs/workflow/debugging/coreclr/debugging.md b/docs/workflow/debugging/coreclr/debugging.md index 0eda9dc372738..4e8d8c00b95db 100644 --- a/docs/workflow/debugging/coreclr/debugging.md +++ b/docs/workflow/debugging/coreclr/debugging.md @@ -8,28 +8,32 @@ SOS has moved to the diagnostics repo. For more information on SOS, installation Debugging CoreCLR on Windows ============================ -1. Perform a build of the repo. -2. Open solution \\artifacts\obj\coreclr\windows.\.\\CoreCLR.sln in Visual Studio. \ and \ are based +1. Open the CoreCLR solution in Visual Studio. + - Method 1: Use the build scripts to open the solution. + 1. Run `./build.cmd -vs coreclr.sln -a -c `. This will create and launch the CoreCLR solution in VS for the specified architecture and configuration. + - Method 2: Manually build and open the solution. + 1. Perform a build of the repo with the `-msbuild` flag. + 2. Open solution `\\artifacts\obj\coreclr\windows.\.\\ide\CoreCLR.sln` in Visual Studio. `` and `` are based on type of build you did. By default they are 'x64' and 'Debug'. -3. Right-click the INSTALL project and choose ‘Set as StartUp Project’ -4. Bring up the properties page for the INSTALL project -5. Select Configuration Properties->Debugging from the left side tree control -6. Set Command=`$(SolutionDir)\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)\corerun.exe` +2. Right-click the INSTALL project and choose ‘Set as StartUp Project’ +3. Bring up the properties page for the INSTALL project +4. Select Configuration Properties->Debugging from the left side tree control +5. Set Command=`$(SolutionDir)\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)\corerun.exe` 1. This points to the folder where the built runtime binaries are present. -7. Set Command Arguments=`` (e.g. HelloWorld.dll) -8. Set Working Directory=`$(SolutionDir)\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)` +6. Set Command Arguments=`` (e.g. HelloWorld.dll) +7. Set Working Directory=`$(SolutionDir)\..\..\..\bin\coreclr\windows.$(Platform).$(Configuration)` 1. This points to the folder containing CoreCLR binaries. -9. Set Environment=`CORE_LIBRARIES=$(SolutionDir)\..\..\..\bin\runtime\-windows-$(Configuration)-$(Platform)`, +8. Set Environment=`CORE_LIBRARIES=$(SolutionDir)\..\..\..\bin\runtime\-windows-$(Configuration)-$(Platform)`, where '\' is the target framework of current branch, for example `netcoreapp3.1` `net5.0`. 1. This points to the folder containing core libraries except `System.Private.CoreLib`. 2. This step can be skipped if you are debugging CLR tests that references only `System.Private.CoreLib`. Otherwise, it's required to debug a realworld application that references anything else, including `System.Runtime`. -10. Right-click the INSTALL project and choose 'Build' +9. Right-click the INSTALL project and choose 'Build' 1. This will load necessary information from cmake to Visual Studio. -11. Press F11 to start debugging at wmain in corerun (or set a breakpoint in source and press F5 to run to it) +10. Press F11 to start debugging at wmain in corerun (or set a breakpoint in source and press F5 to run to it) 1. As an example, set a breakpoint for the EEStartup function in ceemain.cpp to break into CoreCLR startup. -Steps 1-10 only need to be done once, and then (11) can be repeated whenever you want to start debugging. The above can be done with Visual Studio 2019 as writing. +Steps 1-9 only need to be done once as long as there's been no changes to the CMake files in the repository. Afterwards, step 10 can be repeated whenever you want to start debugging. The above can be done with Visual Studio 2019 as writing. Keeping with latest version of Visual Studio is recommended. ### Using SOS with windbg or cdb on Windows ### diff --git a/docs/workflow/editing-and-debugging.md b/docs/workflow/editing-and-debugging.md index 50213150e4b9d..46e3d0b5e58fb 100644 --- a/docs/workflow/editing-and-debugging.md +++ b/docs/workflow/editing-and-debugging.md @@ -19,19 +19,20 @@ The repository has a number of Visual Studio Solutions files (`*.sln`) that are * `src\coreclr\System.Private.CoreLib\System.Private.CorLib.sln` - This solution is for all managed (C#) code that is defined in the runtime itself. This is all class library support of one form or another. - * `artifacts\obj\coreclr\windows..\CoreCLR.sln` - this solution contains most native (C++) projects + * `artifacts\obj\coreclr\windows..\ide\CoreCLR.sln` - this solution contains most native (C++) projects associated with the repository, including * `coreclr` - This is the main runtime DLL (the GC, class loader, interop are all here) * `corjit` - This is the Just In Time (JIT) compiler that compiles .NET Intermediate language to native code. * `corerun` - This is the simple host program that can run a .NET application * `crossgen` - This is the host program that runs the JIT compiler and produces .NET Native images (`*.ni.dll`) for C# code. + * This project can be automatically generated and opened in Visual Studio by running `./build.cmd -vs CoreCLR.sln -a -c ` from the root of the repository. Thus opening one of these two solution files (double clicking on them in Explorer) is typically all you need to do most editing. Notice that the CoreCLR solution is under the `artifacts` directory. This is because it is created as part of the build. -Thus you can only launch this solution after you have built at least once. +Thus you can only launch this solution after you have built at least once with the `-msbuild` flag or run the `./build.cmd -vs CoreCLR.sln` command line with the specified architecture and configuration. * See [Debugging CoreCLR](debugging/coreclr/debugging.md) diff --git a/docs/workflow/requirements/windows-requirements.md b/docs/workflow/requirements/windows-requirements.md index 6b11f07a6fc84..56f662f6f2199 100644 --- a/docs/workflow/requirements/windows-requirements.md +++ b/docs/workflow/requirements/windows-requirements.md @@ -42,9 +42,9 @@ The dotnet/runtime repository requires at least Visual Studio 2019 16.6. - Add its location (e.g. C:\Program Files (x86)\CMake\bin) to the PATH environment variable. The installation script has a check box to do this, but you can do it yourself after the fact following the instructions at [Adding to the Default PATH variable](#adding-to-the-default-path-variable). -The dotnet/runtime repository recommends using CMake 3.16.0 or newer, but works with CMake 3.15.5. +The dotnet/runtime repository recommends using CMake 3.16.4 or newer, but it may work with CMake 3.15.5. -## Ninja (optional) +## Ninja - Install Ninja in one of the two following ways - [Download the executable](https://github.com/ninja-build/ninja/releases) and add its location to [the Default PATH variable](#adding-to-the-default-path-variable). diff --git a/docs/workflow/testing/coreclr/unix-test-instructions.md b/docs/workflow/testing/coreclr/unix-test-instructions.md index c11a694396584..18842af2100bc 100644 --- a/docs/workflow/testing/coreclr/unix-test-instructions.md +++ b/docs/workflow/testing/coreclr/unix-test-instructions.md @@ -18,6 +18,12 @@ To build the tests on Unix: ./src/tests/build.sh ``` +By default, the test build uses Release as the libraries configuration. To use a different configuration, set the `LibrariesConfiguration` property to the desired configuration. For example: + +``` +./src/tests/build.sh /p:LibrariesConfiguration=Debug +``` + Please note that this builds the Priority 0 tests. To build priority 1: ```sh diff --git a/docs/workflow/testing/coreclr/windows-test-instructions.md b/docs/workflow/testing/coreclr/windows-test-instructions.md index 5b7457a291c9e..3819907618b4b 100644 --- a/docs/workflow/testing/coreclr/windows-test-instructions.md +++ b/docs/workflow/testing/coreclr/windows-test-instructions.md @@ -9,6 +9,12 @@ Building coreclr tests must be done using a specific script as follows: src\tests\build.cmd ``` +By default, the test build uses Release as the libraries configuration. To use a different configuration, set the `LibrariesConfiguration` property to the desired configuration. For example: + +``` +src\tests\build.cmd /p:LibrariesConfiguration=Debug +``` + ## Building Precompiled Tests ``` @@ -106,4 +112,4 @@ If you wish to run the test under a debugger (e.g. [WinDbg](http://msdn.microsof ## Modifying a test -If test changes are needed, make the change, and re-build the test project. This will binplace the binaries in the test binaries folder (e.g. `\artifacts\tests\coreclr\windows.x64.Checked\Exceptions\Finalization`). Then re-run the test following the instructions above. \ No newline at end of file +If test changes are needed, make the change, and re-build the test project. This will binplace the binaries in the test binaries folder (e.g. `\artifacts\tests\coreclr\windows.x64.Checked\Exceptions\Finalization`). Then re-run the test following the instructions above. diff --git a/docs/workflow/testing/host/testing.md b/docs/workflow/testing/host/testing.md index bd8be8d409909..5d990c42080fa 100644 --- a/docs/workflow/testing/host/testing.md +++ b/docs/workflow/testing/host/testing.md @@ -6,10 +6,12 @@ The [host tests](/src/installer/tests) use [xunit](http://xunit.github.io/) for To build the host tests, first build the product: -1. Build CoreCLR and libraries: - - * [CoreCLR](../../building/coreclr/README.md) - * [Libraries](../../building/libraries/README.md) +1. Build CoreCLR and libraries (`clr` and `libs` subsets): + ``` + build.cmd/sh -subset clr+libs -c Release + ``` + * [CoreCLR](../../building/coreclr/README.md) build instructions + * [Libraries](../../building/libraries/README.md) build instructions 2. Build the host and packs: ``` @@ -58,6 +60,8 @@ By default, the above command will also build the tests before running them. To ### Running specific tests +If all tests have not been previously run, make sure the [test context](#test-context) is set up for the test library. + Tests from a specific test project can be run using [`dotnet test`](https://docs.microsoft.com/dotnet/core/tools/dotnet-test) targeting the built test binary. For example: ``` dotnet test artifacts/bin/HostActivation.Tests/Debug/net5.0/HostActivation.Tests.dll @@ -68,12 +72,10 @@ To filter to specific tests within the test library, use the [filter options](ht dotnet test artifacts/bin/HostActivation.Tests/Debug/net5.0/HostActivation.Tests.dll --filter DependencyResolution ``` -If all tests have not been previously run, make sure the [test context](#test-context) is set up for the test library. - ### Visual Studio The [Microsoft.DotNet.CoreSetup.sln](/src/installer/Microsoft.DotNet.CoreSetup.sln) can be used to run and debug host tests through Visual Studio. When using the solution, the product should have already been [built](#building-tests) and the [test context](#test-context) set up. ### Preserving test artifacts -In order to test the hosting components, the tests launch a separate process (e.g. `dotnet`, apphost, native host) and validate the expected output (standard output and error) of the launched process. This usually involves copying or creating test artifacts in the form of an application to run or a .NET install to run against. The tests will delete these artifacts after the test finishes. To allow inspection or usage after the test finishes, set the environment variable `PRESERVE_TEST_RUNS=1` to avoid deleting the test artifacts. \ No newline at end of file +In order to test the hosting components, the tests launch a separate process (e.g. `dotnet`, apphost, native host) and validate the expected output (standard output and error) of the launched process. This usually involves copying or creating test artifacts in the form of an application to run or a .NET install to run against. The tests will delete these artifacts after the test finishes. To allow inspection or usage after the test finishes, set the environment variable `PRESERVE_TEST_RUNS=1` to avoid deleting the test artifacts. diff --git a/docs/workflow/testing/libraries/testing-android.md b/docs/workflow/testing/libraries/testing-android.md index 4c753f6832ce5..08ad7316814b5 100644 --- a/docs/workflow/testing/libraries/testing-android.md +++ b/docs/workflow/testing/libraries/testing-android.md @@ -45,15 +45,7 @@ curl https://dl.google.com/android/repository/commandlinetools-${HOST_OS_SHORT}- mkdir ${ANDROID_SDK_ROOT} && unzip ~/asdk.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && rm -rf ~/asdk.zip yes | ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} --licenses ${ANDROID_SDK_ROOT}/cmdline-tools/tools/bin/sdkmanager --sdk_root=${ANDROID_SDK_ROOT} "platform-tools" "platforms;android-${SDK_API_LEVEL}" "build-tools;${SDK_BUILD_TOOLS}" - -# We also need to download precompiled binaries and headers for OpenSSL from maven, this step is a temporary hack -# and will be removed once we figure out how to integrate OpenSSL properly as a dependency -export ANDROID_OPENSSL_AAR=~/openssl-android -curl https://maven.google.com/com/android/ndk/thirdparty/openssl/${OPENSSL_VER}/openssl-${OPENSSL_VER}.aar -L --output ~/openssl.zip -unzip ~/openssl.zip -d ${ANDROID_OPENSSL_AAR} && rm -rf ~/openssl.zip -printf "\n\nexport ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}\nexport ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}\nexport ANDROID_OPENSSL_AAR=${ANDROID_OPENSSL_AAR}\n" >> ${BASHRC} ``` -Save it to a file (e.g. `deps.sh`) and execute using `source` (e.g. `chmod +x deps.sh && source ./deps.sh`) in order to propogate the `ANDROID_NDK_ROOT`, `ANDROID_SDK_ROOT` and `ANDROID_OPENSSL_AAR` environment variables to the current process. ## Building Libs and Tests for Android diff --git a/docs/workflow/testing/libraries/testing-apple.md b/docs/workflow/testing/libraries/testing-apple.md index 3e9be3c600c51..99adf6a87176b 100644 --- a/docs/workflow/testing/libraries/testing-apple.md +++ b/docs/workflow/testing/libraries/testing-apple.md @@ -2,15 +2,17 @@ In order to build libraries and tests for iOS or tvOS you need recent version of XCode installed (e.g. 11.3 or higher). -Build Libraries for iOS: +Build Libraries for iOS Simulator: ``` -./build.sh mono+libs -os iOS -arch x64 +./build.sh mono+libs -os iOSSimulator -arch x64 ``` Run tests one by one for each test suite on a simulator: ``` -./build.sh libs.tests -os iOS -arch x64 -test +./build.sh libs.tests -os iOSSimulator -arch x64 -test ``` -In order to run the tests on a device you need to specify `DevTeamProvisioning` (see [developer.apple.com/account/#/membership](https://developer.apple.com/account/#/membership), scroll down to `Team ID`): +In order to run the tests on a device: +- Set the os to `iOS` instead of `iOSSimulator` +- Specify `DevTeamProvisioning` (see [developer.apple.com/account/#/membership](https://developer.apple.com/account/#/membership), scroll down to `Team ID`): ``` ./build.sh libs.tests -os iOS -arch x64 -test /p:DevTeamProvisioning=H1A2B3C4D5 ``` diff --git a/docs/workflow/testing/libraries/testing-wasm.md b/docs/workflow/testing/libraries/testing-wasm.md index 3979f9033a09b..242e6c9cee4a9 100644 --- a/docs/workflow/testing/libraries/testing-wasm.md +++ b/docs/workflow/testing/libraries/testing-wasm.md @@ -96,6 +96,18 @@ The following shows how to run tests for a specific library make -C src/mono/wasm/ run-browser-tests-System.AppContext ``` +### Passing arguments to xharness + +- `$(WasmXHarnessArgs)` - xharness command arguments + + Example: `WasmXHarnessArgs="--xyz"` -> becomes `dotnet xharness wasm test --xyz` + +- `$(WasmXHarnessMonoArgs)` - arguments to mono + + Example: `WasmXHarnessMonoArgs="--runtime-arg=--trace=E"` + +- `$(WasmTestAppArgs)` - arguments for the test app itself + ### Running outer loop tests using Browser instance To run all tests, including "outer loop" tests (which are typically slower and in some test suites less reliable, but which are more comprehensive): diff --git a/docs/workflow/testing/mono/testing.md b/docs/workflow/testing/mono/testing.md index f05bd5c5ea050..d0d4f6c745ade 100644 --- a/docs/workflow/testing/mono/testing.md +++ b/docs/workflow/testing/mono/testing.md @@ -12,13 +12,13 @@ To build the runtime tests for Mono JIT or interpreter, build CoreCLR and execut Run individual test: ``` -cd ../mono/netcore +cd src/mono make run-tests-coreclr CoreClrTest="bash ../../artifacts/tests/coreclr/OSX.x64.Release/JIT/opt/InstructionCombining/DivToMul/DivToMul.sh" ``` Run all tests: ``` -cd ../mono/netcore +cd src/mono make run-tests-coreclr-all ``` diff --git a/docs/workflow/trimming/ILLink-files.md b/docs/workflow/trimming/ILLink-files.md new file mode 100644 index 0000000000000..bbce56daa43a4 --- /dev/null +++ b/docs/workflow/trimming/ILLink-files.md @@ -0,0 +1,45 @@ +# ILLink Files + +There are a few `ILLink.*.xml` files under `src` folders in `dotnet/runtime`. These files are used by the trimming tool for various reasons. + +See https://github.com/mono/linker/blob/main/docs/data-formats.md for full documentation on these files. + +## ILLink.Descriptors.xml + +Descriptors are used to direct the trimming tool to always keep some items in the assembly, regardless of if the trimming tool can find any references to them. + +We try to limit the usage of descriptor files as much as possible. Since using a descriptor means the code will always be preserved, even in the final application. Typically the main scenario they are used is when non-IL code (e.g. C/C++, JavaScript, etc.) is calling into IL code. The trimming tool isn't able to see the non-IL code, so it doesn't know which IL methods are necessary. + +In some cases it is only necessary to preserve items only during `dotnet/runtime`'s build, but we don't want to unconditionally preserve them in an the final application. Examples of these cases are non-public methods only used by tests, or non-public methods that are called through Reflection by another assembly. To only preserve items during `dotnet/runtime`'s build, use a `ILLink.Descriptors.LibraryBuild.xml` file. + +In almost all cases, when using a descriptors file, add a comment justifying why it is necessary. + +## ILLink.Substitutions.xml + +Substitutions direct the trimming tool to replace specific method's body with either a throw or return constant statements. + +These files are mainly used to implement [feature switches](feature-switches.md). + +They can also be used to hard-code constants depending on the platform we are building for, typically in `System.Private.CoreLib.dll` since, at this time, that is the only assembly we build specifically for each target architecture. In those cases, there are multiple `ILLink.Substitutions.{arch}.xml` files, which get included depending on the architecture. This is possible through an MSBuild Item `@(ILLinkSubstitutionsXmls)` which can conditionally get added to, and all the .xml files are combined into a final `ILLink.Substitutions.xml`, which is embedded into the assembly. + +## ILLink.Suppressions.xml + +When we build `dotnet/runtime`, we run the trimming tool to analyze our assemblies for code that is using patterns (like Reflection) that may be broken once the application is trimmed. When the trimming tool encounters code that isn't trim compatible, it issues a warning. Because we haven't addressed all these warnings in the code, we suppress the existing warnings in `ILLink.Suppressions.xml` files, and fail the build when an unsuppressed warning is encountered. This ensures that no new code can introduce new warnings while we are addressing the existing warnings. + +If your new feature or bug fix is introducing new ILLink warnings, the warnings need to be addressed before your PR can be merged. No new suppressions should be added to an `ILLink.Suppressions.xml` file. To address the warnings, see [Linking the .NET Libraries](https://github.com/dotnet/designs/blob/main/accepted/2020/linking-libraries.md). Typically, either adding `[DynamicallyAccessedMembers]` or `[RequiresUnreferencedCode]` attributes are acceptable ways of addressing the warnings. If the warning is a false-positive (meaning it is trim compatible, but the trimming tool wasn't able to tell), it can be suppressed in code using an `[UnconditionalSuppressMessage]`. + +ILLink warnings that are suppressed by the `ILLink.Suppressions.xml` file will still be emitted when the final application is published. This allows developers to see where their application might be broken when it is trimmed. Warnings that are suppressed by `[UnconditionalSuppressMessage]` attributes in `dotnet/runtime` code will never be emitted, during the `dotnet/runtime` build nor in the final application. + +Sometimes it is beneficial to leave an ILLink warning as unsuppressed so the final application's developer sees the warning. An examples of this is using the [`Startup Hooks`](../../design/features/host-startup-hook.md) feature in .NET. By default this feature is disabled when trimming a .NET application. However, the application can re-enable the feature. When they do, an ILLInk warning is emitted when the application is trimmed telling them the feature may not work after trimming. + +To suppress a warning only in the `dotnet/runtime` build, but keep emitting it in the final application, add the warning to a `ILLink.Suppressions.LibraryBuild.xml` file, and include a justification why this approach was taken. + +## ILLink.LinkAttributes.xml + +Attribute annotations direct the trimming tool to behave as if the specified item has the specified attribute. + +This is mainly used to tell the trimming tool which attributes to remove from the trimmed application. This is useful because some attributes are only needed at development time. They aren't necessary at runtime. Trimming unnecessary attributes can make the application smaller. + +Under the covers, the way this works is that the `ILLink.LinkAttributes.xml` tells the trimming tool to act like a `[RemoveAttributeInstances]` attribute is applied to the attribute type we want to remove. The trimming tool removes any instantiations of the attribute in all the assemblies of the application. However, if the trimming tool encounters code that is trying to read the attribute at runtime, it doesn't trim the attribute instances. For example, if runtime code is reading the `ObsoleteAttribute`, `ObsoleteAttribute` instances won't be trimmed even if it was asked to be removed through this file. + +This is also how the above `ILLink.Suppressions.xml` file works under the covers. It injects `[UnconditionalSuppressMessage]` attributes to tell the trimming tool to act as if there was a suppress attribute in code. \ No newline at end of file diff --git a/docs/workflow/trimming/feature-switches.md b/docs/workflow/trimming/feature-switches.md index 40971d1fdb9df..36ac156f4f86b 100644 --- a/docs/workflow/trimming/feature-switches.md +++ b/docs/workflow/trimming/feature-switches.md @@ -19,6 +19,7 @@ configurations but their defaults might vary as any SDK can set the defaults dif | StartupHookSupport | System.StartupHookProvider.IsSupported | Startup hooks are disabled when set to false. Startup hook related functionality can be trimmed. | | TBD | System.Threading.ThreadPool.EnableDispatchAutoreleasePool | When set to true, creates an NSAutoreleasePool around each thread pool work item on applicable platforms. | | CustomResourceTypesSupport | System.Resources.ResourceManager.AllowCustomResourceTypes | Use of custom resource types is disabled when set to false. ResourceManager code paths that use reflection for custom types can be trimmed. | +| EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization | BinaryFormatter serialization support is trimmed when set to false. | Any feature-switch which defines property can be set in csproj file or on the command line as any other MSBuild property. Those without predefined property name diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index cde7125c5eafb..16f5ecb87d2f8 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -40,7 +40,7 @@ - + diff --git a/eng/Subsets.props b/eng/Subsets.props index f8e2005ff3b70..5d1b8740cadef 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -25,6 +25,8 @@ clr+mono+libs+host+packs mono+libs+packs + + clr+libs+host+packs @@ -40,7 +42,7 @@
- clr.runtime+clr.jit+clr.alljits+linuxdac+clr.corelib+clr.tools+clr.nativecorelib+clr.packages + clr.native+linuxdac+clr.corelib+clr.tools+clr.nativecorelib+clr.packages mono.llvm+ mono.llvm+ @@ -70,16 +72,18 @@ <_subset>+$(_subset.Trim('+'))+ - ClrRuntimeSubset=true;ClrJitSubset=true + ClrRuntimeSubset=true;ClrJitSubset=true;ClrILToolsSubset=true + - - + + + @@ -98,6 +102,7 @@ + @@ -142,14 +147,18 @@ - + - $(ClrRuntimeBuildSubsets);ClrRuntimeSubset=true;ClrJitSubset=true + $(ClrRuntimeBuildSubsets);ClrRuntimeSubset=true + + + + $(ClrRuntimeBuildSubsets);ClrFullNativeBuild=true @@ -161,7 +170,11 @@ - $(ClrRuntimeBuildSubsets);ClrAllJitsSubset=true;ClrJitSubset=true + $(ClrRuntimeBuildSubsets);ClrAllJitsSubset=true + + + + $(ClrRuntimeBuildSubsets);ClrILToolsSubset=true @@ -200,13 +213,13 @@ $(CoreClrProjectRoot)crossgen-corelib.proj" Category="clr" /> - + - + @@ -215,7 +228,7 @@ - + @@ -266,11 +279,11 @@ - + - + @@ -282,25 +295,31 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + diff --git a/eng/Tools.props b/eng/Tools.props index 1a9804dbaf692..a9a9fb714be97 100644 --- a/eng/Tools.props +++ b/eng/Tools.props @@ -7,6 +7,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 3db4c219dfb24..33d6d5e04e612 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,74 +1,74 @@ - + https://github.com/dotnet/icu - 3dc14146be2c110eedc947afa62894f9b53b4f04 + 29647ace51f6bb8085326ff137525f7a6d89d726 - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 7f13798e5f567b72ffe63205bf49839245f0f8c1 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd - + https://github.com/dotnet/arcade - 287fba3cbedce004fbd9823c268327960a69ca88 + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -94,117 +94,121 @@ https://github.com/microsoft/vstest 140434f7109d357d0158ade9e5164a4861513965 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/runtime-assets - c7c14818023404ed900360565f8289b8e14d9aa4 + 055ed026132a7070e41629cfb5e410f0fcbdf948 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 - + https://github.com/dotnet/llvm-project - 121a29ab0b411db38efa6659307d86fe04aefcb8 + a2bf9755287bdd3dcf998e41042e79dfd0136c85 https://github.com/dotnet/runtime 38017c3935de95d0335bac04f4901ddfc2718656 - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/dotnet/runtime - acbbb505492244b4c07a4a368257ba86a1fc02e1 + 102d1e856c7e0e553abeec937783da5debed73ad - + https://github.com/mono/linker - 0c4902a114192fce1e7570d998e70d24669e9cc3 + 388fef00320370785b08bbc42dda4a87f3fa38ab - + https://github.com/dotnet/xharness - 645910141e0ef98efee3c5bf9128b8fefb1a3c01 + c2c34bf7fdeb5a89e83817ced9a1a2c3c4cfc15c - + https://github.com/dotnet/xharness - 645910141e0ef98efee3c5bf9128b8fefb1a3c01 + c2c34bf7fdeb5a89e83817ced9a1a2c3c4cfc15c + + + https://github.com/dotnet/arcade + 1bfe91238cb39b9620b878b8f1bf0c789324b4cd diff --git a/eng/Versions.props b/eng/Versions.props index 261c39bba7ca4..16f6260879c6e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -7,7 +7,7 @@ 0 0 preview - 3 + 4 $(MajorVersion).$(MinorVersion).0.0 @@ -17,7 +17,7 @@ true true - true + false false - 6.0.0-beta.21160.7 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 2.5.1-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 - 6.0.0-beta.21155.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 2.5.1-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 + 6.0.0-beta.21203.1 5.9.0-preview.2 6.0.0-alpha.1.20612.4 - 6.0.0-preview.3.21157.6 - 6.0.0-preview.3.21157.6 + 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21178.6 3.1.0 - 6.0.0-preview.3.21157.6 + 6.0.0-preview.4.21178.6 1.2.0-beta.304 4.5.1 @@ -97,32 +98,35 @@ 4.7.0 4.7.0 4.7.0 - 6.0.0-preview.3.21157.6 - 6.0.0-preview.3.21157.6 + 6.0.0-preview.4.21178.6 + 6.0.0-preview.4.21178.6 4.3.0 4.5.4 4.5.0 1.1.1 4.3.0 - 6.0.0-preview.3.21157.6 + 6.0.0-preview.4.21178.6 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 - 5.0.0-beta.21159.1 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 6.0.0-beta.21174.2 + 99.99.99-master-20210317.2 + 99.99.99-master-20210317.2 + 99.99.99-master-20210317.2 99.99.99-master-20200806.6 99.99.99-master-20200806.6 99.99.99-master-20200806.6 99.99.99-master-20200806.6 99.99.99-master-20200806.6 - 1.7.0 + 16.9.0-beta1.21055.5 2.0.0-beta1.20253.1 2.0.65 2.2.0 @@ -135,17 +139,17 @@ These are used as reference assemblies only, so they must not take a ProdCon/source-build version. Insert "RefOnly" to avoid assignment via PVP. --> - 15.7.179 + 16.8.0 $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) - 4.9.4 - 4.9.4 + 5.8.0 + 5.8.0 1.0.1-prerelease-00006 16.9.0-preview-20201201-01 - 1.0.0-prerelease.21154.2 - 1.0.0-prerelease.21154.2 + 1.0.0-prerelease.21201.1 + 1.0.0-prerelease.21201.1 2.4.1 2.4.2 1.3.0 @@ -155,18 +159,18 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.2.21126.1 + 6.0.100-preview.2.21205.2 - 6.0.0-preview.3.21151.1 + 6.0.0-preview.4.21179.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 - 9.0.1-alpha.1.21158.1 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 + 9.0.1-alpha.1.21179.2 diff --git a/eng/build.ps1 b/eng/build.ps1 index 4d6c6fe5b2d4e..f93182c5bd89e 100644 --- a/eng/build.ps1 +++ b/eng/build.ps1 @@ -17,6 +17,7 @@ Param( [ValidateSet("Debug","Release")][string][Alias('lc')]$librariesConfiguration, [ValidateSet("CoreCLR","Mono")][string][Alias('rf')]$runtimeFlavor, [switch]$ninja, + [switch]$msbuild, [string]$cmakeargs, [switch]$pgoinstrument, [Parameter(ValueFromRemainingArguments=$true)][String[]]$properties @@ -51,7 +52,7 @@ function Get-Help() { Write-Host " [Default: Minimal]" Write-Host " -vs Open the solution with Visual Studio using the locally acquired SDK." Write-Host " Path or any project or solution name is accepted." - Write-Host " (Example: -vs Microsoft.CSharp)" + Write-Host " (Example: -vs Microsoft.CSharp or -vs CoreCLR.sln)" Write-Host "" Write-Host "Actions (defaults to -restore -build):" @@ -79,7 +80,8 @@ function Get-Help() { Write-Host "Native build settings:" Write-Host " -cmakeargs User-settable additional arguments passed to CMake." - Write-Host " -ninja Use Ninja instead of MSBuild to run the native build." + Write-Host " -ninja Use Ninja to drive the native build. (default)" + Write-Host " -msbuild Use MSBuild to drive the native build. This is a no-op for Mono." Write-Host " -pgoinstrument Build the CLR with PGO instrumentation." Write-Host "Command-line arguments not listed above are passed through to MSBuild." @@ -133,10 +135,10 @@ if ($vs) { if ($runtimeConfiguration) { $configToOpen = $runtimeConfiguration } - $vs = Split-Path $PSScriptRoot -Parent | Join-Path -ChildPath "artifacts\obj\coreclr" | Join-Path -ChildPath "windows.$archToOpen.$((Get-Culture).TextInfo.ToTitleCase($configToOpen))" | Join-Path -ChildPath "CoreCLR.sln" + $vs = Split-Path $PSScriptRoot -Parent | Join-Path -ChildPath "artifacts\obj\coreclr" | Join-Path -ChildPath "windows.$archToOpen.$((Get-Culture).TextInfo.ToTitleCase($configToOpen))" | Join-Path -ChildPath "ide" | Join-Path -ChildPath "CoreCLR.sln" if (-Not (Test-Path $vs)) { $repoRoot = Split-Path $PSScriptRoot -Parent - Invoke-Expression "& `"$repoRoot/src/coreclr/build-runtime.cmd`" -configureonly -$archToOpen -$configToOpen" + Invoke-Expression "& `"$repoRoot/src/coreclr/build-runtime.cmd`" -configureonly -$archToOpen -$configToOpen -msbuild" if ($lastExitCode -ne 0) { Write-Error "Failed to generate the CoreCLR solution file." exit 1 @@ -234,7 +236,9 @@ foreach ($argument in $PSBoundParameters.Keys) "properties" { $arguments += " " + $properties } "verbosity" { $arguments += " -$argument " + $($PSBoundParameters[$argument]) } "cmakeargs" { $arguments += " /p:CMakeArgs=`"$($PSBoundParameters[$argument])`"" } - "ninja" { $arguments += " /p:Ninja=$($PSBoundParameters[$argument])" } + # The -ninja switch is a no-op since Ninja is the default generator on Windows. + "ninja" { } + "msbuild" { $arguments += " /p:Ninja=false" } "pgoinstrument" { $arguments += " /p:PgoInstrument=$($PSBoundParameters[$argument])"} # configuration and arch can be specified multiple times, so they should be no-ops here "configuration" {} @@ -245,11 +249,25 @@ foreach ($argument in $PSBoundParameters.Keys) $failedBuilds = @() +if ($os -eq "Browser") { + # override default arch for Browser, we only support wasm + $arch = "wasm" + + if ($msbuild -eq $True) { + Write-Error "Using the -msbuild option isn't supported when building for Browser on Windows, we need need ninja for Emscripten." + exit 1 + } +} + foreach ($config in $configuration) { $argumentsWithConfig = $arguments + " -configuration $((Get-Culture).TextInfo.ToTitleCase($config))"; foreach ($singleArch in $arch) { $argumentsWithArch = "/p:TargetArchitecture=$singleArch " + $argumentsWithConfig - $env:__DistroRid="win-$singleArch" + if ($os -eq "Browser") { + $env:__DistroRid="browser-$singleArch" + } else { + $env:__DistroRid="win-$singleArch" + } Invoke-Expression "& `"$PSScriptRoot/common/build.ps1`" $argumentsWithArch" if ($lastExitCode -ne 0) { $failedBuilds += "Configuration: $config, Architecture: $singleArch" @@ -265,4 +283,8 @@ if ($failedBuilds.Count -ne 0) { exit 1 } +if ($ninja) { + Write-Host "The -ninja option has no effect on Windows builds since the Ninja generator is the default generator." +} + exit 0 diff --git a/eng/build.sh b/eng/build.sh index dc72e26d50092..50b91f7187634 100755 --- a/eng/build.sh +++ b/eng/build.sh @@ -28,8 +28,8 @@ usage() echo " --help (-h) Print help and exit." echo " --librariesConfiguration (-lc) Libraries build configuration: Debug or Release." echo " [Default: Debug]" - echo " --os Target operating system: windows, Linux, FreeBSD, OSX, MacCatalyst, tvOS, iOS, Android," - echo " Browser, NetBSD, illumos or Solaris." + echo " --os Target operating system: windows, Linux, FreeBSD, OSX, MacCatalyst, tvOS," + echo " tvOSSimulator, iOS, iOSSimulator, Android, Browser, NetBSD, illumos or Solaris." echo " [Default: Your machine's OS.]" echo " --projects Project or solution file(s) to build." echo " --runtimeConfiguration (-rc) Runtime build configuration: Debug, Release or Checked." @@ -266,8 +266,12 @@ while [[ $# > 0 ]]; do os="MacCatalyst" ;; tvos) os="tvOS" ;; + tvossimulator) + os="tvOSSimulator" ;; ios) os="iOS" ;; + iossimulator) + os="iOSSimulator" ;; android) os="Android" ;; browser) @@ -278,7 +282,7 @@ while [[ $# > 0 ]]; do os="Solaris" ;; *) echo "Unsupported target OS '$2'." - echo "The allowed values are windows, Linux, FreeBSD, OSX, MacCatalyst, tvOS, iOS, Android, Browser, illumos and Solaris." + echo "The allowed values are windows, Linux, FreeBSD, OSX, MacCatalyst, tvOS, tvOSSimulator, iOS, iOSSimulator, Android, Browser, illumos and Solaris." exit 1 ;; esac diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 new file mode 100644 index 0000000000000..7225ddc666906 --- /dev/null +++ b/eng/common/generate-locproject.ps1 @@ -0,0 +1,101 @@ +Param( + [Parameter(Mandatory=$true)][string] $SourcesDirectory, # Directory where source files live; if using a Localize directory it should live in here + [string] $LanguageSet = 'VS_Main_Languages', # Language set to be used in the LocProject.json + [switch] $UseCheckedInLocProjectJson, # When set, generates a LocProject.json and compares it to one that already exists in the repo; otherwise just generates one + [switch] $CreateNeutralXlfs # Creates neutral xlf files. Only set to false when running locally +) + +# Generates LocProject.json files for the OneLocBuild task. OneLocBuildTask is described here: +# https://ceapex.visualstudio.com/CEINTL/_wiki/wikis/CEINTL.wiki/107/Localization-with-OneLocBuild-Task + +Set-StrictMode -Version 2.0 +$ErrorActionPreference = "Stop" +. $PSScriptRoot\tools.ps1 + +Import-Module -Name (Join-Path $PSScriptRoot 'native\CommonLibrary.psm1') + +$exclusionsFilePath = "$SourcesDirectory\Localize\LocExclusions.json" +$exclusions = @{ Exclusions = @() } +if (Test-Path -Path $exclusionsFilePath) +{ + $exclusions = Get-Content "$exclusionsFilePath" | ConvertFrom-Json +} + +Push-Location "$SourcesDirectory" # push location for Resolve-Path -Relative to work + +# Template files +$jsonFiles = @() +$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "\.template\.config\\localize\\en\..+\.json" } # .NET templating pattern +$jsonFiles += Get-ChildItem -Recurse -Path "$SourcesDirectory" | Where-Object { $_.FullName -Match "en\\strings\.json" } # current winforms pattern + +$xlfFiles = @() + +$allXlfFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory\*\*.xlf" +$langXlfFiles = @() +if ($allXlfFiles) { + $null = $allXlfFiles[0].FullName -Match "\.([\w-]+)\.xlf" # matches '[langcode].xlf' + $firstLangCode = $Matches.1 + $langXlfFiles = Get-ChildItem -Recurse -Path "$SourcesDirectory\*\*.$firstLangCode.xlf" +} +$langXlfFiles | ForEach-Object { + $null = $_.Name -Match "(.+)\.[\w-]+\.xlf" # matches '[filename].[langcode].xlf + + $destinationFile = "$($_.Directory.FullName)\$($Matches.1).xlf" + $xlfFiles += Copy-Item "$($_.FullName)" -Destination $destinationFile -PassThru +} + +$locFiles = $jsonFiles + $xlfFiles + +$locJson = @{ + Projects = @( + @{ + LanguageSet = $LanguageSet + LocItems = @( + $locFiles | ForEach-Object { + $outputPath = "$(($_.DirectoryName | Resolve-Path -Relative) + "\")" + $continue = $true + foreach ($exclusion in $exclusions.Exclusions) { + if ($outputPath.Contains($exclusion)) + { + $continue = $false + } + } + $sourceFile = ($_.FullName | Resolve-Path -Relative) + if (!$CreateNeutralXlfs -and $_.Extension -eq '.xlf') { + Remove-Item -Path $sourceFile + } + if ($continue) + { + return @{ + SourceFile = $sourceFile + CopyOption = "LangIDOnName" + OutputPath = $outputPath + } + } + } + ) + } + ) +} + +$json = ConvertTo-Json $locJson -Depth 5 +Write-Host "LocProject.json generated:`n`n$json`n`n" +Pop-Location + +if (!$UseCheckedInLocProjectJson) { + New-Item "$SourcesDirectory\Localize\LocProject.json" -Force # Need this to make sure the Localize directory is created + Set-Content "$SourcesDirectory\Localize\LocProject.json" $json +} +else { + New-Item "$SourcesDirectory\Localize\LocProject-generated.json" -Force # Need this to make sure the Localize directory is created + Set-Content "$SourcesDirectory\Localize\LocProject-generated.json" $json + + if ((Get-FileHash "$SourcesDirectory\Localize\LocProject-generated.json").Hash -ne (Get-FileHash "$SourcesDirectory\Localize\LocProject.json").Hash) { + Write-PipelineTelemetryError -Category "OneLocBuild" -Message "Existing LocProject.json differs from generated LocProject.json. Download LocProject-generated.json and compare them." + + exit 1 + } + else { + Write-Host "Generated LocProject.json and current LocProject.json are identical." + } +} \ No newline at end of file diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml index 53c100222b217..4a32181fd8f93 100644 --- a/eng/common/templates/job/execute-sdl.yml +++ b/eng/common/templates/job/execute-sdl.yml @@ -45,6 +45,7 @@ jobs: buildId: $(AzDOBuildId) artifactName: ${{ artifactName }} downloadPath: $(Build.ArtifactStagingDirectory)\artifacts + checkDownloadedFiles: true - ${{ if eq(parameters.artifactNames, '') }}: - task: DownloadBuildArtifacts@0 displayName: Download Build Artifacts @@ -57,6 +58,7 @@ jobs: downloadType: specific files itemPattern: "**" downloadPath: $(Build.ArtifactStagingDirectory)\artifacts + checkDownloadedFiles: true - powershell: eng/common/sdl/extract-artifact-packages.ps1 -InputPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml new file mode 100644 index 0000000000000..928a70cda2c5a --- /dev/null +++ b/eng/common/templates/job/onelocbuild.yml @@ -0,0 +1,78 @@ +parameters: + # Optional: dependencies of the job + dependsOn: '' + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: + vmImage: vs2017-win2016 + + CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex + GithubPat: $(BotAccount-dotnet-bot-repo-PAT) + + SourcesDirectory: $(Build.SourcesDirectory) + CreatePr: true + UseCheckedInLocProjectJson: false + LanguageSet: VS_Main_Languages + LclSource: lclFilesInRepo + LclPackageId: '' + RepoType: gitHub + +jobs: +- job: OneLocBuild + + dependsOn: ${{ parameters.dependsOn }} + + displayName: OneLocBuild + + pool: ${{ parameters.pool }} + + variables: + - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat + - name: _GenerateLocProjectArguments + value: -SourcesDirectory ${{ parameters.SourcesDirectory }} + -LanguageSet "${{ parameters.LanguageSet }}" + -CreateNeutralXlfs + - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: + - name: _GenerateLocProjectArguments + value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson + + + steps: + - task: Powershell@2 + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + arguments: $(_GenerateLocProjectArguments) + displayName: Generate LocProject.json + + - task: OneLocBuild@2 + displayName: OneLocBuild + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + locProj: Localize/LocProject.json + outDir: $(Build.ArtifactStagingDirectory) + lclSource: ${{ parameters.LclSource }} + lclPackageId: ${{ parameters.LclPackageId }} + isCreatePrSelected: ${{ parameters.CreatePr }} + packageSourceAuth: patAuth + patVariable: ${{ parameters.CeapexPat }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + repoType: ${{ parameters.RepoType }} + gitHubPatVariable: "${{ parameters.GithubPat }}" + condition: always() + + - task: PublishBuildArtifacts@1 + displayName: Publish Localization Files + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)/loc' + PublishLocation: Container + ArtifactName: Loc + condition: always() + + - task: PublishBuildArtifacts@1 + displayName: Publish LocProject.json + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/Localize/' + PublishLocation: Container + ArtifactName: Loc + condition: always() \ No newline at end of file diff --git a/eng/common/templates/job/performance.yml b/eng/common/templates/job/performance.yml index f877fd7a89800..e6755df223429 100644 --- a/eng/common/templates/job/performance.yml +++ b/eng/common/templates/job/performance.yml @@ -71,11 +71,11 @@ jobs: # Run all of the steps to setup repo - ${{ each step in parameters.steps }}: - ${{ step }} - - powershell: $(Build.SourcesDirectory)\eng\common\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) ${{ parameters.extraSetupParameters }} + - powershell: $(Build.SourcesDirectory)\eng\testing\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) ${{ parameters.extraSetupParameters }} displayName: Performance Setup (Windows) condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} - - script: $(Build.SourcesDirectory)/eng/common/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) ${{ parameters.extraSetupParameters }} + - script: $(Build.SourcesDirectory)/eng/testing/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) ${{ parameters.extraSetupParameters }} displayName: Performance Setup (Unix) condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 66ef736417f69..3b9e2524ff37c 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -52,6 +52,7 @@ jobs: inputs: artifactName: AssetManifests downloadPath: '$(Build.StagingDirectory)/Download' + checkDownloadedFiles: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/templates/phases/publish-build-assets.yml b/eng/common/templates/phases/publish-build-assets.yml index a0a8074282aa8..4e51e472e2bba 100644 --- a/eng/common/templates/phases/publish-build-assets.yml +++ b/eng/common/templates/phases/publish-build-assets.yml @@ -20,6 +20,7 @@ phases: inputs: artifactName: AssetManifests downloadPath: '$(Build.StagingDirectory)/Download' + checkDownloadedFiles: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - task: AzureKeyVault@1 diff --git a/eng/common/templates/post-build/channels/generic-internal-channel.yml b/eng/common/templates/post-build/channels/generic-internal-channel.yml index 7ae5255921aa8..58fa9a35b8ca9 100644 --- a/eng/common/templates/post-build/channels/generic-internal-channel.yml +++ b/eng/common/templates/post-build/channels/generic-internal-channel.yml @@ -58,6 +58,7 @@ stages: PdbArtifacts/** BlobArtifacts/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -124,6 +125,7 @@ stages: BlobArtifacts/** AssetManifests/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true - task: NuGetToolInstaller@1 displayName: 'Install NuGet.exe' diff --git a/eng/common/templates/post-build/channels/generic-public-channel.yml b/eng/common/templates/post-build/channels/generic-public-channel.yml index 6cf39dbb2907c..b50c0b3bdb8e6 100644 --- a/eng/common/templates/post-build/channels/generic-public-channel.yml +++ b/eng/common/templates/post-build/channels/generic-public-channel.yml @@ -56,6 +56,7 @@ stages: PdbArtifacts/** BlobArtifacts/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -123,6 +124,7 @@ stages: BlobArtifacts/** AssetManifests/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true - task: NuGetToolInstaller@1 displayName: 'Install NuGet.exe' diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index c84ac55ebf8e4..4f79cf0f33703 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -117,6 +117,7 @@ stages: pipeline: $(AzDOPipelineId) buildId: $(AzDOBuildId) artifactName: PackageArtifacts + checkDownloadedFiles: true - task: PowerShell@2 displayName: Validate @@ -149,6 +150,7 @@ stages: pipeline: $(AzDOPipelineId) buildId: $(AzDOBuildId) artifactName: PackageArtifacts + checkDownloadedFiles: true itemPattern: | ** !**/Microsoft.SourceBuild.Intermediate.*.nupkg @@ -205,6 +207,7 @@ stages: pipeline: $(AzDOPipelineId) buildId: $(AzDOBuildId) artifactName: BlobArtifacts + checkDownloadedFiles: true - task: PowerShell@2 displayName: Validate diff --git a/eng/common/templates/post-build/setup-maestro-vars.yml b/eng/common/templates/post-build/setup-maestro-vars.yml index d0cbfb6c6ffdd..4a22b2e6f6de7 100644 --- a/eng/common/templates/post-build/setup-maestro-vars.yml +++ b/eng/common/templates/post-build/setup-maestro-vars.yml @@ -18,6 +18,7 @@ jobs: inputs: buildType: current artifactName: ReleaseConfigs + checkDownloadedFiles: true - task: PowerShell@2 name: setReleaseVars diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml index 8e336b7d16b34..65ee5992bf460 100644 --- a/eng/common/templates/steps/source-build.yml +++ b/eng/common/templates/steps/source-build.yml @@ -36,7 +36,7 @@ steps: ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ --configuration $buildConfig \ - --restore --build --pack --publish \ + --restore --build --pack --publish -bl \ $officialBuildArgs \ $targetRidArgs \ /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ diff --git a/eng/illink.targets b/eng/illink.targets index c7a97df0c4d36..52acb8716e004 100644 --- a/eng/illink.targets +++ b/eng/illink.targets @@ -24,9 +24,9 @@ $(ILLinkTrimInputPath)$(TargetName).pdb $(IntermediateOutputPath) - $(ILLinkDirectory)ILLinkTrim.xml - - $(ILLinkDirectory)ILLinkTrim_LibraryBuild.xml + $(ILLinkDirectory)ILLink.Descriptors.xml + + $(ILLinkDirectory)ILLink.Descriptors.LibraryBuild.xml $(IntermediateOutputPath)ILLink.Descriptors.xml $(IntermediateOutputPath)ILLink.Substitutions.xml @@ -35,13 +35,14 @@ $(ILLinkDirectory)ILLink.Suppressions $(ILLinkSuppressionsXmlFilePrefix).xml $(ILLinkSuppressionsXmlFilePrefix).$(Configuration).xml + $(ILLinkSuppressionsXmlFilePrefix).LibraryBuild.xml true - + @@ -73,6 +74,8 @@ Include="$(ILLinkSuppressionsXmlFile)" /> + @@ -90,8 +93,8 @@ - - + + ILLink.Descriptors.xml @@ -112,18 +115,18 @@ - $(ILLinkDescriptorsXmlIntermediatePath) + $(ILLinkDescriptorsXmlIntermediatePath) + CombinedLinkerXmlFile="$(ILLinkDescriptorsXml)" /> - + @@ -227,8 +230,8 @@ $(ILLinkArgs) --action link $(TargetName) $(ILLinkArgs) -b true - - $(ILLinkArgs) -x "$(ILLinkTrimXmlLibraryBuild)" + + $(ILLinkArgs) -x "$(ILLinkDescriptorsLibraryBuildXml)" - - - + + + >>:TARGET_64BIT>) elseif (CLR_CMAKE_TARGET_ARCH_ARM) set(ARCH_SOURCES_DIR arm) - if (ARM_SOFTFP) - set(ARCH_TARGET_NAME armel) - else () - set(ARCH_TARGET_NAME arm) - endif () + set(ARCH_TARGET_NAME arm) add_compile_definitions($<$>>:TARGET_ARM>) elseif (CLR_CMAKE_TARGET_ARCH_I386) set(ARCH_TARGET_NAME x86) @@ -479,7 +463,11 @@ if (MSVC) # Compile options for targeting windows add_compile_options($<$:/nologo>) # Suppress Startup Banner - add_compile_options($<$:/W3>) # set warning level to 3 + # /W3 is added by default by CMake, so remove it + string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "/W3" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + # set default warning level to 3 but allow targets to override it. + add_compile_options($<$:/W$>,$,3>>>) add_compile_options($<$:/WX>) # treat warnings as errors add_compile_options($<$:/Oi>) # enable intrinsics add_compile_options($<$:/Oy->) # disable suppressing of the creation of frame pointers on the call stack for quicker function calls @@ -502,59 +490,45 @@ if (MSVC) add_compile_options($<$:/Zc:inline>) # All inline functions must have their definition available in the current translation unit. add_compile_options($<$:/Zc:forScope>) # Enforce standards-compliant for scope. - add_compile_options($<$:/wd4960>) - add_compile_options($<$:/wd4961>) - add_compile_options($<$:/wd4603>) - add_compile_options($<$:/wd4627>) - add_compile_options($<$:/wd4838>) - add_compile_options($<$:/wd4456>) - add_compile_options($<$:/wd4457>) - add_compile_options($<$:/wd4458>) - add_compile_options($<$:/wd4459>) - add_compile_options($<$:/wd4091>) - add_compile_options($<$:/we4640>) - # Disable Warnings: - # 4291: Delete not defined for new, c++ exception may cause leak. - # 5105: Windows SDK headers use 'defined' operator in some macros - add_compile_options($<$:/wd4291>) - add_compile_options($<$:/wd5105>) + add_compile_options($<$:/wd4065>) # switch statement contains 'default' but no 'case' labels + add_compile_options($<$:/wd4100>) # 'identifier' : unreferenced formal parameter + add_compile_options($<$:/wd4127>) # conditional expression is constant + add_compile_options($<$:/wd4189>) # local variable is initialized but not referenced + add_compile_options($<$:/wd4200>) # nonstandard extension used : zero-sized array in struct/union + add_compile_options($<$:/wd4201>) # nonstandard extension used : nameless struct/union + add_compile_options($<$:/wd4245>) # conversion from 'type1' to 'type2', signed/unsigned mismatch + add_compile_options($<$:/wd4291>) # no matching operator delete found; memory will not be freed if initialization throws an exception + add_compile_options($<$:/wd4456>) # declaration of 'identifier' hides previous local declaration + add_compile_options($<$:/wd4457>) # declaration of 'identifier' hides function parameter + add_compile_options($<$:/wd4458>) # declaration of 'identifier' hides class member + add_compile_options($<$:/wd4733>) # Inline asm assigning to 'FS:0' : handler not registered as safe handler + add_compile_options($<$:/wd4838>) # conversion from 'type_1' to 'type_2' requires a narrowing conversion + add_compile_options($<$:/wd4960>) # 'function' is too big to be profiled + add_compile_options($<$:/wd4961>) # No profile data was merged into '.pgd file', profile-guided optimizations disabled + add_compile_options($<$:/wd5105>) # macro expansion producing 'defined' has undefined behavior # Treat Warnings as Errors: - # 4007: 'main' : must be __cdecl. - # 4013: 'function' undefined - assuming extern returning int. - # 4102: "'%$S' : unreferenced label". - # 4551: Function call missing argument list. - # 4700: Local used w/o being initialized. - # 4806: Unsafe operation involving type 'bool'. - add_compile_options($<$:/we4007>) - add_compile_options($<$:/we4013>) - add_compile_options($<$:/we4102>) - add_compile_options($<$:/we4551>) - add_compile_options($<$:/we4700>) - add_compile_options($<$:/we4806>) + add_compile_options($<$:/we4007>) # 'main' : must be __cdecl. + add_compile_options($<$:/we4013>) # 'function' undefined - assuming extern returning int. + add_compile_options($<$:/we4102>) # "'%$S' : unreferenced label". + add_compile_options($<$:/we4551>) # Function call missing argument list. + add_compile_options($<$:/we4700>) # Local used w/o being initialized. + add_compile_options($<$:/we4640>) # 'instance' : construction of local static object is not thread-safe + add_compile_options($<$:/we4806>) # Unsafe operation involving type 'bool'. # Set Warning Level 3: - # 4092: Sizeof returns 'unsigned long'. - # 4121: Structure is sensitive to alignment. - # 4125: Decimal digit in octal sequence. - # 4130: Logical operation on address of string constant. - # 4132: Const object should be initialized. - # 4212: Function declaration used ellipsis. - # 4530: C++ exception handler used, but unwind semantics are not enabled. Specify -GX. - # 35038: data member 'member1' will be initialized after data member 'member2'. - add_compile_options($<$:/w34092>) - add_compile_options($<$:/w34121>) - add_compile_options($<$:/w34125>) - add_compile_options($<$:/w34130>) - add_compile_options($<$:/w34132>) - add_compile_options($<$:/w34212>) - add_compile_options($<$:/w34530>) - add_compile_options($<$:/w35038>) + add_compile_options($<$:/w34092>) # Sizeof returns 'unsigned long'. + add_compile_options($<$:/w34121>) # Structure is sensitive to alignment. + add_compile_options($<$:/w34125>) # Decimal digit in octal sequence. + add_compile_options($<$:/w34130>) # Logical operation on address of string constant. + add_compile_options($<$:/w34132>) # Const object should be initialized. + add_compile_options($<$:/w34212>) # Function declaration used ellipsis. + add_compile_options($<$:/w34530>) # C++ exception handler used, but unwind semantics are not enabled. Specify -GX. + add_compile_options($<$:/w35038>) # data member 'member1' will be initialized after data member 'member2'. # Set Warning Level 4: - # 4177: Pragma data_seg s/b at global scope. - add_compile_options($<$:/w44177>) + add_compile_options($<$:/w44177>) # Pragma data_seg s/b at global scope. add_compile_options($<$:/Zi>) # enable debugging information add_compile_options($<$:/ZH:SHA_256>) # use SHA256 for generating hashes of compiler processed source files. diff --git a/eng/native/configureplatform.cmake b/eng/native/configureplatform.cmake index 9e7e7743c0f9e..aa141437e190c 100644 --- a/eng/native/configureplatform.cmake +++ b/eng/native/configureplatform.cmake @@ -41,9 +41,9 @@ if(CLR_CMAKE_HOST_OS STREQUAL Linux) set(CLR_CMAKE_HOST_UNIX_ARMV7L 1) elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL arm OR CMAKE_SYSTEM_PROCESSOR STREQUAL armv7-a) set(CLR_CMAKE_HOST_UNIX_ARM 1) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 OR CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) set(CLR_CMAKE_HOST_UNIX_ARM64 1) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL i686) + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL i686 OR CMAKE_SYSTEM_PROCESSOR STREQUAL x86) set(CLR_CMAKE_HOST_UNIX_X86 1) else() clr_unknown_arch() @@ -88,7 +88,7 @@ if(CLR_CMAKE_HOST_OS STREQUAL Darwin) set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_C_COMPILER} -o -c ") endif(CLR_CMAKE_HOST_OS STREQUAL Darwin) -if(CLR_CMAKE_HOST_OS STREQUAL iOS) +if(CLR_CMAKE_HOST_OS STREQUAL iOS OR CLR_CMAKE_HOST_OS STREQUAL iOSSimulator) set(CLR_CMAKE_HOST_UNIX 1) set(CLR_CMAKE_HOST_IOS 1) if(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") @@ -102,9 +102,9 @@ if(CLR_CMAKE_HOST_OS STREQUAL iOS) else() clr_unknown_arch() endif() -endif(CLR_CMAKE_HOST_OS STREQUAL iOS) +endif(CLR_CMAKE_HOST_OS STREQUAL iOS OR CLR_CMAKE_HOST_OS STREQUAL iOSSimulator) -if(CLR_CMAKE_HOST_OS STREQUAL tvOS) +if(CLR_CMAKE_HOST_OS STREQUAL tvOS OR CLR_CMAKE_HOST_OS STREQUAL tvOSSimulator) set(CLR_CMAKE_HOST_UNIX 1) set(CLR_CMAKE_HOST_TVOS 1) if(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") @@ -114,7 +114,7 @@ if(CLR_CMAKE_HOST_OS STREQUAL tvOS) else() clr_unknown_arch() endif() -endif(CLR_CMAKE_HOST_OS STREQUAL tvOS) +endif(CLR_CMAKE_HOST_OS STREQUAL tvOS OR CLR_CMAKE_HOST_OS STREQUAL tvOSSimulator) if(CLR_CMAKE_HOST_OS STREQUAL Android) set(CLR_CMAKE_HOST_UNIX 1) @@ -300,20 +300,20 @@ if(CLR_CMAKE_TARGET_OS STREQUAL Darwin) set(CLR_CMAKE_TARGET_OSX 1) endif(CLR_CMAKE_TARGET_OS STREQUAL Darwin) -if(CLR_CMAKE_TARGET_OS STREQUAL iOS) +if(CLR_CMAKE_TARGET_OS STREQUAL iOS OR CLR_CMAKE_TARGET_OS STREQUAL iOSSimulator) set(CLR_CMAKE_TARGET_UNIX 1) set(CLR_CMAKE_TARGET_IOS 1) -endif(CLR_CMAKE_TARGET_OS STREQUAL iOS) +endif(CLR_CMAKE_TARGET_OS STREQUAL iOS OR CLR_CMAKE_TARGET_OS STREQUAL iOSSimulator) if(CLR_CMAKE_TARGET_OS STREQUAL MacCatalyst) set(CLR_CMAKE_TARGET_UNIX 1) set(CLR_CMAKE_TARGET_MACCATALYST 1) endif(CLR_CMAKE_TARGET_OS STREQUAL MacCatalyst) -if(CLR_CMAKE_TARGET_OS STREQUAL tvOS) +if(CLR_CMAKE_TARGET_OS STREQUAL tvOS OR CLR_CMAKE_TARGET_OS STREQUAL tvOSSimulator) set(CLR_CMAKE_TARGET_UNIX 1) set(CLR_CMAKE_TARGET_TVOS 1) -endif(CLR_CMAKE_TARGET_OS STREQUAL tvOS) +endif(CLR_CMAKE_TARGET_OS STREQUAL tvOS OR CLR_CMAKE_TARGET_OS STREQUAL tvOSSimulator) if(CLR_CMAKE_TARGET_OS STREQUAL FreeBSD) set(CLR_CMAKE_TARGET_UNIX 1) diff --git a/eng/native/functions.cmake b/eng/native/functions.cmake index f39fb67bd0cf5..f5b4a44619130 100644 --- a/eng/native/functions.cmake +++ b/eng/native/functions.cmake @@ -206,6 +206,27 @@ function(compile_asm) set(${COMPILE_ASM_OUTPUT_OBJECTS} ${ASSEMBLED_OBJECTS} PARENT_SCOPE) endfunction() +# add_component(componentName [targetName] [EXCLUDE_FROM_ALL]) +function(add_component componentName) + if (${ARGC} GREATER 2 OR ${ARGC} EQUAL 2) + set(componentTargetName "${ARGV1}") + else() + set(componentTargetName "${componentName}") + endif() + if (${ARGC} EQUAL 3 AND "${ARG2}" STREQUAL "EXCLUDE_FROM_ALL") + set(exclude_from_all_flag "EXCLUDE_FROM_ALL") + endif() + get_property(definedComponents GLOBAL PROPERTY CLR_CMAKE_COMPONENTS) + list (FIND definedComponents "${componentName}" componentIndex) + if (${componentIndex} EQUAL -1) + list (APPEND definedComponents "${componentName}") + add_custom_target("${componentTargetName}" + COMMAND "${CMAKE_COMMAND}" "-DCMAKE_INSTALL_COMPONENT=${componentName}" "-DBUILD_TYPE=$" -P "${CMAKE_BINARY_DIR}/cmake_install.cmake" + ${exclude_from_all_flag}) + set_property(GLOBAL PROPERTY CLR_CMAKE_COMPONENTS ${definedComponents}) + endif() +endfunction() + function(generate_exports_file) set(INPUT_LIST ${ARGN}) list(GET INPUT_LIST -1 outputFilename) @@ -248,12 +269,29 @@ function(generate_exports_file_prefix inputFilename outputFilename prefix) PROPERTIES GENERATED TRUE) endfunction() +function (get_symbol_file_name targetName outputSymbolFilename) + if (CLR_CMAKE_HOST_UNIX) + if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) + set(strip_destination_file $.dwarf) + else () + set(strip_destination_file $.dbg) + endif () + + set(${outputSymbolFilename} ${strip_destination_file} PARENT_SCOPE) + else(CLR_CMAKE_HOST_UNIX) + # We can't use the $ generator expression here since + # the generator expression isn't supported on resource DLLs. + set(${outputSymbolFilename} $/$$.pdb PARENT_SCOPE) + endif(CLR_CMAKE_HOST_UNIX) +endfunction() + function(strip_symbols targetName outputFilename) + get_symbol_file_name(${targetName} strip_destination_file) + set(${outputFilename} ${strip_destination_file} PARENT_SCOPE) if (CLR_CMAKE_HOST_UNIX) set(strip_source_file $) if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) - set(strip_destination_file ${strip_source_file}.dwarf) # Ensure that dsymutil and strip are present find_program(DSYMUTIL dsymutil) @@ -282,7 +320,6 @@ function(strip_symbols targetName outputFilename) COMMENT "Stripping symbols from ${strip_source_file} into file ${strip_destination_file}" ) else (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) - set(strip_destination_file ${strip_source_file}.dbg) add_custom_command( TARGET ${targetName} @@ -294,26 +331,13 @@ function(strip_symbols targetName outputFilename) COMMENT "Stripping symbols from ${strip_source_file} into file ${strip_destination_file}" ) endif (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) - - set(${outputFilename} ${strip_destination_file} PARENT_SCOPE) - else(CLR_CMAKE_HOST_UNIX) - get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - if(is_multi_config) - # We can't use the $ generator expression here since - # the generator expression isn't supported on resource DLLs. - set(${outputFilename} ${CMAKE_CURRENT_BINARY_DIR}/$/${targetName}.pdb PARENT_SCOPE) - else() - # We can't use the $ generator expression here since - # the generator expression isn't supported on resource DLLs. - set(${outputFilename} ${CMAKE_CURRENT_BINARY_DIR}/${targetName}.pdb PARENT_SCOPE) - endif() endif(CLR_CMAKE_HOST_UNIX) endfunction() function(install_with_stripped_symbols targetName kind destination) if(NOT CLR_CMAKE_KEEP_NATIVE_SYMBOLS) strip_symbols(${targetName} symbol_file) - install_symbols(${symbol_file} ${destination}) + install_symbol_file(${symbol_file} ${destination} ${ARGN}) endif() if ((CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) AND ("${kind}" STREQUAL "TARGETS")) @@ -328,59 +352,75 @@ function(install_with_stripped_symbols targetName kind destination) else() message(FATAL_ERROR "The `kind` argument has to be either TARGETS or PROGRAMS, ${kind} was provided instead") endif() - install(${kind} ${install_source} DESTINATION ${destination}) + install(${kind} ${install_source} DESTINATION ${destination} ${ARGN}) endfunction() -function(install_symbols symbol_file destination_path) +function(install_symbol_file symbol_file destination_path) if(CLR_CMAKE_TARGET_WIN32) - install(FILES ${symbol_file} DESTINATION ${destination_path}/PDB) + install(FILES ${symbol_file} DESTINATION ${destination_path}/PDB ${ARGN}) else() - install(FILES ${symbol_file} DESTINATION ${destination_path}) + install(FILES ${symbol_file} DESTINATION ${destination_path} ${ARGN}) endif() endfunction() -# install_clr(TARGETS TARGETS targetName [targetName2 ...] [ADDITIONAL_DESTINATIONS destination]) +# install_clr(TARGETS targetName [targetName2 ...] [DESTINATIONS destination [destination2 ...]] [COMPONENT componentName]) function(install_clr) - set(multiValueArgs TARGETS ADDITIONAL_DESTINATIONS) - cmake_parse_arguments(INSTALL_CLR "" "" "${multiValueArgs}" ${ARGV}) + set(multiValueArgs TARGETS DESTINATIONS) + set(singleValueArgs COMPONENT) + set(options "") + cmake_parse_arguments(INSTALL_CLR "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGV}) if ("${INSTALL_CLR_TARGETS}" STREQUAL "") message(FATAL_ERROR "At least one target must be passed to install_clr(TARGETS )") endif() - set(destinations ".") + if ("${INSTALL_CLR_DESTINATIONS}" STREQUAL "") + message(FATAL_ERROR "At least one destination must be passed to install_clr.") + endif() + + set(destinations "") - if (NOT "${INSTALL_CLR_ADDITIONAL_DESTINATIONS}" STREQUAL "") - list(APPEND destinations ${INSTALL_CLR_ADDITIONAL_DESTINATIONS}) + if (NOT "${INSTALL_CLR_DESTINATIONS}" STREQUAL "") + list(APPEND destinations ${INSTALL_CLR_DESTINATIONS}) + endif() + + if ("${INSTALL_CLR_COMPONENT}" STREQUAL "") + set(INSTALL_CLR_COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) endif() foreach(targetName ${INSTALL_CLR_TARGETS}) - list(FIND CLR_CROSS_COMPONENTS_LIST ${targetName} INDEX) - if (NOT DEFINED CLR_CROSS_COMPONENTS_LIST OR NOT ${INDEX} EQUAL -1) - if (NOT CLR_CMAKE_KEEP_NATIVE_SYMBOLS) - strip_symbols(${targetName} symbol_file) - endif() + if (NOT "${INSTALL_CLR_COMPONENT}" STREQUAL "${targetName}") + get_property(definedComponents GLOBAL PROPERTY CLR_CMAKE_COMPONENTS) + list(FIND definedComponents "${INSTALL_CLR_COMPONENT}" componentIdx) + if (${componentIdx} EQUAL -1) + message(FATAL_ERROR "The ${INSTALL_CLR_COMPONENT} component is not defined. Add a call to `add_component(${INSTALL_CLR_COMPONENT})` to define the component in the build.") + endif() + add_dependencies(${INSTALL_CLR_COMPONENT} ${targetName}) + endif() + get_target_property(targetType ${targetName} TYPE) + if (NOT CLR_CMAKE_KEEP_NATIVE_SYMBOLS AND NOT "${targetType}" STREQUAL "STATIC_LIBRARY") + get_symbol_file_name(${targetName} symbolFile) + endif() - foreach(destination ${destinations}) - # We don't need to install the export libraries for our DLLs - # since they won't be directly linked against. - install(PROGRAMS $ DESTINATION ${destination}) - if (NOT CLR_CMAKE_KEEP_NATIVE_SYMBOLS) - install_symbols(${symbol_file} ${destination}) - endif() + foreach(destination ${destinations}) + # We don't need to install the export libraries for our DLLs + # since they won't be directly linked against. + install(PROGRAMS $ DESTINATION ${destination} COMPONENT ${INSTALL_CLR_COMPONENT}) + if (NOT "${symbolFile}" STREQUAL "") + install_symbol_file(${symbolFile} ${destination} COMPONENT ${INSTALL_CLR_COMPONENT}) + endif() - if(CLR_CMAKE_PGO_INSTRUMENT) - if(WIN32) - get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - if(is_multi_config) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/${targetName}.pgd DESTINATION ${destination}/PGD OPTIONAL) - else() - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${targetName}.pgd DESTINATION ${destination}/PGD OPTIONAL) - endif() - endif() + if(CLR_CMAKE_PGO_INSTRUMENT) + if(WIN32) + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/$/${targetName}.pgd DESTINATION ${destination}/PGD OPTIONAL COMPONENT ${INSTALL_CLR_COMPONENT}) + else() + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${targetName}.pgd DESTINATION ${destination}/PGD OPTIONAL COMPONENT ${INSTALL_CLR_COMPONENT}) endif() - endforeach() - endif() + endif() + endif() + endforeach() endforeach() endfunction() @@ -392,19 +432,24 @@ endfunction() # - creating executable pages from anonymous memory, # - making read-only-after-relocations (RELRO) data pages writable again. function(disable_pax_mprotect targetName) - # Try to locate the paxctl tool. Failure to find it is not fatal, - # but the generated executables won't work on a system where PAX is set - # to prevent applications to create executable memory mappings. - find_program(PAXCTL paxctl) - - if (NOT PAXCTL STREQUAL "PAXCTL-NOTFOUND") - add_custom_command( - TARGET ${targetName} - POST_BUILD - VERBATIM - COMMAND ${PAXCTL} -c -m $ - ) - endif() + # Disabling PAX hardening only makes sense in systems that use Elf image formats. Particularly, looking + # for paxctl in macOS is problematic as it collides with popular software for that OS that performs completely + # unrelated functionality. Only look for it when we'll generate Elf images. + if (CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CLR_CMAKE_HOST_SUNOS) + # Try to locate the paxctl tool. Failure to find it is not fatal, + # but the generated executables won't work on a system where PAX is set + # to prevent applications to create executable memory mappings. + find_program(PAXCTL paxctl) + + if (NOT PAXCTL STREQUAL "PAXCTL-NOTFOUND") + add_custom_command( + TARGET ${targetName} + POST_BUILD + VERBATIM + COMMAND ${PAXCTL} -c -m $ + ) + endif() + endif(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CLR_CMAKE_HOST_SUNOS) endfunction() if (CMAKE_VERSION VERSION_LESS "3.12") @@ -422,73 +467,29 @@ if (CMAKE_VERSION VERSION_LESS "3.16") endfunction() endif() -function(_add_executable) +function(add_executable_clr) if(NOT WIN32) add_executable(${ARGV} ${VERSION_FILE_PATH}) disable_pax_mprotect(${ARGV}) else() add_executable(${ARGV}) endif(NOT WIN32) - list(FIND CLR_CROSS_COMPONENTS_LIST ${ARGV0} INDEX) - if (DEFINED CLR_CROSS_COMPONENTS_LIST AND ${INDEX} EQUAL -1) - set_target_properties(${ARGV0} PROPERTIES EXCLUDE_FROM_ALL 1) + if(NOT CLR_CMAKE_KEEP_NATIVE_SYMBOLS) + strip_symbols(${ARGV0} symbolFile) endif() endfunction() -function(_add_library) +function(add_library_clr) if(NOT WIN32 AND "${ARGV1}" STREQUAL "SHARED") add_library(${ARGV} ${VERSION_FILE_PATH}) else() add_library(${ARGV}) endif(NOT WIN32 AND "${ARGV1}" STREQUAL "SHARED") - list(FIND CLR_CROSS_COMPONENTS_LIST ${ARGV0} INDEX) - if (DEFINED CLR_CROSS_COMPONENTS_LIST AND ${INDEX} EQUAL -1) - set_target_properties(${ARGV0} PROPERTIES EXCLUDE_FROM_ALL 1) + if("${ARGV1}" STREQUAL "SHARED" AND NOT CLR_CMAKE_KEEP_NATIVE_SYMBOLS) + strip_symbols(${ARGV0} symbolFile) endif() endfunction() -function(_install) - if(NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) - install(${ARGV}) - endif() -endfunction() - -function(add_library_clr) - _add_library(${ARGV}) -endfunction() - -function(add_executable_clr) - _add_executable(${ARGV}) -endfunction() - -function(generate_module_index Target ModuleIndexFile) - if(CLR_CMAKE_HOST_WIN32) - set(scriptExt ".cmd") - else() - set(scriptExt ".sh") - endif() - - set(index_timestamp ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${Target}_index.timestamp) - - add_custom_command( - OUTPUT ${index_timestamp} - COMMAND ${CLR_ENG_NATIVE_DIR}/genmoduleindex${scriptExt} $ ${ModuleIndexFile} - COMMAND ${CMAKE_COMMAND} -E touch ${index_timestamp} - DEPENDS ${Target} - COMMENT "Generating ${Target} module index file -> ${ModuleIndexFile}" - ) - - set_source_files_properties( - ${ModuleIndexFile} - PROPERTIES GENERATED TRUE - ) - - add_custom_target( - ${Target}_module_index_header - DEPENDS ${index_timestamp} - ) -endfunction(generate_module_index) - # add_linker_flag(Flag [Config1 Config2 ...]) function(add_linker_flag Flag) if (ARGN STREQUAL "") diff --git a/eng/native/gen-buildsys.cmd b/eng/native/gen-buildsys.cmd index c340ec2f9fadb..2d81dd174e0a0 100644 --- a/eng/native/gen-buildsys.cmd +++ b/eng/native/gen-buildsys.cmd @@ -10,8 +10,9 @@ for %%x in (%*) do Set /A argC+=1 if %argC% lss 4 GOTO :USAGE if %1=="/?" GOTO :USAGE -setlocal +setlocal enabledelayedexpansion set basePath=%~dp0 +set __repoRoot=%~dp0..\..\ :: remove quotes set "basePath=%basePath:"=%" :: remove trailing slash @@ -29,7 +30,7 @@ if /i "%__Ninja%" == "1" ( if /i NOT "%__Arch%" == "wasm" ( if /i "%__VSVersion%" == "vs2019" (set __CmakeGenerator=%__CmakeGenerator% 16 2019) if /i "%__VSVersion%" == "vs2017" (set __CmakeGenerator=%__CmakeGenerator% 15 2017) - + if /i "%__Arch%" == "x64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A x64) if /i "%__Arch%" == "arm" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A ARM) if /i "%__Arch%" == "arm64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A ARM64) @@ -40,15 +41,18 @@ if /i "%__Ninja%" == "1" ( ) if /i "%__Arch%" == "wasm" ( + if "%EMSDK_PATH%" == "" ( - echo Error: Should set EMSDK_PATH environment variable pointing to emsdk root. - exit /B 1 - ) + if not exist "%__repoRoot%src\mono\wasm\emsdk" ( + echo Error: Should set EMSDK_PATH environment variable pointing to emsdk root. + exit /B 1 + ) - if "%EMSCRIPTEN_ROOT%" == "" ( - set EMSCRIPTEN_ROOT="%EMSDK_PATH/upstream/emscripten%" + set EMSDK_PATH=%__repoRoot%src\mono\wasm\emsdk + set EMSDK_PATH=!EMSDK_PATH:\=/! ) - set __ExtraCmakeParams=%__ExtraCmakeParams% "-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1" "-DCMAKE_TOOLCHAIN_FILE=%EMSCRIPTEN%/cmake/Modules/Platform/Emscripten.cmake" + + set __ExtraCmakeParams=%__ExtraCmakeParams% "-DCMAKE_TOOLCHAIN_FILE=!EMSDK_PATH!/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" set __UseEmcmake=1 ) else ( set __ExtraCmakeParams=%__ExtraCmakeParams% "-DCMAKE_SYSTEM_VERSION=10.0" @@ -63,10 +67,32 @@ goto loop set __ExtraCmakeParams="-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DCLR_CMAKE_HOST_ARCH=%__Arch%" %__ExtraCmakeParams% +set __CmdLineOptionsUpToDateFile=%__IntermediatesDir%\cmake_cmd_line.txt +set __CMakeCmdLineCache= +if not "%__ConfigureOnly%" == "1" ( + REM MSBuild can't reload from a CMake reconfigure during build correctly, so only do this + REM command-line up to date check for non-VS generators. + if not "%__CmakeGenerator%" == "Visual Studio" ( + if exist "%__CmdLineOptionsUpToDateFile%" ( + set /p __CMakeCmdLineCache=<"%__CmdLineOptionsUpToDateFile%" + REM Strip the extra space from the end of the cached command line + if [!__ExtraCmakeParams!] == [!__CMakeCmdLineCache:~0,-1!] ( + echo The CMake command line is the same as the last run. Skipping running CMake. + exit /B 0 + ) else ( + echo The CMake command line differs from the last run. Running CMake again. + echo %__ExtraCmakeParams% > %__CmdLineOptionsUpToDateFile% + ) + ) else ( + echo %__ExtraCmakeParams% > %__CmdLineOptionsUpToDateFile% + ) + ) +) + if /i "%__UseEmcmake%" == "1" ( - emcmake "%CMakePath%" %__ExtraCmakeParams% --no-warn-unused-cli -G "%__CmakeGenerator%" -B %__IntermediatesDir% -S %__SourceDir% + call "!EMSDK_PATH!/emsdk_env.bat" > nul 2>&1 && emcmake "%CMakePath%" %__ExtraCmakeParams% --no-warn-unused-cli -G "%__CmakeGenerator%" -B %__IntermediatesDir% -S %__SourceDir% ) else ( - "%CMakePath%" %__ExtraCmakeParams% --no-warn-unused-cli -G "%__CmakeGenerator%" -B %__IntermediatesDir% -S %__SourceDir% + "%CMakePath%" %__ExtraCmakeParams% --no-warn-unused-cli -G "%__CmakeGenerator%" -B %__IntermediatesDir% -S %__SourceDir% ) endlocal exit /B %errorlevel% diff --git a/eng/native/gen-buildsys.sh b/eng/native/gen-buildsys.sh index f852ae0b25c86..f671182e473e9 100755 --- a/eng/native/gen-buildsys.sh +++ b/eng/native/gen-buildsys.sh @@ -97,6 +97,19 @@ if [[ "$build_arch" == "wasm" ]]; then cmake_command="emcmake $cmake_command" fi +cmake_args_to_cache="$scan_build\n$SCAN_BUILD_COMMAND\n$generator\n$__UnprocessedCMakeArgs" +cmake_args_cache_file="$__CMakeBinDir/cmake_cmd_line.txt" +if [[ -z "$__ConfigureOnly" ]]; then + if [[ -e "$cmake_args_cache_file" ]]; then + cmake_args_cache=$(<"$cmake_args_cache_file") + if [[ "$cmake_args_cache" == "$cmake_args_to_cache" ]]; then + echo "CMake command line is unchanged. Reusing previous cache instead of regenerating." + exit 0 + fi + fi + echo $cmake_args_to_cache > $cmake_args_cache_file +fi + # We have to be able to build with CMake 3.6.2, so we can't use the -S or -B options pushd "$2" diff --git a/eng/native/init-compiler-and-cmake.cmd b/eng/native/init-compiler-and-cmake.cmd deleted file mode 100644 index 43e4cf8b8fecd..0000000000000 --- a/eng/native/init-compiler-and-cmake.cmd +++ /dev/null @@ -1,92 +0,0 @@ -@if not defined _echo @echo off -rem -rem This file locates VS C++ compilers and cmake for windows. - -rem Make sure the current directory stays intact -set VSCMD_START_DIR="%CD%" - -:SetupArgs -:: Initialize the args that will be passed to cmake -set __VCBuildArch=x86_amd64 - -:Arg_Loop -:: Since the native build requires some configuration information before msbuild is called, we have to do some manual args parsing -if [%1] == [] goto :ToolsVersion -if /i [%1] == [x86] ( set __VCBuildArch=x86&&shift&goto Arg_Loop) -if /i [%1] == [arm] ( set __VCBuildArch=x86_arm&&shift&goto Arg_Loop) -if /i [%1] == [x64] ( set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) -if /i [%1] == [arm64] ( set __VCBuildArch=x86_arm64&&shift&goto Arg_Loop) - -shift -goto :Arg_Loop - -:ToolsVersion -:: Default to highest Visual Studio version available -:: -:: For VS2017 and later, multiple instances can be installed on the same box SxS and VSxxxCOMNTOOLS is only set if the user -:: has launched the VS2017 or VS2019 Developer Command Prompt. -:: -:: Following this logic, we will default to the VS2017 or VS2019 toolset if VS150COMNTOOLS or VS160COMMONTOOLS tools is -:: set, as this indicates the user is running from the VS2017 or VS2019 Developer Command Prompt and -:: is already configured to use that toolset. Otherwise, we will fail the script if no supported VS instance can be found. - -if defined VisualStudioVersion goto :RunVCVars - -set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -if exist %_VSWHERE% ( - for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools -) -if not exist "%_VSCOMNTOOLS%" goto :MissingVersion - -call "%_VSCOMNTOOLS%\VsDevCmd.bat" -no_logo - -:RunVCVars -if "%VisualStudioVersion%"=="16.0" ( - goto :VS2019 -) else if "%VisualStudioVersion%"=="15.0" ( - goto :VS2017 -) - -:MissingVersion -:: Can't find appropriate VS install -echo Error: Visual Studio 2019 required -echo Please see https://github.com/dotnet/runtime/tree/main/docs/workflow/building/libraries for build instructions. -exit /b 1 - -:VS2019 -:: Setup vars for VS2019 -set __VSVersion=vs2019 -set __PlatformToolset=v142 -:: Set the environment for the native build -call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -goto FindDIASDK - -:VS2017 -:: Setup vars for VS2017 -set __VSVersion=vs2017 -set __PlatformToolset=v141 -:: Set the environment for the native build -call "%VS150COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -goto FindDIASDK - -:FindDIASDK -if exist "%VSINSTALLDIR%DIA SDK" goto CheckRepoRoot -echo Error: DIA SDK is missing at "%VSINSTALLDIR%DIA SDK". ^ -Did you install all the requirements for building on Windows, including the "Desktop Development with C++" workload? ^ -Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md ^ -Another possibility is that you have a parallel installation of Visual Studio and the DIA SDK is there. In this case it ^ -may help to copy its "DIA SDK" folder into "%VSINSTALLDIR%" manually, then try again. -exit /b 1 - -:CheckRepoRoot - -if exist "%__repoRoot%" goto :FindCMake -:: Can't find repo root -echo Error: variable __repoRoot not set. -exit /b 1 - -:FindCMake -:: Find CMake - -:: Eval the output from set-cmake-path.ps1 -for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "cd "%__repoRoot:"=%"; & eng\native\set-cmake-path.ps1"') do %%a diff --git a/eng/native/init-distro-rid.sh b/eng/native/init-distro-rid.sh index 9c0092a22bce2..f71aa8640b160 100644 --- a/eng/native/init-distro-rid.sh +++ b/eng/native/init-distro-rid.sh @@ -174,8 +174,12 @@ initDistroRidGlobal() distroRid="maccatalyst-$buildArch" elif [ "$targetOs" = "tvOS" ]; then distroRid="tvos-$buildArch" + elif [ "$targetOs" = "tvOSSimulator" ]; then + distroRid="tvossimulator-$buildArch" elif [ "$targetOs" = "iOS" ]; then distroRid="ios-$buildArch" + elif [ "$targetOs" = "iOSSimulator" ]; then + distroRid="iossimulator-$buildArch" elif [ "$targetOs" = "Android" ]; then distroRid="android-$buildArch" elif [ "$targetOs" = "Browser" ]; then diff --git a/eng/native/init-os-and-arch.sh b/eng/native/init-os-and-arch.sh index 1f4893a47ed9b..cedee61031dea 100644 --- a/eng/native/init-os-and-arch.sh +++ b/eng/native/init-os-and-arch.sh @@ -27,17 +27,6 @@ if [ "$os" = "SunOS" ]; then os="Solaris" fi CPUName=$(isainfo -n) -elif [ "$os" = "OSX" ]; then - # On OSX, universal binaries make uname -m unreliable. The uname -m response changes - # based on what hardware is being emulated. - # Use sysctl instead. - if [ "$(sysctl -q -n hw.optional.arm64)" = "1" ]; then - CPUName=arm64 - elif [ "$(sysctl -q -n hw.optional.x86_64)" = "1" ]; then - CPUName=x86_64 - else - CPUName=$(uname -m) - fi else # For the rest of the operating systems, use uname(1) to determine what the CPU is. CPUName=$(uname -m) diff --git a/eng/native/init-vs-env.cmd b/eng/native/init-vs-env.cmd new file mode 100644 index 0000000000000..629ce20aaa7fd --- /dev/null +++ b/eng/native/init-vs-env.cmd @@ -0,0 +1,71 @@ +@if not defined _echo @echo off + +:: Initializes Visual Studio developer environment. If a build architecture is passed +:: as an argument, it also initializes VC++ build environment and CMakePath. + +set "__VCBuildArch=" +if /i "%~1" == "x86" (set __VCBuildArch=x86) +if /i "%~1" == "x64" (set __VCBuildArch=x86_amd64) +if /i "%~1" == "arm" (set __VCBuildArch=x86_arm) +if /i "%~1" == "arm64" (set __VCBuildArch=x86_arm64) +if /i "%~1" == "wasm" (set __VCBuildArch=x86_amd64) + +:: Default to highest Visual Studio version available that has Visual C++ tools. +:: +:: For VS2017 and later, multiple instances can be installed on the same box SxS and VS1*0COMNTOOLS +:: is no longer set as a global environment variable and is instead only set if the user +:: has launched the Visual Studio Developer Command Prompt. +:: +:: Following this logic, we will default to the Visual Studio toolset assocated with the active +:: Developer Command Prompt. Otherwise, we will query VSWhere to locate the later version of +:: Visual Studio available on the machine. Finally, we will fail the script if no supported +:: instance can be found. + +if defined VisualStudioVersion goto :VSDetected + +set "__VSWhere=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" +set "__VSCOMNTOOLS=" + +if exist "%__VSWhere%" ( + for /f "tokens=*" %%p in ( + '"%__VSWhere%" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath' + ) do set __VSCOMNTOOLS=%%p\Common7\Tools +) + +if not exist "%__VSCOMNTOOLS%" goto :VSMissing + +:: Make sure the current directory stays intact +set "VSCMD_START_DIR=%CD%" + +call "%__VSCOMNTOOLS%\VsDevCmd.bat" -no_logo + +:: Clean up helper variables +set "__VSWhere=" +set "__VSCOMNTOOLS=" +set "VSCMD_START_DIR=" + +:VSDetected +if "%VisualStudioVersion%"=="16.0" ( + set __VSVersion=vs2019 + set __PlatformToolset=v142 + goto :SetVCEnvironment +) + +:VSMissing +echo %__MsgPrefix%Error: Visual Studio 2019 with C++ tools required. ^ +Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md for build requirements. +exit /b 1 + +:SetVCEnvironment + +if "%__VCBuildArch%"=="" exit /b 0 + +:: Set the environment for the native build +call "%VCINSTALLDIR%Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% +if not "%ErrorLevel%"=="0" exit /b 1 + +set "__VCBuildArch=" + +:: Set CMakePath by evaluating the output from set-cmake-path.ps1. +:: In case of a failure the output is "exit /b 1". +for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass -File "%~dp0set-cmake-path.ps1"') do %%a diff --git a/eng/native/set-cmake-path.ps1 b/eng/native/set-cmake-path.ps1 index a08d501f40bbf..d83ce5ab5841d 100644 --- a/eng/native/set-cmake-path.ps1 +++ b/eng/native/set-cmake-path.ps1 @@ -1,73 +1,42 @@ -# This file probes for the prerequisites for the build system, and outputs commands for eval'ing -# from the cmd scripts to set variables (and exit on error) +# This script locates the CMake executable for the build system and outputs either the "set CMakePath=..." +# command (if CMake is found) or the "exit /b 1" command (if not found) for evaluating from batch files. -function GetCMakeVersions -{ - $items = @() - $items += @(Get-ChildItem hklm:\SOFTWARE\Wow6432Node\Kitware -ErrorAction SilentlyContinue) - $items += @(Get-ChildItem hklm:\SOFTWARE\Kitware -ErrorAction SilentlyContinue) - return $items | where { $_.PSChildName.StartsWith("CMake") } -} +Set-StrictMode -Version 3 -function GetCMakeInfo($regKey) -{ - try { - $version = [System.Version] $regKey.PSChildName.Split(' ')[1] - } - catch { - return $null - } - $itemProperty = Get-ItemProperty $regKey.PSPath; - if (Get-Member -inputobject $itemProperty -name "InstallDir" -Membertype Properties) { - $cmakeDir = $itemProperty.InstallDir - } - else { - $cmakeDir = $itemProperty.'(default)' +function LocateCMake { + # Find the first cmake.exe on the PATH + $cmakeApp = (Get-Command cmake.exe -ErrorAction SilentlyContinue) + if ($cmakeApp -ne $null) { + return $cmakeApp.Path } - $cmakePath = [System.IO.Path]::Combine($cmakeDir, "artifacts\cmake.exe") - if (![System.IO.File]::Exists($cmakePath)) { - return $null - } - return @{'version' = $version; 'path' = $cmakePath} -} -function LocateCMake -{ - $errorMsg = "CMake is a pre-requisite to build this repository but it was not found on the path. Please install CMake from https://cmake.org/download/ and ensure it is on your path." - $inPathPath = (get-command cmake.exe -ErrorAction SilentlyContinue) - if ($inPathPath -ne $null) { - # Resolve the first version of CMake if multiple commands are found - if ($inPathPath.Length -gt 1) { - return $inPathPath[0].Path - } - return $inPathPath.Path + # Find cmake.exe using the registry + $cmakeRegKey = Get-ItemProperty HKLM:\SOFTWARE\Kitware\CMake -Name InstallDir -ErrorAction SilentlyContinue + if ($cmakeRegKey -eq $null) { + $cmakeRegKey = Get-ItemProperty HKLM:\SOFTWARE\Wow6432Node\Kitware\CMake -Name InstallDir -ErrorAction SilentlyContinue } - # Let us hope that CMake keep using their current version scheme - $validVersions = @() - foreach ($regKey in GetCMakeVersions) { - $info = GetCMakeInfo($regKey) - if ($info -ne $null) { - $validVersions += @($info) + + if ($cmakeRegKey -ne $null) { + $cmakePath = $cmakeRegKey.InstallDir + "bin\cmake.exe" + if (Test-Path $cmakePath -PathType Leaf) { + return $cmakePath } } - $newestCMakePath = ($validVersions | - Sort-Object -property @{Expression={$_.version}; Ascending=$false} | - select -first 1).path - if ($newestCMakePath -eq $null) { - Throw $errorMsg - } - return $newestCMakePath + + return $null } try { $cmakePath = LocateCMake - $version = [Version]$(& $cmakePath --version | Select-String -Pattern '\d+\.\d+\.\d+' | %{$_.Matches.Value}) - if ($version -lt [Version]"3.14.0") { - Throw "This repository requires CMake 3.14 and recommends CMake 3.16. The newest version of CMake installed is $version. Please install CMake 3.16 or newer from https://cmake.org/download/ and ensure it is on your path." + if ($cmakePath -eq $null) { + throw "CMake is a pre-requisite to build this repository but it was not found on the PATH or in the registry. Please install CMake from https://cmake.org/download/." } - elseif ($version -lt [Version]"3.16.0") { - [System.Console]::Error.WriteLine("CMake 3.16 or newer is recommended for building this repository. The newest version of CMake installed is $version. Please install CMake 3.16 or newer from https://cmake.org/download/ and ensure it is on your path.") + + $version = [Version]$(& $cmakePath --version | Select-String -Pattern '\d+\.\d+\.\d+' | %{$_.Matches.Value}) + + if ($version -lt [Version]"3.16.4") { + throw "CMake 3.16.4 or newer is required for building this repository. The newest version of CMake installed is $version. Please install CMake 3.16.4 or newer from https://cmake.org/download/." } [System.Console]::WriteLine("set CMakePath=" + $cmakePath) diff --git a/eng/packaging.props b/eng/packaging.props index 2ecbff672088d..a4da94f4e0f3b 100644 --- a/eng/packaging.props +++ b/eng/packaging.props @@ -46,4 +46,11 @@ + + + + + @(NETCoreApp30RIDs) + + diff --git a/eng/pipelines/common/global-build-job.yml b/eng/pipelines/common/global-build-job.yml index 3baff96b8df14..126cecfacaf05 100644 --- a/eng/pipelines/common/global-build-job.yml +++ b/eng/pipelines/common/global-build-job.yml @@ -86,10 +86,6 @@ jobs: ${{ if ne(parameters.enableRichCodeNavigation, true) }}: value: '' - - ${{ if eq(parameters.osGroup, 'Android') }}: - - name: ANDROID_OPENSSL_AAR - value: /tmp/openssl-android - - ${{ each variable in parameters.variables }}: - ${{ variable }} @@ -108,7 +104,7 @@ jobs: artifact: Mono_Offsets_${{monoCrossAOTTargetOS}} path: '$(Build.SourcesDirectory)/artifacts/obj/mono/offsetfiles' - - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS', 'Android') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: $(Build.SourcesDirectory)/eng/install-native-dependencies.sh ${{ parameters.osGroup }} ${{ parameters.archType }} azDO displayName: Install Build Dependencies @@ -117,6 +113,16 @@ jobs: df -h displayName: Disk Usage before Build + - ${{ if eq(parameters.nameSuffix, 'Browser_wasm_Windows') }}: + # Update machine certs + - task: PowerShell@2 + displayName: Update machine certs + inputs: + filePath: $(Build.SourcesDirectory)/eng/pipelines/mono/update-machine-certs.ps1 + + - script: $(Build.SourcesDirectory)\eng\common\init-tools-native.cmd -InstallDirectory $(Build.SourcesDirectory)\native-tools -Force + displayName: Install native dependencies + # Build - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) ${{ parameters.buildArgs }} $(_officialBuildParameter) $(_crossBuildPropertyArg) $(_cxx11Parameter) $(_richCodeNavigationParam) displayName: Build product diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index cbf5370a09300..000d1dcecdfda 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -188,6 +188,7 @@ jobs: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} + hostedOs: Linux osGroup: Browser archType: wasm targetRid: browser-wasm @@ -203,6 +204,25 @@ jobs: platforms: ${{ parameters.platforms }} ${{ insert }}: ${{ parameters.jobParameters }} +- ${{ if containsValue(parameters.platforms, 'Browser_wasm_win') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + hostedOs: windows + osGroup: Browser + archType: wasm + targetRid: browser-wasm + platform: Browser_wasm + jobParameters: + runtimeFlavor: ${{ parameters.runtimeFlavor }} + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + ${{ insert }}: ${{ parameters.jobParameters }} + # FreeBSD - ${{ if containsValue(parameters.platforms, 'FreeBSD_x64') }}: - template: xplat-setup.yml @@ -365,18 +385,39 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} -# tvOS x64 +# tvOS arm64 -- ${{ if containsValue(parameters.platforms, 'tvOS_x64') }}: +- ${{ if containsValue(parameters.platforms, 'tvOS_arm64') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} osGroup: tvOS + archType: arm64 + targetRid: tvos-arm64 + platform: tvOS_arm64 + jobParameters: + runtimeFlavor: mono + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + +# tvOS Simulator x64 + +- ${{ if containsValue(parameters.platforms, 'tvOSSimulator_x64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: tvOSSimulator archType: x64 - targetRid: tvos-x64 - platform: tvOS_x64 + targetRid: tvossimulator-x64 + platform: tvOSSimulator_x64 jobParameters: runtimeFlavor: mono stagedBuild: ${{ parameters.stagedBuild }} @@ -386,18 +427,18 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} -# tvOS arm64 +# tvOS Simulator arm64 -- ${{ if containsValue(parameters.platforms, 'tvOS_arm64') }}: +- ${{ if containsValue(parameters.platforms, 'tvOSSimulator_arm64') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} - osGroup: tvOS + osGroup: tvOSSimulator archType: arm64 - targetRid: tvos-arm64 - platform: tvOS_arm64 + targetRid: tvossimulator-arm64 + platform: tvOSSimulator_arm64 jobParameters: runtimeFlavor: mono stagedBuild: ${{ parameters.stagedBuild }} @@ -407,18 +448,18 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} -# iOS x64 +# iOS arm -- ${{ if containsValue(parameters.platforms, 'iOS_x64') }}: +- ${{ if containsValue(parameters.platforms, 'iOS_arm') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} osGroup: iOS - archType: x64 - targetRid: ios-x64 - platform: iOS_x64 + archType: arm + targetRid: ios-arm + platform: iOS_arm jobParameters: runtimeFlavor: mono stagedBuild: ${{ parameters.stagedBuild }} @@ -428,18 +469,18 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} -# iOS x86 +# iOS arm64 -- ${{ if containsValue(parameters.platforms, 'iOS_x86') }}: +- ${{ if containsValue(parameters.platforms, 'iOS_arm64') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} osGroup: iOS - archType: x86 - targetRid: ios-x86 - platform: iOS_x86 + archType: arm64 + targetRid: ios-arm64 + platform: iOS_arm64 jobParameters: runtimeFlavor: mono stagedBuild: ${{ parameters.stagedBuild }} @@ -447,21 +488,20 @@ jobs: ${{ if eq(parameters.passPlatforms, true) }}: platforms: ${{ parameters.platforms }} helixQueueGroup: ${{ parameters.helixQueueGroup }} - managedTestBuildOsGroup: OSX ${{ insert }}: ${{ parameters.jobParameters }} -# iOS arm +# iOS Simulator x64 -- ${{ if containsValue(parameters.platforms, 'iOS_arm') }}: +- ${{ if containsValue(parameters.platforms, 'iOSSimulator_x64') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} - osGroup: iOS - archType: arm - targetRid: ios-arm - platform: iOS_arm + osGroup: iOSSimulator + archType: x64 + targetRid: iossimulator-x64 + platform: iOSSimulator_x64 jobParameters: runtimeFlavor: mono stagedBuild: ${{ parameters.stagedBuild }} @@ -471,18 +511,40 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} -# iOS arm64 +# iOS Simulator x86 -- ${{ if containsValue(parameters.platforms, 'iOS_arm64') }}: +- ${{ if containsValue(parameters.platforms, 'iOSSimulator_x86') }}: - template: xplat-setup.yml parameters: jobTemplate: ${{ parameters.jobTemplate }} helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} variables: ${{ parameters.variables }} - osGroup: iOS + osGroup: iOSSimulator + archType: x86 + targetRid: iossimulator-x86 + platform: iOSsimulator_x86 + jobParameters: + runtimeFlavor: mono + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + managedTestBuildOsGroup: OSX + ${{ insert }}: ${{ parameters.jobParameters }} + +# iOS Simulator arm64 + +- ${{ if containsValue(parameters.platforms, 'iOSSimulator_arm64') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: iOSSimulator archType: arm64 - targetRid: ios-arm64 - platform: iOS_arm64 + targetRid: iossimulator-arm64 + platform: iOSSimulator_arm64 jobParameters: runtimeFlavor: mono stagedBuild: ${{ parameters.stagedBuild }} diff --git a/eng/pipelines/common/templates/runtimes/build-test-job.yml b/eng/pipelines/common/templates/runtimes/build-test-job.yml index 14d17b0849b5b..50de94ee79c7d 100644 --- a/eng/pipelines/common/templates/runtimes/build-test-job.yml +++ b/eng/pipelines/common/templates/runtimes/build-test-job.yml @@ -116,7 +116,7 @@ jobs: artifactName: '$(coreClrProductArtifactName)' displayName: 'CoreCLR product build' - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h @@ -126,7 +126,7 @@ jobs: - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) allTargets skipnative skipgeneratelayout skiptestwrappers $(buildConfig) $(archType) $(crossArg) $(priorityArg) ci $(librariesOverrideArg) displayName: Build managed test components - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml index 54436b52f7a52..1c3554510aed9 100644 --- a/eng/pipelines/common/templates/runtimes/run-test-job.yml +++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml @@ -454,7 +454,7 @@ jobs: - jitstressregs0x1000 - jitminopts - forcerelocs - - gcstress15 + - gcstress0xf ${{ if in(parameters.testGroup, 'gc-longrunning') }}: longRunningGcTests: true scenarios: diff --git a/eng/pipelines/common/xplat-setup.yml b/eng/pipelines/common/xplat-setup.yml index 5245e0314a82b..6fc1b7b746a2e 100644 --- a/eng/pipelines/common/xplat-setup.yml +++ b/eng/pipelines/common/xplat-setup.yml @@ -1,5 +1,6 @@ parameters: jobTemplate: '' + hostedOs: '' osGroup: '' osSubgroup: '' archType: '' @@ -47,7 +48,7 @@ jobs: - name: _BuildConfig value: $(buildConfigUpper) - - ${{ if eq(parameters.osGroup, 'windows') }}: + - ${{ if or(eq(parameters.osGroup, 'windows'), eq(parameters.hostedOs, 'windows')) }}: - name: archiveExtension value: '.zip' - name: archiveType @@ -63,7 +64,7 @@ jobs: - name: setScriptToEchoAndFailOnNonZero value: '' - - ${{ if ne(parameters.osGroup, 'windows') }}: + - ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.hostedOs, 'windows')) }}: - name: archiveExtension value: '.tar.gz' - name: archiveType @@ -109,17 +110,17 @@ jobs: ${{ if eq(parameters.jobParameters.pool, '') }}: pool: # Public Linux Build Pool - ${{ if and(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Browser', 'Android'), eq(variables['System.TeamProject'], 'public')) }}: + ${{ if and(or(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Android'), eq(parameters.hostedOs, 'Linux')), eq(variables['System.TeamProject'], 'public')) }}: name: NetCorePublic-Pool queue: BuildPool.Ubuntu.1604.Amd64.Open # Official Build Linux Pool - ${{ if and(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Browser', 'Android'), ne(variables['System.TeamProject'], 'public')) }}: + ${{ if and(or(in(parameters.osGroup, 'Linux', 'FreeBSD', 'Browser', 'Android'), eq(parameters.hostedOs, 'Linux')), ne(variables['System.TeamProject'], 'public')) }}: name: NetCoreInternal-Pool queue: BuildPool.Ubuntu.1604.Amd64 # OSX Build Pool (we don't have on-prem OSX BuildPool - ${{ if in(parameters.osGroup, 'OSX', 'MacCatalyst', 'iOS', 'tvOS') }}: + ${{ if in(parameters.osGroup, 'OSX', 'MacCatalyst', 'iOS', 'iOSSimulator', 'tvOS', 'tvOSSimulator') }}: vmImage: 'macOS-10.15' # Official Build Windows Pool @@ -128,7 +129,7 @@ jobs: queue: BuildPool.Windows.10.Amd64.VS2019 # Public Windows Build Pool - ${{ if and(eq(parameters.osGroup, 'windows'), eq(variables['System.TeamProject'], 'public')) }}: + ${{ if and(or(eq(parameters.osGroup, 'windows'), eq(parameters.hostedOs, 'windows')), eq(variables['System.TeamProject'], 'public')) }}: name: NetCorePublic-Pool queue: BuildPool.Windows.10.Amd64.VS2019.Open diff --git a/eng/pipelines/coreclr/gcstress-extra.yml b/eng/pipelines/coreclr/gcstress-extra.yml index 4bd0adcbba92a..ab867202f80a7 100644 --- a/eng/pipelines/coreclr/gcstress-extra.yml +++ b/eng/pipelines/coreclr/gcstress-extra.yml @@ -16,6 +16,9 @@ jobs: buildConfig: checked platformGroup: gcstress platforms: + # It is too early to include OSX_arm64 in platform group gcstress + # Adding it here will enable it also + - OSX_arm64 - CoreClrTestBuildHost # Either OSX_x64 or Linux_x64 jobParameters: testGroup: gcstress-extra @@ -35,6 +38,10 @@ jobs: jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml buildConfig: checked platformGroup: gcstress + platforms: + # It is too early to include OSX_arm64 in platform group gcstress + # Adding it here will enable it also + - OSX_arm64 helixQueueGroup: ci helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml jobParameters: diff --git a/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml b/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml index c2f8a86448b22..436deaca79677 100644 --- a/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml +++ b/eng/pipelines/coreclr/gcstress0x3-gcstress0xc.yml @@ -16,6 +16,9 @@ jobs: buildConfig: checked platformGroup: gcstress platforms: + # It is too early to include OSX_arm64 in platform group gcstress + # Adding it here will enable it also + - OSX_arm64 - CoreClrTestBuildHost # Either OSX_x64 or Linux_x64 jobParameters: testGroup: gcstress0x3-gcstress0xc @@ -35,6 +38,10 @@ jobs: jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml buildConfig: checked platformGroup: gcstress + platforms: + # It is too early to include OSX_arm64 in platform group gcstress + # Adding it here will enable it also + - OSX_arm64 helixQueueGroup: ci helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml jobParameters: diff --git a/eng/pipelines/coreclr/perf.yml b/eng/pipelines/coreclr/perf.yml index 879ff40c57d73..2f71e622cfcfa 100644 --- a/eng/pipelines/coreclr/perf.yml +++ b/eng/pipelines/coreclr/perf.yml @@ -109,6 +109,72 @@ jobs: archiveType: zip archiveExtension: .zip + # build mono for AOT + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Linux_x64 + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: AOT + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/common/upload-artifact-step.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: AOT Mono Artifacts + artifactName: LinuxMonoAOTx64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Linux_arm64 + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: AOT + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/common/upload-artifact-step.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: AOT Mono Artifacts + artifactName: LinuxMonoAOTarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + + # build mono Android scenarios + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Android_arm64 + jobParameters: + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + nameSuffix: AndroidMono + isOfficialBuild: false + extraStepsTemplate: /eng/pipelines/coreclr/templates/build-perf-sample-apps.yml + extraStepsParameters: + rootFolder: '$(Build.SourcesDirectory)/artifacts/' + includeRootFolder: true + displayName: Android Mono Artifacts + artifactName: AndroidMonoarm64 + archiveExtension: '.tar.gz' + archiveType: tar + tarCompression: gz + + # build mono - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -118,6 +184,22 @@ jobs: platforms: - Linux_x64 + # run mono android scenarios + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml + buildConfig: release + runtimeFlavor: mono + platforms: + - Windows_x64 + jobParameters: + testGroup: perf + runtimeType: AndroidMono + projectFile: android_scenarios.proj + runKind: android_scenarios + runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml + logicalmachine: 'perfpixel4a' + # run mono microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -153,7 +235,7 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml logicalmachine: 'perftiger' - # run mono wasm microbenchmarks perf job + #run mono wasm microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml parameters: jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml # NOTE: should we move this file out of coreclr tempelates because it contains mono jobs? @@ -171,6 +253,24 @@ jobs: runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml logicalmachine: 'perftiger' + # run mono aot microbenchmarks perf job + - template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/perf-job.yml # NOTE: should we move this file out of coreclr tempelates because it contains mono jobs? + buildConfig: release + runtimeFlavor: aot + platforms: + - Linux_x64 + - Linux_arm64 + jobParameters: + testGroup: perf + liveLibrariesBuildConfig: Release + runtimeType: mono + codeGenType: 'AOT' + projectFile: microbenchmarks.proj + runKind: micro_mono + runJobTemplate: /eng/pipelines/coreclr/templates/run-performance-job.yml + logicalmachine: 'perftiger' # run coreclr perftiger microbenchmarks perf job - template: /eng/pipelines/common/platform-matrix.yml diff --git a/eng/pipelines/coreclr/r2r-extra.yml b/eng/pipelines/coreclr/r2r-extra.yml index eee93c9af341c..236fc090dddc0 100644 --- a/eng/pipelines/coreclr/r2r-extra.yml +++ b/eng/pipelines/coreclr/r2r-extra.yml @@ -34,7 +34,7 @@ jobs: parameters: jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml buildConfig: checked - platformGroup: gcstress # r2r-extra testGroup runs gcstress15 scenario + platformGroup: gcstress # r2r-extra testGroup runs gcstress0xf scenario helixQueueGroup: ci helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml jobParameters: diff --git a/eng/pipelines/coreclr/superpmi.yml b/eng/pipelines/coreclr/superpmi.yml index f581f69bbf649..cf670f3d17b9d 100644 --- a/eng/pipelines/coreclr/superpmi.yml +++ b/eng/pipelines/coreclr/superpmi.yml @@ -123,10 +123,9 @@ jobs: platforms: # Linux tests are built on the OSX machines. # - OSX_x64 - # TODO: Linux crossgen2 jobs crash during collection, and need to be investigated. - # - Linux_arm - # - Linux_arm64 - # - Linux_x64 + - Linux_arm + - Linux_arm64 + - Linux_x64 - windows_x64 - windows_x86 - windows_arm64 diff --git a/eng/pipelines/coreclr/templates/build-job.yml b/eng/pipelines/coreclr/templates/build-job.yml index 349cdfd0bb215..3727b277dcd85 100644 --- a/eng/pipelines/coreclr/templates/build-job.yml +++ b/eng/pipelines/coreclr/templates/build-job.yml @@ -130,13 +130,7 @@ jobs: value: '' - ${{ if ne(parameters.testGroup, 'innerloop') }}: - name: clrBuildPALTestsBuildArg - value: '-paltests ' - - - name: ninjaArg - value: '' - - ${{ if eq(parameters.osGroup, 'windows') }}: - - name: ninjaArg - value: '-ninja' + value: '-component runtime -component alljits -component paltests ' - name: pgoInstrumentArg value: '' @@ -181,7 +175,7 @@ jobs: continueOnError: false condition: and(succeeded(), in(variables['SignType'], 'real', 'test')) - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h @@ -194,13 +188,13 @@ jobs: # Build CoreCLR Runtime - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: $(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) $(crossArg) $(osArg) -ci $(compilerArg) $(clrBuildPALTestsBuildArg) $(ninjaArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) + - script: $(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) $(crossArg) $(osArg) -ci $(compilerArg) $(clrBuildPALTestsBuildArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) displayName: Build CoreCLR Runtime - ${{ if eq(parameters.osGroup, 'windows') }}: - - script: set __TestIntermediateDir=int&&$(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) -ci $(ninjaArg) $(enforcePgoArg) $(pgoInstrumentArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) + - script: set __TestIntermediateDir=int&&$(Build.SourcesDirectory)/src/coreclr/build-runtime$(scriptExt) $(buildConfig) $(archType) -ci $(enforcePgoArg) $(pgoInstrumentArg) $(officialBuildIdArg) $(clrInterpreterBuildArg) displayName: Build CoreCLR Runtime - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h diff --git a/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml new file mode 100644 index 0000000000000..2b556af6d911b --- /dev/null +++ b/eng/pipelines/coreclr/templates/build-perf-sample-apps.yml @@ -0,0 +1,45 @@ +parameters: + osGroup: '' + osSubgroup: '' + archType: '' + buildConfig: '' + runtimeFlavor: '' + helixQueues: '' + targetRid: '' + nameSuffix: '' + platform: '' + shouldContinueOnError: '' + rootFolder: '' + includeRootFolder: '' + displayName: '' + artifactName: '' + archiveExtension: '' + archiveType: '' + tarCompression: '' + +steps: +# Build Android sample app + - ${{ if eq(parameters.osGroup, 'Android') }}: + - script: make run MONO_ARCH=arm64 DEPLOY_AND_RUN=false + workingDirectory: $(Build.SourcesDirectory)/src/mono/sample/Android + displayName: Build HelloAndroid sample app + + - template: /eng/pipelines/common/upload-artifact-step.yml + parameters: + osGroup: ${{ parameters.osGroup }} + osSubgroup: ${{ parameters.osSubgroup }} + archType: ${{ parameters.archType }} + buildConfig: ${{ parameters.buildConfig }} + runtimeFlavor: ${{ parameters.runtimeFlavor }} + helixQueues: ${{ parameters.helixQueues }} + targetRid: ${{ parameters.targetRid }} + nameSuffix: ${{ parameters.nameSuffix }} + platform: ${{ parameters.platform }} + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + rootFolder: ${{ parameters.rootFolder }} + includeRootFolder: ${{ parameters.includeRootFolder }} + displayName: ${{ parameters.displayName }} + artifactName: ${{ parameters.artifactName }} + archiveExtension: ${{ parameters.archiveExtension }} + archiveType: ${{ parameters.archiveType }} + tarCompression: ${{ parameters.tarCompression }} \ No newline at end of file diff --git a/eng/pipelines/coreclr/templates/perf-job.yml b/eng/pipelines/coreclr/templates/perf-job.yml index ef7bf235269f0..d208f362fac02 100644 --- a/eng/pipelines/coreclr/templates/perf-job.yml +++ b/eng/pipelines/coreclr/templates/perf-job.yml @@ -11,7 +11,7 @@ parameters: runtimeType: 'coreclr' pool: '' codeGenType: 'JIT' - projetFile: '' + projectFile: '' runKind: '' runJobTemplate: '/eng/pipelines/coreclr/templates/run-performance-job.yml' additionalSetupParameters: '' @@ -44,29 +44,38 @@ jobs: logicalmachine: ${{ parameters.logicalmachine }} # Test job depends on the corresponding build job dependsOn: - - ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} + - ${{ if ne(parameters.runtimeType, 'AndroidMono')}}: + - ${{ format('coreclr_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - ${{ if ne(parameters.liveLibrariesBuildConfig, '') }}: - ${{ format('libraries_build_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.liveLibrariesBuildConfig) }} - - ${{ if eq(parameters.runtimeType, 'mono') }}: + - ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: - ${{ format('mono_{0}_product_build_{1}{2}_{3}_{4}', parameters.runtimeVariant, parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - ${{ if eq(parameters.runtimeType, 'wasm') }}: - ${{ format('build_{0}{1}_{2}_{3}_{4}', 'Browser', '', 'wasm', parameters.buildConfig, parameters.runtimeType) }} + - ${{ if eq(parameters.codeGenType, 'AOT')}}: + - ${{ format('build_{0}{1}_{2}_{3}_{4}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig, parameters.codeGenType) }} + - ${{ if eq(parameters.runtimeType, 'AndroidMono')}}: + - ${{ 'build_Android_arm64_release_AndroidMono' }} - ${{ if eq(parameters.osGroup, 'windows') }}: + ${{ if and(eq(parameters.osGroup, 'windows'), ne(parameters.runtimeType, 'AndroidMono')) }}: ${{ if eq(parameters.runtimeType, 'mono') }}: extraSetupParameters: -Architecture ${{ parameters.archType }} -MonoDotnet $(Build.SourcesDirectory)\.dotnet-mono ${{ if eq(parameters.runtimeType, 'coreclr') }}: extraSetupParameters: -CoreRootDirectory $(Build.SourcesDirectory)\artifacts\tests\coreclr\${{ parameters.osGroup }}.${{ parameters.archType }}.Release\Tests\Core_Root -Architecture ${{ parameters.archType }} - ${{ if ne(parameters.osGroup, 'windows') }}: - ${{ if eq(parameters.runtimeType, 'mono') }}: + ${{ if and(ne(parameters.osGroup, 'windows'), ne(parameters.runtimeType, 'AndroidMono')) }}: + ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: extraSetupParameters: --architecture ${{ parameters.archType }} --monodotnet $(Build.SourcesDirectory)/.dotnet-mono ${{ if eq(parameters.runtimeType, 'wasm') }}: extraSetupParameters: --architecture ${{ parameters.archType }} --wasm $(librariesDownloadDir)/bin/wasm + ${{ if eq(parameters.codeGenType, 'AOT') }}: + extraSetupParameters: --architecture ${{ parameters.archType }} --monoaot $(librariesDownloadDir)/bin/aot ${{ if and(eq(parameters.runtimeType, 'coreclr'), ne(parameters.osSubGroup, '_musl')) }}: extraSetupParameters: --corerootdirectory $(Build.SourcesDirectory)/artifacts/tests/coreclr/${{ parameters.osGroup }}.${{ parameters.archType }}.Release/Tests/Core_Root --architecture ${{ parameters.archType }} ${{ if and(eq(parameters.runtimeType, 'coreclr'), eq(parameters.osSubGroup, '_musl')) }}: extraSetupParameters: --corerootdirectory $(Build.SourcesDirectory)/artifacts/tests/coreclr/${{ parameters.osGroup }}.${{ parameters.archType }}.Release/Tests/Core_Root --architecture ${{ parameters.archType }} --alpine - + ${{ if eq(parameters.runtimeType, 'AndroidMono') }}: + extraSetupParameters: -Architecture ${{ parameters.archType }} -AndroidMono + variables: ${{ parameters.variables }} frameworks: @@ -85,15 +94,16 @@ jobs: displayName: 'live-built libraries' # Download coreclr - - template: /eng/pipelines/common/download-artifact-step.yml - parameters: - unpackFolder: $(buildProductRootFolderPath) - artifactFileName: '$(buildProductArtifactName)$(archiveExtension)' - artifactName: '$(buildProductArtifactName)' - displayName: 'Coreclr product build' + - ${{ if ne(parameters.runtimeType, 'AndroidMono') }}: + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(buildProductRootFolderPath) + artifactFileName: '$(buildProductArtifactName)$(archiveExtension)' + artifactName: '$(buildProductArtifactName)' + displayName: 'Coreclr product build' # Download mono - - ${{ if eq(parameters.runtimeType, 'mono') }}: + - ${{ if and(eq(parameters.runtimeType, 'mono'), ne(parameters.codeGenType, 'AOT')) }}: - template: /eng/pipelines/common/download-artifact-step.yml parameters: unpackFolder: $(librariesDownloadDir)/bin/mono/$(osGroup).$(archType).$(buildConfigUpper) @@ -114,6 +124,29 @@ jobs: - script: "mkdir $(librariesDownloadDir)/bin/wasm;unzip -o $(librariesDownloadDir)/BrowserWasm/artifacts/packages/Release/Shipping/Microsoft.NETCore.App.Runtime.browser-wasm.6.0.0-ci.nupkg data/* runtimes/* -d $(librariesDownloadDir)/bin/wasm;cp src/mono/wasm/runtime-test.js $(librariesDownloadDir)/bin/wasm/runtime-test.js;find $(librariesDownloadDir)/bin/wasm -type f -exec chmod 664 {} \\;" displayName: "Create wasm directory (Linux)" + # Download AOT + - ${{ if eq(parameters.codeGenType, 'AOT') }}: + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(librariesDownloadDir)/LinuxMonoAOT + artifactFileName: LinuxMonoAOT${{ parameters.archType }}.tar.gz + artifactName: LinuxMonoAOT${{ parameters.archType }} + displayName: AOT Mono Artifacts + + - script: "mkdir -p $(librariesDownloadDir)/bin/aot/sgen;mkdir -p $(librariesDownloadDir)/bin/aot/pack;cp -r $(librariesDownloadDir)/LinuxMonoAOT/artifacts/obj/mono/Linux.${{ parameters.archType }}.Release/mono/* $(librariesDownloadDir)/bin/aot/sgen;cp -r $(librariesDownloadDir)/LinuxMonoAOT/artifacts/bin/microsoft.netcore.app.runtime.linux-${{ parameters.archType }}/Release/* $(librariesDownloadDir)/bin/aot/pack" + displayName: "Create aot directory (Linux)" + + # Download AndroidMono + - ${{ if eq(parameters.runtimeType, 'AndroidMono')}}: + - template: /eng/pipelines/common/download-artifact-step.yml + parameters: + unpackFolder: $(Build.SourcesDirectory) + cleanUnpackFolder: false + artifactFileName: 'AndroidMonoarm64.tar.gz' + artifactName: 'AndroidMonoarm64' + displayName: 'Mono Android runtime' + + # Create Core_Root - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(buildConfig) $(archType) generatelayoutonly $(librariesOverrideArg) displayName: Create Core_Root @@ -122,8 +155,8 @@ jobs: # Copy the runtime directory into the testhost folder to include OOBs. - script: "build.cmd -subset libs.pretest -configuration release -ci -arch $(archType) -testscope innerloop /p:RuntimeArtifactsPath=$(librariesDownloadDir)\\bin\\mono\\$(osGroup).$(archType).$(buildConfigUpper) /p:RuntimeFlavor=mono;xcopy $(Build.SourcesDirectory)\\artifacts\\bin\\runtime\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\* $(Build.SourcesDirectory)\\artifacts\\bin\\testhost\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\shared\\Microsoft.NETCore.App\\6.0.0 /E /I /Y;xcopy $(Build.SourcesDirectory)\\artifacts\\bin\\testhost\\$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)\\* $(Build.SourcesDirectory)\\.dotnet-mono /E /I /Y;copy $(Build.SourcesDirectory)\\artifacts\\bin\\coreclr\\$(osGroup).$(archType).$(buildConfigUpper)\\corerun.exe $(Build.SourcesDirectory)\\.dotnet-mono\\shared\\Microsoft.NETCore.App\\6.0.0\\corerun.exe" displayName: "Create mono dotnet (Windows)" - condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), eq(variables.osGroup, 'windows')) + condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), eq(variables.osGroup, 'windows'), ne('${{ parameters.runtimeType }}', 'AndroidMono')) - script: "mkdir $(Build.SourcesDirectory)/.dotnet-mono;./build.sh -subset libs.pretest -configuration release -ci -arch $(archType) -testscope innerloop /p:RuntimeArtifactsPath=$(librariesDownloadDir)/bin/mono/$(osGroup).$(archType).$(buildConfigUpper) /p:RuntimeFlavor=mono;cp $(Build.SourcesDirectory)/artifacts/bin/runtime/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/* $(Build.SourcesDirectory)/artifacts/bin/testhost/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/shared/Microsoft.NETCore.App/6.0.0 -rf;cp $(Build.SourcesDirectory)/artifacts/bin/testhost/$(_Framework)-$(osGroup)-$(buildConfigUpper)-$(archType)/* $(Build.SourcesDirectory)/.dotnet-mono -r;cp $(Build.SourcesDirectory)/artifacts/bin/coreclr/$(osGroup).$(archType).$(buildConfigUpper)/corerun $(Build.SourcesDirectory)/.dotnet-mono/shared/Microsoft.NETCore.App/6.0.0/corerun" displayName: "Create mono dotnet (Linux)" - condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), ne(variables.osGroup, 'windows')) + condition: and(and(succeeded(), eq(variables.runtimeFlavorName, 'Mono')), ne(variables.osGroup, 'windows'), ne('${{ parameters.runtimeType }}', 'AndroidMono')) diff --git a/eng/common/templates/steps/perf-send-to-helix.yml b/eng/pipelines/coreclr/templates/perf-send-to-helix.yml similarity index 94% rename from eng/common/templates/steps/perf-send-to-helix.yml rename to eng/pipelines/coreclr/templates/perf-send-to-helix.yml index 3427b311a7283..a6f4636ce03df 100644 --- a/eng/common/templates/steps/perf-send-to-helix.yml +++ b/eng/pipelines/coreclr/templates/perf-send-to-helix.yml @@ -26,10 +26,10 @@ steps: - template: /eng/pipelines/common/templates/runtimes/send-to-helix-inner-step.yml parameters: osGroup: ${{ parameters.osGroup }} - sendParams: $(Build.SourcesDirectory)/eng/common/performance/${{ parameters.ProjectFile }} /restore /t:Test /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog + sendParams: $(Build.SourcesDirectory)/eng/testing/performance/${{ parameters.ProjectFile }} /restore /t:Test /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog displayName: ${{ parameters.DisplayNamePrefix }} condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} + shouldContinueOnError: ${{ parameters.continueOnError }} environment: BuildConfig: $(_BuildConfig) HelixSource: ${{ parameters.HelixSource }} diff --git a/eng/pipelines/coreclr/templates/run-performance-job.yml b/eng/pipelines/coreclr/templates/run-performance-job.yml index 4a8c44e6a6f3c..1de4d300920ec 100644 --- a/eng/pipelines/coreclr/templates/run-performance-job.yml +++ b/eng/pipelines/coreclr/templates/run-performance-job.yml @@ -111,18 +111,18 @@ jobs: _Framework: ${{ framework }} steps: - ${{ parameters.steps }} - - powershell: $(Build.SourcesDirectory)\eng\common\performance\performance-setup.ps1 $(IsInternal)$(Interpreter) -Framework $(_Framework) -Kind ${{ parameters.runKind }} -LogicalMachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} + - powershell: $(Build.SourcesDirectory)\eng\testing\performance\performance-setup.ps1 $(IsInternal)$(Interpreter) -Framework $(_Framework) -Kind ${{ parameters.runKind }} -LogicalMachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} displayName: Performance Setup (Windows) condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} - - script: $(Build.SourcesDirectory)/eng/common/performance/performance-setup.sh $(IsInternal)$(Interpreter) --framework $(_Framework) --kind ${{ parameters.runKind }} --logicalmachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} + - script: $(Build.SourcesDirectory)/eng/testing/performance/performance-setup.sh $(IsInternal)$(Interpreter) --framework $(_Framework) --kind ${{ parameters.runKind }} --logicalmachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} displayName: Performance Setup (Unix) condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} - script: $(Python) $(PerformanceDirectory)/scripts/ci_setup.py $(SetupArguments) displayName: Run ci setup script # Run perf testing in helix - - template: /eng/common/templates/steps/perf-send-to-helix.yml + - template: /eng/pipelines/coreclr/templates/perf-send-to-helix.yml parameters: HelixSource: '$(HelixSourcePrefix)/$(Build.Repository.Name)/$(Build.SourceBranch)' # sources must start with pr/, official/, prodcon/, or agent/ HelixType: 'test/performance/$(Kind)/$(_Framework)/$(Architecture)' diff --git a/eng/pipelines/coreclr/templates/run-scenarios-job.yml b/eng/pipelines/coreclr/templates/run-scenarios-job.yml index 9b5cb92043fb7..075807453ec3d 100644 --- a/eng/pipelines/coreclr/templates/run-scenarios-job.yml +++ b/eng/pipelines/coreclr/templates/run-scenarios-job.yml @@ -88,11 +88,11 @@ jobs: steps: - ${{ parameters.steps }} # run performance-setup - - powershell: $(Build.SourcesDirectory)\eng\common\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) -Kind ${{ parameters.runKind }} -LogicalMachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} ${{ parameters.additionalSetupParameters }} + - powershell: $(Build.SourcesDirectory)\eng\testing\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) -Kind ${{ parameters.runKind }} -LogicalMachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} ${{ parameters.additionalSetupParameters }} displayName: Performance Setup (Windows) condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} - - script: $(Build.SourcesDirectory)/eng/common/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) --kind ${{ parameters.runKind }} --logicalmachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} ${{ parameters.additionalSetupParameters }} + - script: $(Build.SourcesDirectory)/eng/testing/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) --kind ${{ parameters.runKind }} --logicalmachine ${{ parameters.logicalMachine }} ${{ parameters.extraSetupParameters }} ${{ parameters.additionalSetupParameters }} displayName: Performance Setup (Linux) condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} @@ -138,7 +138,7 @@ jobs: condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) # run perf testing in helix - - template: /eng/common/templates/steps/perf-send-to-helix.yml + - template: /eng/pipelines/coreclr/templates/perf-send-to-helix.yml parameters: HelixSource: '$(HelixSourcePrefix)/$(Build.Repository.Name)/$(Build.SourceBranch)' # sources must start with pr/, official/, prodcon/, or agent/ HelixType: 'test/performance/$(Kind)/$(_Framework)/$(Architecture)' diff --git a/eng/pipelines/global-build.yml b/eng/pipelines/global-build.yml index c7dd786be0bcf..3a737e69c5213 100644 --- a/eng/pipelines/global-build.yml +++ b/eng/pipelines/global-build.yml @@ -36,7 +36,6 @@ jobs: parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml buildConfig: release - timeoutInMinutes: 90 platforms: - windows_x86 - OSX_x64 @@ -44,6 +43,22 @@ jobs: testGroup: innerloop nameSuffix: Runtime_Debug buildArgs: -c release -runtimeConfiguration debug + timeoutInMinutes: 90 + +# +# Build with Release config and runtimeConfiguration with MSBuild generator +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + platforms: + - windows_x86 + jobParameters: + testGroup: innerloop + nameSuffix: MSBuild_CMake + buildArgs: -c Release -msbuild + timeoutInMinutes: 90 # # Build with Debug config and Release runtimeConfiguration @@ -52,29 +67,29 @@ jobs: parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml buildConfig: debug - timeoutInMinutes: 90 platforms: - Linux_x64 jobParameters: testGroup: innerloop nameSuffix: Runtime_Release buildArgs: -c debug -runtimeConfiguration release + timeoutInMinutes: 90 # -# Build with RuntimeFlavor only. This excercise code paths where only Configuration is +# Build with RuntimeFlavor only. This excercise code paths where only RuntimeFlavor is # specified. Catches cases where we depend on Configuration also being specified # - template: /eng/pipelines/common/platform-matrix.yml parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml buildConfig: debug - timeoutInMinutes: 90 platforms: - Linux_x64 jobParameters: testGroup: innerloop nameSuffix: RuntimeFlavor_Mono buildArgs: /p:RuntimeFlavor=Mono + timeoutInMinutes: 90 # # Build Mono + Libraries. This excercises the code path where we build libraries without @@ -84,13 +99,28 @@ jobs: parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml buildConfig: debug - timeoutInMinutes: 90 platforms: - windows_x64 jobParameters: testGroup: innerloop nameSuffix: Mono_Libraries buildArgs: -subset mono+libs /p:RuntimeFlavor=Mono + timeoutInMinutes: 90 + +# +# Build Libraries AllConfigurations. This exercises the code path where we build libraries for all +# configurations on a non Windows operating system. +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: debug + platforms: + - Linux_x64 + jobParameters: + nameSuffix: Libraries_AllConfigurations + buildArgs: -subset libs -allconfigurations + timeoutInMinutes: 90 # # SourceBuild Build @@ -99,9 +129,9 @@ jobs: parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml buildConfig: release - timeoutInMinutes: 90 platforms: - Linux_x64 jobParameters: nameSuffix: SourceBuild buildArgs: /p:DotNetBuildFromSource=true + timeoutInMinutes: 90 diff --git a/eng/pipelines/installer/jobs/base-job.yml b/eng/pipelines/installer/jobs/base-job.yml index 5941160fbdb32..706774485917b 100644 --- a/eng/pipelines/installer/jobs/base-job.yml +++ b/eng/pipelines/installer/jobs/base-job.yml @@ -415,7 +415,7 @@ jobs: displayName: Build and Package continueOnError: ${{ and(eq(variables.SkipTests, false), eq(parameters.shouldContinueOnError, true)) }} - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h diff --git a/eng/pipelines/libraries/build-job.yml b/eng/pipelines/libraries/build-job.yml index 9d26ad7a4af06..ee0c0fc938f69 100644 --- a/eng/pipelines/libraries/build-job.yml +++ b/eng/pipelines/libraries/build-job.yml @@ -96,7 +96,7 @@ jobs: $(_additionalBuildArguments) displayName: Restore and Build Product - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h diff --git a/eng/pipelines/libraries/execute-trimming-tests-steps.yml b/eng/pipelines/libraries/execute-trimming-tests-steps.yml index 183cebbcf83bd..a0172e2363428 100644 --- a/eng/pipelines/libraries/execute-trimming-tests-steps.yml +++ b/eng/pipelines/libraries/execute-trimming-tests-steps.yml @@ -1,7 +1,8 @@ parameters: archType: '' + extraTestArgs: '' steps: # Execute tests - - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) -s libs.tests -c $(_BuildConfig) /p:TestAssemblies=false /p:TestTrimming=true $(_officialBuildParameter) $(_crossBuildPropertyArg) /bl:$(Build.SourcesDirectory)/artifacts/log/$(buildConfigUpper)/TrimmingTests.binlog + - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -arch ${{ parameters.archType }} $(_osParameter) -s libs.tests -c $(_BuildConfig) /p:TestAssemblies=false /p:TestTrimming=true $(_officialBuildParameter) $(_crossBuildPropertyArg) /bl:$(Build.SourcesDirectory)/artifacts/log/$(buildConfigUpper)/TrimmingTests.binlog ${{ parameters.extraTestArgs }} displayName: Run Trimming Tests diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 7f66f5b0b6171..a54f7ff17da50 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -52,7 +52,7 @@ jobs: # Linux x64 - ${{ if eq(parameters.platform, 'Linux_x64') }}: - - ${{ if eq(parameters.jobParameters.interpreter, '') }}: + - ${{ if and(eq(parameters.jobParameters.interpreter, ''), ne(parameters.jobParameters.isSingleFile, true)) }}: - ${{ if and(eq(parameters.jobParameters.testScope, 'outerloop'), eq(parameters.jobParameters.runtimeFlavor, 'mono')) }}: - (Centos.8.Amd64.Open)Ubuntu.1604.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:centos-8-helix-20201229003624-c1bf759 - RedHat.7.Amd64.Open @@ -81,7 +81,7 @@ jobs: - Ubuntu.1804.Amd64.Open - SLES.15.Amd64.Open - (Fedora.30.Amd64.Open)ubuntu.1604.amd64.open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-30-helix-20200512010621-4f8cef7 - - ${{ if eq(parameters.jobParameters.interpreter, 'true') }}: + - ${{ if or(eq(parameters.jobParameters.interpreter, 'true'), eq(parameters.jobParameters.isSingleFile, true)) }}: # Limiting interp runs as we don't need as much coverage. - Debian.9.Amd64.Open @@ -105,10 +105,18 @@ jobs: - ${{ if in(parameters.platform, 'Android_arm', 'Android_arm64') }}: - Windows.10.Amd64.Android.Open - # iOS x64/x86 - - ${{ if in(parameters.platform, 'iOS_x64', 'iOS_x86') }}: + # iOS/tvOS simulator x64/x86 + - ${{ if in(parameters.platform, 'iOSSimulator_x64', 'iOSSimulator_x86', 'tvOSSimulator_x64') }}: - OSX.1015.Amd64.Open + # iOS devices + - ${{ if in(parameters.platform, 'iOS_arm64') }}: + - OSX.1015.Amd64.Iphone.Open + + # tvOS devices + - ${{ if in(parameters.platform, 'tvOS_arm64') }}: + - OSX.1015.Amd64.AppleTV.Open + # windows x64 - ${{ if eq(parameters.platform, 'windows_x64') }}: # netcoreapp @@ -124,7 +132,7 @@ jobs: - ${{ if ne(parameters.jobParameters.runtimeFlavor, 'mono') }}: - (Windows.Nano.1809.Amd64.Open)windows.10.amd64.serverrs5.open@mcr.microsoft.com/dotnet-buildtools/prereqs:nanoserver-1809-helix-amd64-08e8e40-20200107182504 - (Windows.Server.Core.1909.Amd64.Open)windows.10.amd64.server20h1.open@mcr.microsoft.com/dotnet-buildtools/prereqs:windowsservercore-2004-helix-amd64-20200904200251-272704c - - ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}: + - ${{ if ne(parameters.jobParameters.isFullMatrix, true) }}: - Windows.81.Amd64.Open - Windows.10.Amd64.Server19H1.ES.Open - ${{ if eq(parameters.jobParameters.testScope, 'outerloop') }}: @@ -135,7 +143,7 @@ jobs: # .NETFramework - ${{ if eq(parameters.jobParameters.framework, 'net48') }}: - Windows.10.Amd64.Client19H1.Open - + # AllConfigurations - ${{ if eq(parameters.jobParameters.framework, 'allConfigurations') }}: - Windows.10.Amd64.Server19H1.Open @@ -152,7 +160,7 @@ jobs: - Windows.7.Amd64.Open - Windows.10.Amd64.ServerRS5.Open - Windows.10.Amd64.Server19H1.Open - - ${{ if eq(parameters.jobParameters.isFullMatrix, false) }}: + - ${{ if ne(parameters.jobParameters.isFullMatrix, true) }}: - ${{ if eq(parameters.jobParameters.buildConfig, 'Release') }}: - Windows.10.Amd64.Server19H1.ES.Open - ${{ if eq(parameters.jobParameters.buildConfig, 'Debug') }}: diff --git a/eng/pipelines/mono/templates/build-job.yml b/eng/pipelines/mono/templates/build-job.yml index 07af7f490ebbf..ef9af237168b7 100644 --- a/eng/pipelines/mono/templates/build-job.yml +++ b/eng/pipelines/mono/templates/build-job.yml @@ -61,6 +61,8 @@ jobs: value: '' - name: llvmParameter value: '' + - name: msCorDbi + value: '+mono.mscordbi' - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: - name: officialBuildIdArg value: '/p:officialBuildId=$(Build.BuildNumber)' @@ -74,9 +76,15 @@ jobs: - ${{ if eq(parameters.osGroup, 'tvOS') }}: - name: osOverride value: -os tvOS + - ${{ if eq(parameters.osGroup, 'tvOSSimulator') }}: + - name: osOverride + value: -os tvOSSimulator - ${{ if eq(parameters.osGroup, 'iOS') }}: - name: osOverride value: -os iOS + - ${{ if eq(parameters.osGroup, 'iOSSimulator') }}: + - name: osOverride + value: -os iOSSimulator - ${{ if eq(parameters.osGroup, 'Android') }}: - name: osOverride value: -os Android @@ -97,11 +105,6 @@ jobs: - ${{ if gt(length(parameters.monoCrossAOTTargetOS),0) }}: - name: aotCrossParameter value: /p:MonoCrossAOTTargetOS=${{join('+',parameters.monoCrossAOTTargetOS)}} /p:SkipMonoCrossJitConfigure=true /p:BuildMonoAOTCrossCompilerOnly=true - - name: ninjaArg - value: '' - - ${{ if eq(parameters.osGroup, 'windows') }}: - - name: ninjaArg - value: '-ninja' - ${{ parameters.variables }} steps: @@ -125,7 +128,7 @@ jobs: artifact: Mono_Offsets_${{monoCrossAOTTargetOS}} path: '$(Build.SourcesDirectory)/artifacts/obj/mono/offsetfiles' - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h @@ -133,13 +136,13 @@ jobs: # Build - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: ./build$(scriptExt) -subset mono -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) $(ninjaArg) + - script: ./build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) displayName: Build product - ${{ if eq(parameters.osGroup, 'windows') }}: - - script: build$(scriptExt) -subset mono -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) $(ninjaArg) + - script: build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) displayName: Build product - - ${{ if in(parameters.osGroup, 'OSX', 'iOS','tvOS') }}: + - ${{ if in(parameters.osGroup, 'OSX', 'iOS', 'tvOS') }}: - script: | du -sh $(Build.SourcesDirectory)/* df -h @@ -158,10 +161,10 @@ jobs: # Build packages - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: ./build$(scriptExt) -subset mono -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) $(ninjaArg) -pack $(OutputRidArg) + - script: ./build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) -pack $(OutputRidArg) displayName: Build nupkg - ${{ if eq(parameters.osGroup, 'windows') }}: - - script: build$(scriptExt) -subset mono -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) $(ninjaArg) -pack $(OutputRidArg) + - script: build$(scriptExt) -subset mono$(msCorDbi) -c $(buildConfig) -arch $(archType) $(osOverride) -ci $(officialBuildIdArg) $(aotCrossParameter) $(llvmParameter) -pack $(OutputRidArg) displayName: Build nupkg # Publish official build diff --git a/eng/pipelines/mono/templates/generate-offsets.yml b/eng/pipelines/mono/templates/generate-offsets.yml index ff3b3e9d70ca7..cbb05aaafff99 100644 --- a/eng/pipelines/mono/templates/generate-offsets.yml +++ b/eng/pipelines/mono/templates/generate-offsets.yml @@ -1,6 +1,7 @@ parameters: buildConfig: 'Debug' osGroup: '' + osSubGroup: '' platform: '' container: '' timeoutInMinutes: '' @@ -15,6 +16,7 @@ jobs: parameters: buildConfig: ${{ parameters.buildConfig }} osGroup: ${{ parameters.osGroup }} + osSubGroup: ${{ parameters.osSubGroup }} helixType: 'build/product/' enableMicrobuild: true pool: ${{ parameters.pool }} @@ -22,8 +24,8 @@ jobs: dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }} # Compute job name from template parameters - name: ${{ format('mono_{0}_offsets', parameters.osGroup) }} - displayName: ${{ format('Mono {0} AOT offsets', parameters.osGroup) }} + name: ${{ format('mono_{0}{1}_offsets', parameters.osGroup, parameters.osSubGroup) }} + displayName: ${{ format('Mono {0}{1} AOT offsets', parameters.osGroup, parameters.osSubGroup) }} # Run all steps in the container. # Note that the containers are defined in platform-matrix.yml @@ -35,6 +37,8 @@ jobs: variables: - name: osGroup value: ${{ parameters.osGroup }} + - name: osSubGroup + value: ${{ parameters.osSubGroup }} - name: officialBuildIdArg value: '' - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}: @@ -80,13 +84,13 @@ jobs: displayName: Upload offset files inputs: targetPath: '$(Build.SourcesDirectory)/artifacts/obj/mono/offsetfiles' - artifactName: 'Mono_Offsets_$(osGroup)' + artifactName: 'Mono_Offsets_$(osGroup)$(osSubGroup)' # Publish Logs - task: PublishPipelineArtifact@1 displayName: Publish Logs inputs: targetPath: $(Build.SourcesDirectory)/artifacts/log - artifactName: 'BuildLogs_Mono_Offsets_$(osGroup)' + artifactName: 'BuildLogs_Mono_Offsets_$(osGroup)$(osSubGroup)' continueOnError: true condition: always() diff --git a/eng/pipelines/mono/update-machine-certs.ps1 b/eng/pipelines/mono/update-machine-certs.ps1 new file mode 100644 index 0000000000000..1a6be78b19947 --- /dev/null +++ b/eng/pipelines/mono/update-machine-certs.ps1 @@ -0,0 +1,25 @@ +# This seems to update the machine cert store so that python can download the files as required by emscripten's install +$WebsiteURL="storage.googleapis.com" +Try { + $Conn = New-Object System.Net.Sockets.TcpClient($WebsiteURL,443) + + Try { + $Stream = New-Object System.Net.Security.SslStream($Conn.GetStream()) + $Stream.AuthenticateAsClient($WebsiteURL) + + $Cert = $Stream.Get_RemoteCertificate() + + $ValidTo = [datetime]::Parse($Cert.GetExpirationDatestring()) + + Write-Host "`nConnection Successfull" -ForegroundColor DarkGreen + Write-Host "Website: $WebsiteURL" + } + Catch { Throw $_ } + Finally { $Conn.close() } + } + Catch { + Write-Host "`nError occurred connecting to $($WebsiteURL)" -ForegroundColor Yellow + Write-Host "Website: $WebsiteURL" + Write-Host "Status:" $_.exception.innerexception.message -ForegroundColor Yellow + Write-Host "" +} diff --git a/eng/pipelines/runtime-linker-tests.yml b/eng/pipelines/runtime-linker-tests.yml index 64c4a55c772fd..836b54c849ab5 100644 --- a/eng/pipelines/runtime-linker-tests.yml +++ b/eng/pipelines/runtime-linker-tests.yml @@ -75,5 +75,7 @@ jobs: testGroup: innerloop timeoutInMinutes: 120 nameSuffix: Runtime_Release - buildArgs: -s mono+libs -c $(_BuildConfig) + buildArgs: -s mono+libs -c $(_BuildConfig) -p:WasmBuildNative=false extraStepsTemplate: /eng/pipelines/libraries/execute-trimming-tests-steps.yml + extraStepsParameters: + extraTestArgs: '/p:WasmBuildNative=false' diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml index a6ba21f154ab0..db754fa7bba76 100644 --- a/eng/pipelines/runtime-official.yml +++ b/eng/pipelines/runtime-official.yml @@ -105,10 +105,12 @@ stages: - Android_arm64 - MacCatalyst_x64 - MacCatalyst_arm64 - - tvOS_x64 + - tvOSSimulator_x64 + - tvOSSimulator_arm64 - tvOS_arm64 - - iOS_x64 - - iOS_x86 + - iOSSimulator_x64 + - iOSSimulator_x86 + - iOSSimulator_arm64 - iOS_arm - iOS_arm64 - OSX_x64 @@ -125,7 +127,7 @@ stages: # - windows_arm # - windows_arm64 jobParameters: - buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) + buildArgs: -s mono+libs+host+packs+mono.mscordbi -c $(_BuildConfig) nameSuffix: AllSubsets_Mono isOfficialBuild: ${{ variables.isOfficialBuild }} extraStepsTemplate: /eng/pipelines/common/upload-intermediate-artifacts-step.yml @@ -141,8 +143,8 @@ stages: platforms: - Android_x64 - Browser_wasm - - tvOS_x64 - - iOS_x64 + - tvOS_arm64 + - iOS_arm64 # # Build Mono release AOT cross-compilers @@ -180,7 +182,6 @@ stages: jobParameters: buildArgs: -s mono+packs -c $(_BuildConfig) /p:MonoCrossAOTTargetOS=Android+Browser /p:SkipMonoCrossJitConfigure=true /p:BuildMonoAOTCrossCompilerOnly=true - -ninja nameSuffix: CrossAOT_Mono runtimeVariant: crossaot dependsOn: diff --git a/eng/pipelines/runtime-staging.yml b/eng/pipelines/runtime-staging.yml index 2a2afabf81398..0197670b07218 100644 --- a/eng/pipelines/runtime-staging.yml +++ b/eng/pipelines/runtime-staging.yml @@ -64,9 +64,46 @@ jobs: buildConfig: Release runtimeFlavor: mono platforms: + - Android_x86 - Android_x64 + - iOSSimulator_x64 + - tvOSSimulator_x64 + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testGroup: innerloop + nameSuffix: AllSubsets_Mono + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true + timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) + +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: - Android_arm64 - - iOS_x64 variables: # map dependencies variables to local variables - name: librariesContainsChange @@ -78,6 +115,59 @@ jobs: nameSuffix: AllSubsets_Mono buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true timeoutInMinutes: 180 + condition: >- + or( + eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'], true), + eq(dependencies.evaluate_paths.outputs['SetPathVars_installer.containsChange'], true), + eq(variables['isFullMatrix'], true)) + + # don't run tests on PRs until we get more Android devices: https://github.com/dotnet/core-eng/issues/11781 + ${{ if eq(variables['isFullMatrix'], true) }}: + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: Mono_$(_BuildConfig) + condition: >- + or( + eq(variables['librariesContainsChange'], true), + eq(variables['monoContainsChange'], true), + eq(variables['isFullMatrix'], true)) + +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: Release + platforms: + - Android_arm + jobParameters: + testGroup: innerloop + nameSuffix: Build_Subset_Mono + buildArgs: -subset mono+libs + +# +# Build the whole product using Mono and run libraries tests +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: mono + platforms: + - Windows_x64 + variables: + # map dependencies variables to local variables + - name: librariesContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] + - name: monoContainsChange + value: $[ dependencies.evaluate_paths.outputs['SetPathVars_mono.containsChange'] ] + jobParameters: + testScope: innerloop + nameSuffix: AllSubsets_Mono + buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true + timeoutInMinutes: 120 condition: >- or( eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), @@ -175,34 +265,6 @@ jobs: creator: dotnet-bot testRunNamePrefixSuffix: Mono_$(_BuildConfig) -# -# Build Mono and Libraries for Android using Android native crypto instead of OpenSSL -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/common/global-build-job.yml - buildConfig: Release - runtimeFlavor: mono - platforms: - - Android_x64 - - Android_x86 - - Android_arm64 - variables: - # disable using OpenSSL - - name: ANDROID_OPENSSL_AAR - value: '' - # map dependencies variables to local variables - - name: librariesContainsChange - value: $[ dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'] ] - jobParameters: - nameSuffix: Libraries_Mono_AndroidCrypto - buildArgs: -s mono+libs -c $(_BuildConfig) - timeoutInMinutes: 180 - condition: >- - or( - eq(dependencies.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), - eq(variables['isFullMatrix'], true)) - # Run disabled installer tests on Linux x64 - template: /eng/pipelines/common/platform-matrix.yml parameters: @@ -217,3 +279,44 @@ jobs: useContinueOnErrorDuringBuild: true enablePublisTestResults: true testResultsFormat: xunit + +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + buildConfig: release + platforms: + - Browser_wasm_win + jobParameters: + testGroup: innerloop + nameSuffix: Browser_wasm_Windows + buildArgs: -subset mono+libs /p:RuntimeFlavor=Mono -c $(_BuildConfig) + +# +# CoreCLR Build for running Apple Silicon libraries-innerloop +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/coreclr/templates/build-job.yml + buildConfig: release + platforms: + - ${{ if eq(variables['isFullMatrix'], true) }}: + - OSX_arm64 + jobParameters: + testGroup: innerloop +# +# Libraries Build for running Apple Silicon libraries-innerloop +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/libraries/build-job.yml + buildConfig: Release + platforms: + - ${{ if eq(variables['isFullMatrix'], true) }}: + - OSX_arm64 + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + jobParameters: + isOfficialBuild: ${{ variables['isOfficialBuild'] }} + isFullMatrix: ${{ variables['isFullMatrix'] }} + runTests: true + testScope: innerloop + liveRuntimeBuildConfig: release diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 2f600b5929e1b..476150aad686a 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -204,8 +204,8 @@ jobs: platforms: - Android_x64 - Browser_wasm - - tvOS_x64 - - iOS_x64 + - tvOS_arm64 + - iOS_arm64 jobParameters: condition: >- or( @@ -222,12 +222,11 @@ jobs: buildConfig: ${{ variables.debugOnPrReleaseOnRolling }} runtimeFlavor: mono platforms: - - Android_x86 - MacCatalyst_x64 - MacCatalyst_arm64 - - tvOS_x64 + - tvOSSimulator_x64 + - iOSSimulator_x86 - iOS_arm64 - - iOS_x86 - Linux_arm jobParameters: testGroup: innerloop @@ -246,7 +245,6 @@ jobs: buildConfig: Release runtimeFlavor: mono platforms: - - Android_arm - tvOS_arm64 - iOS_arm - Linux_musl_x64 @@ -295,6 +293,7 @@ jobs: creator: dotnet-bot testRunNamePrefixSuffix: Mono_$(_BuildConfig) scenarios: + - buildwasmapps - normal - wasmtestonbrowser condition: >- @@ -303,6 +302,28 @@ jobs: eq(variables['monoContainsChange'], true), eq(variables['isFullMatrix'], true)) +# Build and test libraries under single-file publishing +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + platforms: + - windows_x64 + - Linux_x64 + jobParameters: + testGroup: innerloop + isFullMatrix: ${{ variables.isFullMatrix }} + isSingleFile: true + nameSuffix: SingleFile + buildArgs: -s clr+libs+libs.tests -c $(_BuildConfig) /p:TestSingleFile=true /p:ArchiveTests=true + timeoutInMinutes: 120 + # extra steps, run tests + extraStepsTemplate: /eng/pipelines/libraries/helix.yml + extraStepsParameters: + creator: dotnet-bot + testRunNamePrefixSuffix: SingleFile_$(_BuildConfig) + # # Build the whole product using Mono and run runtime tests # @@ -884,7 +905,7 @@ jobs: runtimeFlavor: mono buildConfig: ${{ variables.debugOnPrReleaseOnRolling }} platforms: - - windows_x64 + # - windows_x64 - OSX_x64 - Linux_arm64 - Linux_x64 diff --git a/eng/restore/optimizationData.targets b/eng/restore/optimizationData.targets index 9b431aa953af2..c7ce30fadd249 100644 --- a/eng/restore/optimizationData.targets +++ b/eng/restore/optimizationData.targets @@ -34,4 +34,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + $(NuGetPackageRoot)%(MIBCPackage.Identity)/%(MIBCPackage.Version) + + <_optimizationMibcFile Include="%(MIBCPackage.PackagePath)/**/*.mibc" SubdirectoryName="$(TargetOS)/$(TargetArchitecture)" /> + + + + + + + + + + + + diff --git a/eng/run-test.sh b/eng/run-test.sh index 94ac893132376..e5cf6a3b21d1e 100644 --- a/eng/run-test.sh +++ b/eng/run-test.sh @@ -287,8 +287,7 @@ then else platform="$(uname)" if [ "$platform" = "FreeBSD" ]; then - output=("$(sysctl hw.ncpu)") - maxProcesses="$((output[1] + 1))" + maxProcesses=$(($(sysctl -n hw.ncpu)+1)) if [ "$platform" = "NetBSD" ] || [ "$platform" = "SunOS" ] ; then maxProcesses=$(($(getconf NPROCESSORS_ONLN)+1)) else diff --git a/eng/slngen.targets b/eng/slngen.targets index 754a506cb48f8..f8066c75c736d 100644 --- a/eng/slngen.targets +++ b/eng/slngen.targets @@ -11,6 +11,6 @@ true - + diff --git a/eng/targetframeworksuffix.props b/eng/targetframeworksuffix.props index 5926e5266de02..00d2acfd4607c 100644 --- a/eng/targetframeworksuffix.props +++ b/eng/targetframeworksuffix.props @@ -9,6 +9,8 @@ $(TargetFrameworkSuffix) 1.0 $(TargetFrameworkSuffix),Version=$(TargetPlatformVersion) + + 0.0 diff --git a/eng/targetingpacks.targets b/eng/targetingpacks.targets index 22435626d3cd4..793f1954524fd 100644 --- a/eng/targetingpacks.targets +++ b/eng/targetingpacks.targets @@ -19,7 +19,7 @@ LatestRuntimeFrameworkVersion="$(ProductVersion)" RuntimeFrameworkName="$(LocalFrameworkOverrideName)" RuntimePackNamePatterns="$(LocalFrameworkOverrideName).Runtime.**RID**" - RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm;win-arm64;win-x64;win-x86;linux-musl-arm;ios-arm64;ios-arm;ios-x64;ios-x86;tvos-arm64;tvos-x64;maccatalyst-x64;maccatalyst-arm64;android-arm64;android-arm;android-x64;android-x86;browser-wasm;osx-arm64" + RuntimePackRuntimeIdentifiers="linux-arm;linux-arm64;linux-musl-arm64;linux-musl-x64;linux-x64;osx-x64;rhel.6-x64;tizen.4.0.0-armel;tizen.5.0.0-armel;win-arm;win-arm64;win-x64;win-x86;linux-musl-arm;ios-arm64;iossimulator-arm64;ios-arm;iossimulator-x64;iossimulator-x86;tvos-arm64;tvossimulator-arm64;tvossimulator-x64;maccatalyst-x64;maccatalyst-arm64;android-arm64;android-arm;android-x64;android-x86;browser-wasm;osx-arm64" TargetFramework="$(NetCoreAppCurrent)" TargetingPackName="$(LocalFrameworkOverrideName).Ref" TargetingPackVersion="$(ProductVersion)" diff --git a/eng/testing/AndroidRunnerTemplate.sh b/eng/testing/AndroidRunnerTemplate.sh index 0fd3a4a77c55a..3dab96f0bf3c4 100644 --- a/eng/testing/AndroidRunnerTemplate.sh +++ b/eng/testing/AndroidRunnerTemplate.sh @@ -10,7 +10,7 @@ TEST_NAME=$4 XHARNESS_OUT="$EXECUTION_DIR/xharness-output" if [ -n "$5" ]; then - EXPECTED_EXIT_CODE="--expected-exit-code $5" + ADDITIONAL_ARGS=${@:5} fi cd $EXECUTION_DIR @@ -40,7 +40,8 @@ $HARNESS_RUNNER android test \ --package-name="net.dot.$ASSEMBLY_NAME" \ --app="$EXECUTION_DIR/bin/$TEST_NAME.apk" \ --output-directory="$XHARNESS_OUT" \ - $EXPECTED_EXIT_CODE + --timeout=1800 \ + $ADDITIONAL_ARGS _exitCode=$? diff --git a/eng/testing/AppleRunnerTemplate.sh b/eng/testing/AppleRunnerTemplate.sh index e02a9a1569717..0f2eb7f2abed8 100644 --- a/eng/testing/AppleRunnerTemplate.sh +++ b/eng/testing/AppleRunnerTemplate.sh @@ -13,17 +13,19 @@ XCODE_PATH=$(xcode-select -p)/../.. if [ -n "$5" ]; then XHARNESS_CMD="run" - EXPECTED_EXIT_CODE="--expected-exit-code $5" + ADDITIONAL_ARGS=${@:5} fi if [[ "$TARGET_OS" == "MacCatalyst" ]]; then TARGET=maccatalyst; fi -if [[ "$TARGET_OS" == "iOS" && "$TARGET_ARCH" == "x86" ]]; then TARGET=ios-simulator-32; fi -if [[ "$TARGET_OS" == "iOS" && "$TARGET_ARCH" == "x64" ]]; then TARGET=ios-simulator-64; fi +if [[ "$TARGET_OS" == "iOSSimulator" && "$TARGET_ARCH" == "x86" ]]; then TARGET=ios-simulator-32; fi +if [[ "$TARGET_OS" == "iOSSimulator" && "$TARGET_ARCH" == "x64" ]]; then TARGET=ios-simulator-64; fi +if [[ "$TARGET_OS" == "iOSSimulator" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=ios-simulator-64; fi if [[ "$TARGET_OS" == "iOS" && "$TARGET_ARCH" == "arm" ]]; then TARGET=ios-device; fi if [[ "$TARGET_OS" == "iOS" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=ios-device; fi -if [[ "$TARGET_OS" == "tvOS" && "$TARGET_ARCH" == "x64" ]]; then TARGET=tvos-simulator; fi +if [[ "$TARGET_OS" == "tvOSSimulator" && "$TARGET_ARCH" == "x64" ]]; then TARGET=tvos-simulator; fi +if [[ "$TARGET_OS" == "tvOSSimulator" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=tvos-simulator; fi if [[ "$TARGET_OS" == "tvOS" && "$TARGET_ARCH" == "arm64" ]]; then TARGET=tvos-device; fi # "Release" in SCHEME_SDK is what xcode produces (see "bool Optimized" property in AppleAppBuilderTask) @@ -60,7 +62,7 @@ $HARNESS_RUNNER apple $XHARNESS_CMD \ --targets="$TARGET" \ --xcode="$XCODE_PATH" \ --output-directory="$XHARNESS_OUT" \ - $EXPECTED_EXIT_CODE + $ADDITIONAL_ARGS _exitCode=$? diff --git a/eng/testing/linker/SupportFiles/Directory.Build.props b/eng/testing/linker/SupportFiles/Directory.Build.props index b5c4dfe732fb2..ad6c9dbb6916d 100644 --- a/eng/testing/linker/SupportFiles/Directory.Build.props +++ b/eng/testing/linker/SupportFiles/Directory.Build.props @@ -3,6 +3,7 @@ true false false + false true true link diff --git a/eng/testing/linker/project.csproj.template b/eng/testing/linker/project.csproj.template index 7ac21cbcb5b5f..53576a8cc2163 100644 --- a/eng/testing/linker/project.csproj.template +++ b/eng/testing/linker/project.csproj.template @@ -29,6 +29,7 @@ {AdditionalProjectReferences} + {TrimmerRootAssemblies} $([MSBuild]::NormalizeDirectory('$(OutDir)', 'AppBundle')) $([MSBuild]::NormalizePath('$(BundleDir)', '$(RunScriptOutputName)')) - true - V8 - $(JSEngineArgs) --engine-arg=--stack-trace-limit=1000 + true true + true + true + true + BundleTestAppleApp;BundleTestAndroidApp - - - $HARNESS_RUNNER wasm $XHARNESS_COMMAND --app=. --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js --output-directory=$XHARNESS_OUT -- $(RunTestsJSArguments) --run WasmTestRunner.dll $(AssemblyName).dll - $HARNESS_RUNNER wasm $XHARNESS_COMMAND --app=. --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js --output-directory=$XHARNESS_OUT --expected-exit-code=$(ExpectedExitCode) -- $(RunTestsJSArguments) --run $(AssemblyName).dll --testing + + false + false + false false + false true - false false - false + false + true - - false - false + false + false false - false - true false false + true true @@ -37,6 +38,16 @@ true + + --expected-exit-code $(ExpectedExitCode) + + + + + $(AdditionalXHarnessArguments) --arg=-m=$(XUnitMethodName) + $(AdditionalXHarnessArguments) --arg=-c=$(XUnitClassName) + + @@ -44,15 +55,19 @@ <_MobileIntermediateOutputPath>$(IntermediateOutputPath)mobile + + + - arm64-v8a armeabi-v7a @@ -60,27 +75,31 @@ x86 AndroidTestRunner.dll + $(PublishDir)$(AssemblyName).runtimeconfig.json + $(PublishDir)runtimeconfig.bin @(MonoAOTCompilerDefaultAotArguments, ';') @(MonoAOTCompilerDefaultProcessArguments, ';') + + - - + - - - + + + + - @@ -127,13 +146,13 @@ - - + - + true @@ -228,60 +247,11 @@ - - - - - PrepareForWasmBuildApp;$(WasmBuildAppDependsOn) - $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk')) - - - - - - - - - - - - - - - - - - - $(BundleDir) - WasmTestRunner.dll - $(MonoProjectRoot)\wasm\runtime-test.js - $(InvariantGlobalization) - true - - - - - - $([System.IO.Directory]::GetParent('%(Identity)').Name) - - - - - - - - - - - - <_runnerFilesToPublish Include="$(AndroidTestRunnerDir)*" Condition="'$(TargetOS)' == 'Android'" /> - <_runnerFilesToPublish Include="$(AppleTestRunnerDir)*" Condition="'$(TargetOS)' == 'MacCatalyst' or '$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'tvOS'" /> + <_runnerFilesToPublish Include="$(AppleTestRunnerDir)*" Condition="'$(TargetOS)' == 'MacCatalyst' or '$(TargetOS)' == 'iOS' or '$(TargetOS)' == 'iOSSimulator' or '$(TargetOS)' == 'tvOS' or '$(TargetOS)' == 'tvOSSimulator'" /> <_runnerFilesToPublish Include="$(WasmTestRunnerDir)*" Condition="'$(TargetOS)' == 'Browser'" /> @@ -299,7 +269,7 @@ + DependsOnTargets="Publish;$(BundleTestAppTargets);ArchiveTests" /> diff --git a/eng/testing/tests.singlefile.targets b/eng/testing/tests.singlefile.targets new file mode 100644 index 0000000000000..86cbc7ee5109a --- /dev/null +++ b/eng/testing/tests.singlefile.targets @@ -0,0 +1,62 @@ + + + Exe + + $([MSBuild]::NormalizeDirectory('$(OutDir)', 'publish')) + $([MSBuild]::NormalizePath('$(BundleDir)', '$(RunScriptOutputName)')) + $(PackageRID) + + $(AssemblyName).exe + chmod +rwx $(AssemblyName) && ./$(AssemblyName) + + + + true + true + true + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(Configuration)', 'corehost'))/singlefilehost + $(SingleFileHostSourcePath).exe + + + + + + + + + + + + + <__Identity>%(ResolvedFileToPublish.Identity) + <__FileName>%(ResolvedFileToPublish.Filename)%(ResolvedFileToPublish.Extension) + + + + <__NewResolvedFiles Include="@(ResolvedFileToPublish)"> + true + + + + + + + + + + + + + + diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets index d5ab901145464..9fc08ddd6bcc7 100644 --- a/eng/testing/tests.targets +++ b/eng/testing/tests.targets @@ -2,7 +2,7 @@ RunnerTemplate.cmd RunnerTemplate.sh - AppleRunnerTemplate.sh + AppleRunnerTemplate.sh AndroidRunnerTemplate.sh WasmRunnerTemplate.sh @@ -25,7 +25,7 @@ - + @@ -37,7 +37,7 @@ <_ZipSourceDirectory>$(OutDir) - <_ZipSourceDirectory Condition="'$(TargetOS)' == 'Browser'">$(BundleDir) + <_ZipSourceDirectory Condition="'$(TargetOS)' == 'Browser' or '$(TestSingleFile)' == 'true'">$(BundleDir) @@ -96,7 +96,7 @@ $(RunTestsCommand) --runtime-path "$(TestHostRootPath.TrimEnd('\/'))" $(RunTestsCommand) --rsp-file "$(TestRspFile)" - "$(RunScriptOutputPath)" $(AssemblyName) $(TargetArchitecture) $(TargetOS) $(TestProjectName) $(ExpectedExitCode) + "$(RunScriptOutputPath)" $(AssemblyName) $(TargetArchitecture) $(TargetOS) $(TestProjectName) $(AdditionalXHarnessArguments) "$(RunScriptOutputPath)" $(JSEngine) $(AssemblyName).dll $(Scenario) @@ -118,10 +118,11 @@ + - + diff --git a/eng/testing/tests.wasm.targets b/eng/testing/tests.wasm.targets new file mode 100644 index 0000000000000..505b3a8bde603 --- /dev/null +++ b/eng/testing/tests.wasm.targets @@ -0,0 +1,82 @@ + + + + $(BundleTestAppTargets);BundleTestWasmApp + + + + V8 + $(JSEngineArgs) --engine-arg=--stack-trace-limit=1000 + + <_XHarnessArgs>wasm $XHARNESS_COMMAND --app=. --output-directory=$XHARNESS_OUT + + <_XHarnessArgs Condition="'$(Scenario)' != 'WasmTestOnBrowser'">$(_XHarnessArgs) --engine=$(JSEngine) $(JSEngineArgs) --js-file=runtime.js + <_XHarnessArgs Condition="'$(IsFunctionalTest)' == 'true'" >$(_XHarnessArgs) --expected-exit-code=$(ExpectedExitCode) + <_XHarnessArgs Condition="'$(WasmXHarnessArgs)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgs) %24WasmXHarnessArgs + + <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(Scenario)' != 'BuildWasmApps'">--run WasmTestRunner.dll $(AssemblyName).dll + <_AppArgs Condition="'$(IsFunctionalTest)' == 'true'">--run $(AssemblyName).dll --testing + + <_AppArgs Condition="'$(WasmTestAppArgs)' != ''">$(_AppArgs) $(WasmTestAppArgs) %24WasmTestAppArgs + + $HARNESS_RUNNER $(_XHarnessArgs) -- $(WasmXHarnessMonoArgs) %24WasmXHarnessMonoArgs $(_AppArgs) + + + + false + true + false + false + false + + + + + + + PrepareForWasmBuildApp;$(WasmBuildAppDependsOn) + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'mono', 'wasm', 'emsdk')) + + + + + + + + + + + + + + + + + + + $(BundleDir) + WasmTestRunner.dll + $(MonoProjectRoot)\wasm\runtime-test.js + $(InvariantGlobalization) + true + false + + + + + + $([System.IO.Directory]::GetParent('%(Identity)').Name) + + + + + + + + + + + + diff --git a/eng/testing/xunit/xunit.console.targets b/eng/testing/xunit/xunit.console.targets index a3e7d2614a4f5..7710a05b5a37d 100644 --- a/eng/testing/xunit/xunit.console.targets +++ b/eng/testing/xunit/xunit.console.targets @@ -2,10 +2,10 @@ true testResults.xml - true + true - + <_depsFileArgument Condition="'$(GenerateDependencyFile)' == 'true'">--depsfile $(AssemblyName).deps.json "$(RunScriptHost)" exec --runtimeconfig $(AssemblyName).runtimeconfig.json $(_depsFileArgument) xunit.console.dll xunit.console.exe @@ -36,7 +36,7 @@ $(_withoutCategories.Replace(';', '%0dcategory=')) - + diff --git a/eng/testing/xunit/xunit.props b/eng/testing/xunit/xunit.props index f63b3906fccb6..3f9c4b67141a2 100644 --- a/eng/testing/xunit/xunit.props +++ b/eng/testing/xunit/xunit.props @@ -13,7 +13,7 @@ - + diff --git a/eng/versioning.targets b/eng/versioning.targets index 9e6d9de071847..02a10237e412b 100644 --- a/eng/versioning.targets +++ b/eng/versioning.targets @@ -35,17 +35,40 @@ true - - + + + + + + + + + + + + + + + + + + <_unsupportedOSPlatforms Include="$(UnsupportedOSPlatforms)" /> - + + <_Parameter1>%(_unsupportedOSPlatforms.Identity) diff --git a/global.json b/global.json index 3e24a5950e665..f2672dc65e4f2 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.100-preview.1.21103.13", + "version": "6.0.100-preview.2.21155.3", "allowPrerelease": true, "rollForward": "major" }, @@ -12,12 +12,12 @@ "python3": "3.7.1" }, "msbuild-sdks": { - "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21155.1", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21155.1", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21155.1", - "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21155.1", + "Microsoft.DotNet.Build.Tasks.TargetFramework.Sdk": "6.0.0-beta.21203.1", + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21203.1", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21203.1", + "Microsoft.DotNet.SharedFramework.Sdk": "6.0.0-beta.21203.1", "Microsoft.Build.NoTargets": "2.0.17", "Microsoft.Build.Traversal": "2.1.1", - "Microsoft.NET.Sdk.IL": "6.0.0-preview.3.21157.6" + "Microsoft.NET.Sdk.IL": "6.0.0-preview.4.21178.6" } } diff --git a/src/coreclr/.nuget/Microsoft.NET.Sdk.IL/targets/Microsoft.NET.Sdk.IL.targets b/src/coreclr/.nuget/Microsoft.NET.Sdk.IL/targets/Microsoft.NET.Sdk.IL.targets index 14821456c99b1..8ca2c087e39a6 100644 --- a/src/coreclr/.nuget/Microsoft.NET.Sdk.IL/targets/Microsoft.NET.Sdk.IL.targets +++ b/src/coreclr/.nuget/Microsoft.NET.Sdk.IL/targets/Microsoft.NET.Sdk.IL.targets @@ -16,7 +16,7 @@ Copyright (c) .NET Foundation. All rights reserved. .il IL Managed - {F571AB99-FB84-49F4-A0DF-F27AA55F3F3F} + {9A19103F-16F7-4668-BE54-9A1E7A4F7556} Properties true diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index 28ecbf7f16296..8f82d83fa4b73 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -12,8 +12,6 @@ include(../../eng/native/configurepaths.cmake) include(${CLR_ENG_NATIVE_DIR}/configurecompiler.cmake) if(MSVC) - string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - add_compile_options($<$:/EHa>) # enable C++ EH (w/ SEH exceptions) set(CMAKE_CXX_STANDARD_LIBRARIES "") # do not link against standard win32 libs i.e. kernel32, uuid, user32, etc. endif (MSVC) @@ -40,7 +38,11 @@ OPTION(CLR_CMAKE_ENABLE_CODE_COVERAGE "Enable code coverage" OFF) # Cross target Component build specific configuration #---------------------------------------------------- if(CLR_CROSS_COMPONENTS_BUILD) - include(crosscomponents.cmake) + add_definitions(-DCROSS_COMPILE) + + if(CLR_CMAKE_HOST_ARCH_AMD64 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_I386)) + set(FEATURE_CROSSBITNESS 1) + endif(CLR_CMAKE_HOST_ARCH_AMD64 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_I386)) endif(CLR_CROSS_COMPONENTS_BUILD) #------------------- @@ -48,10 +50,31 @@ endif(CLR_CROSS_COMPONENTS_BUILD) #------------------- include(pgosupport.cmake) +#--------------------------------------------------- +# Define sub-component targets for the build +#--------------------------------------------------- +include(components.cmake) + +#--------------------------- +# Build the single file host +#--------------------------- +if(NOT CLR_CROSS_COMPONENTS_BUILD) + set(CLR_SINGLE_FILE_HOST_ONLY 1) + add_subdirectory(${CLR_SRC_NATIVE_DIR}/corehost/apphost/static Corehost.Static) + add_dependencies(runtime singlefilehost) +endif() +#------------------------- +# Enable C++ EH with SEH +#------------------------- +if (MSVC) + string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + add_compile_options($<$:/EHa>) # enable C++ EH (w/ SEH exceptions) +endif() + #------------------------------- # Include libraries native shims #------------------------------- -if(NOT CLR_CROSS_COMPONENTS_BUILD AND CLR_CMAKE_BUILD_SUBSET_RUNTIME) +if(NOT CLR_CROSS_COMPONENTS_BUILD) set(STATIC_LIBS_ONLY 1) if(CLR_CMAKE_TARGET_WIN32) @@ -59,7 +82,7 @@ if(NOT CLR_CROSS_COMPONENTS_BUILD AND CLR_CMAKE_BUILD_SUBSET_RUNTIME) else() add_subdirectory(${CLR_REPO_ROOT_DIR}/src/libraries/Native/Unix Native.Unix) endif() -endif(NOT CLR_CROSS_COMPONENTS_BUILD AND CLR_CMAKE_BUILD_SUBSET_RUNTIME) +endif(NOT CLR_CROSS_COMPONENTS_BUILD) #----------------------------------------- # Add Projects @@ -92,7 +115,7 @@ add_subdirectory(pal/prebuilt/inc) add_subdirectory(debug/debug-pal) -if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_BUILD_SUBSET_RUNTIME) +if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(gc/sample) endif() @@ -105,6 +128,7 @@ include_directories("../../artifacts/obj/coreclr") add_subdirectory(tools/aot/jitinterface) + # Above projects do not build with these compile options # All of the compiler options are specified in file compileoptions.cmake # Do not add any new options here. They should be added in compileoptions.cmake @@ -121,15 +145,9 @@ include(clrdefinitions.cmake) if(FEATURE_STANDALONE_GC) add_definitions(-DFEATURE_STANDALONE_GC) - if (CLR_CMAKE_BUILD_SUBSET_RUNTIME) - add_subdirectory(gc) - endif (CLR_CMAKE_BUILD_SUBSET_RUNTIME) + add_subdirectory(gc) endif(FEATURE_STANDALONE_GC) -if(CLR_CMAKE_BUILD_SUBSET_RUNTIME AND NOT CLR_CROSS_COMPONENTS_BUILD) - add_subdirectory(${CLR_SRC_NATIVE_DIR}/corehost/apphost/static Corehost.Static) -endif() - if (CLR_CMAKE_HOST_UNIX) include_directories("pal/inc") include_directories("pal/inc/rt") @@ -157,11 +175,9 @@ endif(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE) add_subdirectory(debug/dbgutil) if(CLR_CMAKE_HOST_UNIX) - if(CLR_CMAKE_BUILD_SUBSET_RUNTIME) - if(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) - add_subdirectory(debug/createdump) - endif(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) - endif(CLR_CMAKE_BUILD_SUBSET_RUNTIME) + if(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) + add_subdirectory(debug/createdump) + endif(CLR_CMAKE_HOST_OSX OR (CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_UNIX_X86 AND NOT CLR_CMAKE_HOST_ANDROID)) # Include the dummy c++ include files include_directories("pal/inc/rt/cpp") @@ -206,36 +222,34 @@ if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSI endif() add_subdirectory(utilcode) -add_subdirectory(gcinfo) -add_subdirectory(jit) add_subdirectory(inc) if(CLR_CMAKE_HOST_UNIX) add_subdirectory(palrt) endif(CLR_CMAKE_HOST_UNIX) +add_subdirectory(ilasm) +add_subdirectory(ildasm) +add_subdirectory(gcinfo) +add_subdirectory(jit) add_subdirectory(vm) -if (CLR_CMAKE_BUILD_SUBSET_RUNTIME) - add_subdirectory(md) - add_subdirectory(debug) - add_subdirectory(binder) - add_subdirectory(classlibnative) - add_subdirectory(dlls) - add_subdirectory(ToolBox) - add_subdirectory(tools) - add_subdirectory(unwinder) - add_subdirectory(ildasm) - add_subdirectory(ilasm) - add_subdirectory(interop) - - if(CLR_CMAKE_HOST_WIN32) - add_subdirectory(hosts) - endif(CLR_CMAKE_HOST_WIN32) -else() - if(CLR_CMAKE_HOST_UNIX) - # this is needed to compile the jit on unix platforms. - # When the runtime subset is compiled, the add_subdirectory(dlls) above - # brings the mscorrc library into the build graph - add_subdirectory(dlls/mscorrc) - endif(CLR_CMAKE_HOST_UNIX) -endif(CLR_CMAKE_BUILD_SUBSET_RUNTIME) +add_subdirectory(md) +add_subdirectory(debug) +add_subdirectory(binder) +add_subdirectory(classlibnative) +add_subdirectory(dlls) +add_subdirectory(ToolBox) +add_subdirectory(tools) +add_subdirectory(unwinder) +add_subdirectory(interop) + +if(CLR_CMAKE_HOST_WIN32) + add_subdirectory(hosts) +endif(CLR_CMAKE_HOST_WIN32) + +#---------------------------------------------------- +# Cross target Component install configuration +#---------------------------------------------------- +if(CLR_CROSS_COMPONENTS_BUILD) + include(crosscomponents.cmake) +endif(CLR_CROSS_COMPONENTS_BUILD) diff --git a/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets b/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets index c36b1c57ab82f..aa50f44411c9f 100644 --- a/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets +++ b/src/coreclr/System.Private.CoreLib/CreateRuntimeRootILLinkDescriptorFile.targets @@ -5,7 +5,7 @@ - <_ILLinkRuntimeRootDescriptorFilePath>$(ILLinkTrimXml) + <_ILLinkRuntimeRootDescriptorFilePath>$(ILLinkDescriptorsXml) <_NamespaceFilePath Condition=" '$(_NamespaceFilePath)' == '' ">$(MSBuildThisFileDirectory)..\vm\namespace.h <_MscorlibFilePath Condition=" '$(_MscorlibFilePath)' == '' ">$(MSBuildThisFileDirectory)..\vm\corelib.h <_CortypeFilePath Condition=" '$(_CortypeFilePath)' == '' ">$(MSBuildThisFileDirectory)..\inc\cortypeinfo.h @@ -14,7 +14,7 @@ - <_ILLinkDescriptorsFilePaths Include="$(ILLinkDirectory)ILLinkTrim.xml" /> + <_ILLinkDescriptorsFilePaths Include="$(ILLinkDirectory)ILLink.Descriptors.xml" /> <_ILLinkDescriptorsFilePaths Include="$(CoreLibSharedDir)ILLink\ILLink.Descriptors.Shared.xml" /> @@ -49,7 +49,7 @@ CombinedLinkerXmlFile="$(_ILLinkDescriptorsIntermediatePath)" /> - + diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 25176927fd999..5f0d5c2c940f8 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -1,4 +1,4 @@ - + false @@ -16,12 +16,16 @@ true true - $(IntermediateOutputPath)System.Private.CoreLib.xml + $(IntermediateOutputPath)ILLink.Descriptors.xml $(MSBuildThisFileDirectory)src\ILLink\ true + + + + @@ -119,7 +123,6 @@ - @@ -297,7 +300,11 @@ - + + diff --git a/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/TableLevels.cs b/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/TableLevels.cs index 3740447693275..1435c9c771782 100644 --- a/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/TableLevels.cs +++ b/src/coreclr/System.Private.CoreLib/Tools/GenUnicodeProp/TableLevels.cs @@ -5,7 +5,7 @@ namespace GenUnicodeProp { - internal class TableLevels + internal sealed class TableLevels { public readonly int Level1Bits; public readonly int Level2Bits; diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLinkTrim.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/ILLink/ILLinkTrim.xml rename to src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.xml diff --git a/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml new file mode 100644 index 0000000000000..7953cca3a6c10 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs index 6caaede63b1f6..9f2c011efac31 100644 --- a/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs +++ b/src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs @@ -446,7 +446,7 @@ private static AssemblyLoadContext GetALC(string assemblyPath) } [ComVisible(true)] - private class BasicClassFactory : IClassFactory + private sealed class BasicClassFactory : IClassFactory { #if FEATURE_COMINTEROP_UNMANAGED_ACTIVATION private readonly Guid _classId; @@ -578,7 +578,7 @@ public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock) } [ComVisible(true)] - private class LicenseClassFactory : IClassFactory2 + private sealed class LicenseClassFactory : IClassFactory2 { #if FEATURE_COMINTEROP_UNMANAGED_ACTIVATION private readonly LicenseInteropProxy _licenseProxy = new LicenseInteropProxy(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs index da6adab43e2f2..0959bc67f47f8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs @@ -13,7 +13,7 @@ internal interface IArraySortHelper } [TypeDependency("System.Collections.Generic.GenericArraySortHelper`1")] - internal partial class ArraySortHelper + internal sealed partial class ArraySortHelper : IArraySortHelper { private static readonly IArraySortHelper s_defaultArraySortHelper = CreateArraySortHelper(); @@ -37,7 +37,7 @@ private static IArraySortHelper CreateArraySortHelper() } } - internal partial class GenericArraySortHelper + internal sealed partial class GenericArraySortHelper : IArraySortHelper { } @@ -48,7 +48,7 @@ internal interface IArraySortHelper } [TypeDependency("System.Collections.Generic.GenericArraySortHelper`2")] - internal partial class ArraySortHelper + internal sealed partial class ArraySortHelper : IArraySortHelper { private static readonly IArraySortHelper s_defaultArraySortHelper = CreateArraySortHelper(); @@ -72,7 +72,7 @@ private static IArraySortHelper CreateArraySortHelper() } } - internal partial class GenericArraySortHelper + internal sealed partial class GenericArraySortHelper : IArraySortHelper { } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs b/src/coreclr/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs deleted file mode 100644 index 287c18661d7f3..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Collections/ObjectModel/ReadOnlyDictionary.cs +++ /dev/null @@ -1,526 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** -** -** -** Purpose: Read-only wrapper for another generic dictionary. -** -===========================================================*/ - -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Threading; - -namespace System.Collections.ObjectModel -{ - [DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))] - [DebuggerDisplay("Count = {Count}")] - internal class ReadOnlyDictionary : IDictionary, IDictionary, IReadOnlyDictionary where TKey : notnull - { - private readonly IDictionary m_dictionary; - private object? m_syncRoot; - private KeyCollection? m_keys; - private ValueCollection? m_values; - - public ReadOnlyDictionary(IDictionary dictionary) - { - if (dictionary == null) - { - throw new ArgumentNullException(nameof(dictionary)); - } - m_dictionary = dictionary; - } - - protected IDictionary Dictionary => m_dictionary; - - public KeyCollection Keys => m_keys ??= new KeyCollection(m_dictionary.Keys); - - public ValueCollection Values => m_values ??= new ValueCollection(m_dictionary.Values); - - #region IDictionary Members - - public bool ContainsKey(TKey key) => m_dictionary.ContainsKey(key); - - ICollection IDictionary.Keys => Keys; - - public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => - m_dictionary.TryGetValue(key, out value); - - ICollection IDictionary.Values => Values; - - public TValue this[TKey key] => m_dictionary[key]; - - void IDictionary.Add(TKey key, TValue value) => - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - bool IDictionary.Remove(TKey key) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - return false; - } - - TValue IDictionary.this[TKey key] - { - get => m_dictionary[key]; - set => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - } - - #endregion - - #region ICollection> Members - - public int Count => m_dictionary.Count; - - bool ICollection>.Contains(KeyValuePair item) => - m_dictionary.Contains(item); - - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) => - m_dictionary.CopyTo(array, arrayIndex); - - bool ICollection>.IsReadOnly => true; - - void ICollection>.Add(KeyValuePair item) => - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - void ICollection>.Clear() => - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - bool ICollection>.Remove(KeyValuePair item) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - return false; - } - - #endregion - - #region IEnumerable> Members - - public IEnumerator> GetEnumerator() => - m_dictionary.GetEnumerator(); - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => - ((IEnumerable)m_dictionary).GetEnumerator(); - - #endregion - - #region IDictionary Members - - private static bool IsCompatibleKey(object key) - { - if (key == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); - } - return key is TKey; - } - - void IDictionary.Add(object key, object? value) => - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - void IDictionary.Clear() => - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - bool IDictionary.Contains(object key) => IsCompatibleKey(key) && ContainsKey((TKey)key); - - IDictionaryEnumerator IDictionary.GetEnumerator() - { - if (m_dictionary is IDictionary d) - { - return d.GetEnumerator(); - } - return new DictionaryEnumerator(m_dictionary); - } - - bool IDictionary.IsFixedSize => true; - - bool IDictionary.IsReadOnly => true; - - ICollection IDictionary.Keys => Keys; - - void IDictionary.Remove(object key) => - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - ICollection IDictionary.Values => Values; - - object? IDictionary.this[object key] - { - get - { - if (IsCompatibleKey(key)) - { - return this[(TKey)key]; - } - return null; - } - set => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - } - - void ICollection.CopyTo(Array array, int index) - { - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - } - - if (array.Rank != 1) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); - } - - if (array.GetLowerBound(0) != 0) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); - } - - if (index < 0 || index > array.Length) - { - ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); - } - - if (array.Length - index < Count) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); - } - - if (array is KeyValuePair[] pairs) - { - m_dictionary.CopyTo(pairs, index); - } - else - { - if (array is DictionaryEntry[] dictEntryArray) - { - foreach (KeyValuePair item in m_dictionary) - { - dictEntryArray[index++] = new DictionaryEntry(item.Key, item.Value); - } - } - else - { - object[]? objects = array as object[]; - if (objects == null) - { - ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); - } - - try - { - foreach (KeyValuePair item in m_dictionary) - { - objects[index++] = new KeyValuePair(item.Key, item.Value); - } - } - catch (ArrayTypeMismatchException) - { - ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); - } - } - } - } - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot - { - get - { - if (m_syncRoot == null) - { - if (m_dictionary is ICollection c) - { - m_syncRoot = c.SyncRoot; - } - else - { - Interlocked.CompareExchange(ref m_syncRoot, new object(), null); - } - } - return m_syncRoot; - } - } - - private struct DictionaryEnumerator : IDictionaryEnumerator - { - private readonly IDictionary m_dictionary; - private readonly IEnumerator> m_enumerator; - - public DictionaryEnumerator(IDictionary dictionary) - { - m_dictionary = dictionary; - m_enumerator = m_dictionary.GetEnumerator(); - } - - public DictionaryEntry Entry => new DictionaryEntry(m_enumerator.Current.Key, m_enumerator.Current.Value); - - public object Key => m_enumerator.Current.Key!; - - public object? Value => m_enumerator.Current.Value; - - public object? Current => Entry; - - public bool MoveNext() => m_enumerator.MoveNext(); - - public void Reset() => m_enumerator.Reset(); - } - - #endregion - - #region IReadOnlyDictionary members - - IEnumerable IReadOnlyDictionary.Keys => Keys; - - IEnumerable IReadOnlyDictionary.Values => Values; - - #endregion IReadOnlyDictionary members - - [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] - [DebuggerDisplay("Count = {Count}")] - public sealed class KeyCollection : ICollection, ICollection, IReadOnlyCollection - { - private readonly ICollection m_collection; - private object? m_syncRoot; - - internal KeyCollection(ICollection collection) - { - if (collection == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); - } - m_collection = collection; - } - - #region ICollection Members - - void ICollection.Add(TKey item) => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - void ICollection.Clear() => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - bool ICollection.Contains(TKey item) => m_collection.Contains(item); - - public void CopyTo(TKey[] array, int arrayIndex) => m_collection.CopyTo(array, arrayIndex); - - public int Count => m_collection.Count; - - bool ICollection.IsReadOnly => true; - - bool ICollection.Remove(TKey item) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - return false; - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() => m_collection.GetEnumerator(); - - #endregion - - #region IEnumerable Members - - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)m_collection).GetEnumerator(); - - #endregion - - #region ICollection Members - - void ICollection.CopyTo(Array array, int index) => ReadOnlyDictionaryHelpers.CopyToNonGenericICollectionHelper(m_collection, array, index); - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot - { - get - { - if (m_syncRoot == null) - { - if (m_collection is ICollection c) - { - m_syncRoot = c.SyncRoot; - } - else - { - Interlocked.CompareExchange(ref m_syncRoot, new object(), null); - } - } - return m_syncRoot; - } - } - - #endregion - } - - [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] - [DebuggerDisplay("Count = {Count}")] - public sealed class ValueCollection : ICollection, ICollection, IReadOnlyCollection - { - private readonly ICollection m_collection; - private object? m_syncRoot; - - internal ValueCollection(ICollection collection) - { - if (collection == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); - } - m_collection = collection; - } - - #region ICollection Members - - void ICollection.Add(TValue item) => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - void ICollection.Clear() => ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - - bool ICollection.Contains(TValue item) => m_collection.Contains(item); - - public void CopyTo(TValue[] array, int arrayIndex) => m_collection.CopyTo(array, arrayIndex); - - public int Count => m_collection.Count; - - bool ICollection.IsReadOnly => true; - - bool ICollection.Remove(TValue item) - { - ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection); - return false; - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() => m_collection.GetEnumerator(); - - #endregion - - #region IEnumerable Members - - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)m_collection).GetEnumerator(); - - #endregion - - #region ICollection Members - - void ICollection.CopyTo(Array array, int index) => ReadOnlyDictionaryHelpers.CopyToNonGenericICollectionHelper(m_collection, array, index); - - bool ICollection.IsSynchronized => false; - - object ICollection.SyncRoot - { - get - { - if (m_syncRoot == null) - { - if (m_collection is ICollection c) - { - m_syncRoot = c.SyncRoot; - } - else - { - Interlocked.CompareExchange(ref m_syncRoot, new object(), null); - } - } - return m_syncRoot; - } - } - - #endregion ICollection Members - } - } - - // To share code when possible, use a non-generic class to get rid of irrelevant type parameters. - internal static class ReadOnlyDictionaryHelpers - { - #region Helper method for our KeyCollection and ValueCollection - - // Abstracted away to avoid redundant implementations. - internal static void CopyToNonGenericICollectionHelper(ICollection collection, Array array, int index) - { - if (array == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - } - - if (array.Rank != 1) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); - } - - if (array.GetLowerBound(0) != 0) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound); - } - - if (index < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); - } - - if (array.Length - index < collection.Count) - { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); - } - - // Easy out if the ICollection implements the non-generic ICollection - if (collection is ICollection nonGenericCollection) - { - nonGenericCollection.CopyTo(array, index); - return; - } - - if (array is T[] items) - { - collection.CopyTo(items, index); - } - else - { - // - // Catch the obvious case assignment will fail. - // We can found all possible problems by doing the check though. - // For example, if the element type of the Array is derived from T, - // we can't figure out if we can successfully copy the element beforehand. - // - Type targetType = array.GetType().GetElementType()!; - Type sourceType = typeof(T); - if (!(targetType.IsAssignableFrom(sourceType) || sourceType.IsAssignableFrom(targetType))) - { - ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); - } - - // - // We can't cast array of value type to object[], so we don't support - // widening of primitive types here. - // - object?[]? objects = array as object[]; - if (objects == null) - { - ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); - } - - try - { - foreach (T item in collection) - { - objects[index++] = item; - } - } - catch (ArrayTypeMismatchException) - { - ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); - } - } - } - - #endregion Helper method for our KeyCollection and ValueCollection - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs index 4015192781288..bed89c466f9d2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.cs @@ -28,7 +28,7 @@ public static class Debugger // This class implements code:ICustomDebuggerNotification and provides a type to be used to notify // the debugger that execution is about to enter a path that involves a cross-thread dependency. // See code:NotifyOfCrossThreadDependency for more details. - private class CrossThreadDependencyNotification : ICustomDebuggerNotification { } + private sealed class CrossThreadDependencyNotification : ICustomDebuggerNotification { } // Do not inline the slow path [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs index a76e031f7fac0..3dd71b805d8f0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrameHelper.cs @@ -11,7 +11,7 @@ namespace System.Diagnostics // Modifying the order or fields of this object may require other changes // to the unmanaged definition of the StackFrameHelper class, in // VM\DebugDebugger.h. The binder will catch some of these layout problems. - internal class StackFrameHelper + internal sealed class StackFrameHelper { private Thread? targetThread; private int[]? rgiOffset; @@ -152,7 +152,7 @@ internal void InitializeSourceInfo(int iSkip, bool fNeedFileInfo, Exception? exc } } - public virtual MethodBase? GetMethodBase(int i) + public MethodBase? GetMethodBase(int i) { // There may be a better way to do this. // we got RuntimeMethodHandles here and we need to go to MethodBase @@ -169,17 +169,17 @@ internal void InitializeSourceInfo(int iSkip, bool fNeedFileInfo, Exception? exc return RuntimeType.GetMethodBase(mhReal); } - public virtual int GetOffset(int i) { return rgiOffset![i]; } - public virtual int GetILOffset(int i) { return rgiILOffset![i]; } - public virtual string? GetFilename(int i) { return rgFilename?[i]; } - public virtual int GetLineNumber(int i) { return rgiLineNumber == null ? 0 : rgiLineNumber[i]; } - public virtual int GetColumnNumber(int i) { return rgiColumnNumber == null ? 0 : rgiColumnNumber[i]; } + public int GetOffset(int i) { return rgiOffset![i]; } + public int GetILOffset(int i) { return rgiILOffset![i]; } + public string? GetFilename(int i) { return rgFilename?[i]; } + public int GetLineNumber(int i) { return rgiLineNumber == null ? 0 : rgiLineNumber[i]; } + public int GetColumnNumber(int i) { return rgiColumnNumber == null ? 0 : rgiColumnNumber[i]; } - public virtual bool IsLastFrameFromForeignExceptionStackTrace(int i) + public bool IsLastFrameFromForeignExceptionStackTrace(int i) { return (rgiLastFrameFromForeignExceptionStackTrace == null) ? false : rgiLastFrameFromForeignExceptionStackTrace[i]; } - public virtual int GetNumberOfFrames() { return iFrameCount; } + public int GetNumberOfFrames() { return iFrameCount; } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs index d6bb435387b07..81b9be41cdf89 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; -using System.Text; namespace System { @@ -323,14 +322,16 @@ internal DispatchState CaptureDispatchState() _remoteStackTraceString, _ipForWatsonBuckets, _watsonBuckets); } - [StackTraceHidden] - internal void SetCurrentStackTrace() + // Returns true if setting the _remoteStackTraceString field is legal, false if not (immutable exception). + // A false return value means the caller should early-exit the operation. + // Can also throw InvalidOperationException if a stack trace is already set or if object has been thrown. + private bool CanSetRemoteStackTrace() { // If this is a preallocated singleton exception, silently skip the operation, // regardless of the value of throwIfHasExistingStack. if (IsImmutableAgileException(this)) { - return; + return false; } // Check to see if the exception already has a stack set in it. @@ -339,13 +340,7 @@ internal void SetCurrentStackTrace() ThrowHelper.ThrowInvalidOperationException(); } - // Store the current stack trace into the "remote" stack trace, which was originally introduced to support - // remoting of exceptions cross app-domain boundaries, and is thus concatenated into Exception.StackTrace - // when it's retrieved. - var sb = new StringBuilder(256); - new StackTrace(fNeedFileInfo: true).ToString(System.Diagnostics.StackTrace.TraceFormat.TrailingNewLine, sb); - sb.AppendLine(SR.Exception_EndStackTraceFromPreviousThrow); - _remoteStackTraceString = sb.ToString(); + return true; } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs index 587738ba62c73..d1072ff86ec8a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/OleAutBinder.cs @@ -12,7 +12,7 @@ namespace System { // Made serializable in anticipation of this class eventually having state. - internal class OleAutBinder : DefaultBinder + internal sealed class OleAutBinder : DefaultBinder { // ChangeType // This binder uses OLEAUT to change the type of the variant. diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index e7b6f2e147dd6..4458fc4e6af2a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1,11 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; namespace System.Reflection { @@ -439,15 +440,33 @@ private void Init(object pca) #region Object Override public override string ToString() { - string ctorArgs = ""; - for (int i = 0; i < ConstructorArguments.Count; i++) - ctorArgs += string.Format(i == 0 ? "{0}" : ", {0}", ConstructorArguments[i]); + var vsb = new ValueStringBuilder(stackalloc char[256]); + + vsb.Append('['); + vsb.Append(Constructor.DeclaringType!.FullName); + vsb.Append('('); + + bool first = true; + + int count = ConstructorArguments.Count; + for (int i = 0; i < count; i++) + { + if (!first) vsb.Append(", "); + vsb.Append(ConstructorArguments[i].ToString()); + first = false; + } + + count = NamedArguments.Count; + for (int i = 0; i < count; i++) + { + if (!first) vsb.Append(", "); + vsb.Append(NamedArguments[i].ToString()); + first = false; + } - string namedArgs = ""; - for (int i = 0; i < NamedArguments.Count; i++) - namedArgs += string.Format(i == 0 && ctorArgs.Length == 0 ? "{0}" : ", {0}", NamedArguments[i]); + vsb.Append(")]"); - return string.Format("[{0}({1}{2})]", Constructor.DeclaringType!.FullName, ctorArgs, namedArgs); + return vsb.ToString(); } public override int GetHashCode() => base.GetHashCode(); public override bool Equals(object? obj) => obj == (object)this; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilderData.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilderData.cs index 4b44a4d21c905..3d1bf2ab0ca9a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilderData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilderData.cs @@ -10,7 +10,7 @@ namespace System.Reflection.Emit /// data member for AssemblyBuilder. Note that what ever data members added to /// this class cannot be accessed from the EE. /// - internal class AssemblyBuilderData + internal sealed class AssemblyBuilderData { public const int AssemblyDefToken = 0x20000001; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs index 3e11afaec3e16..79d0a260f8ac9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Emit { - internal class DynamicILGenerator : ILGenerator + internal sealed class DynamicILGenerator : ILGenerator { internal DynamicScope m_scope; private int m_methodSigToken; @@ -563,7 +563,7 @@ private int GetTokenForSig(byte[] sig) } - internal class DynamicResolver : Resolver + internal sealed class DynamicResolver : Resolver { #region Private Data Members private __ExceptionInfo[]? m_exceptions; @@ -643,7 +643,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo) scout.m_methodHandle = method.m_methodHandle.Value; } - private class DestroyScout + private sealed class DestroyScout { internal RuntimeMethodHandleInternal m_methodHandle; @@ -982,7 +982,7 @@ public int GetTokenFor(byte[] signature) #endregion } - internal class DynamicScope + internal sealed class DynamicScope { #region Private Data Members internal readonly List m_tokens = new List { null }; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ISymWrapperCore.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ISymWrapperCore.cs index 39f4eac6016ea..72e3edbd65bae 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ISymWrapperCore.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ISymWrapperCore.cs @@ -28,7 +28,7 @@ namespace System.Reflection.Emit // SymWrapperCore is never instantiated and is used as an encapsulation class. // It is our "ISymWrapper.dll" assembly within an assembly. //------------------------------------------------------------------------------ - internal class SymWrapperCore + internal sealed class SymWrapperCore { //------------------------------------------------------------------------------ // Block instantiation diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs index db05015f6a664..bf58dc10ac001 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/MethodBuilder.cs @@ -827,7 +827,7 @@ private void ParseCA(ConstructorInfo con) #endregion } - internal class LocalSymInfo + internal sealed class LocalSymInfo { // This class tracks the local variable's debugging information // and namespace information with a given active lexical scope. @@ -928,7 +928,7 @@ internal void AddUsingNamespace(string strNamespace) checked { m_iNameSpaceCount++; } } - internal virtual void EmitLocalSymInfo(ISymbolWriter symWriter) + internal void EmitLocalSymInfo(ISymbolWriter symWriter) { int i; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilderData.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilderData.cs index c925625cb9d71..f4048ea97c7fc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilderData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilderData.cs @@ -6,7 +6,7 @@ namespace System.Reflection.Emit // This is a package private class. This class hold all of the managed // data member for ModuleBuilder. Note that what ever data members added to // this class cannot be accessed from the EE. - internal class ModuleBuilderData + internal sealed class ModuleBuilderData { public const string MultiByteValueClass = "$ArrayType$"; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs index 88c8ccb85cbae..064e8edcd6eab 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/TypeBuilder.cs @@ -19,7 +19,7 @@ public override bool IsAssignableFrom([NotNullWhen(true)] TypeInfo? typeInfo) } #region Declarations - private class CustAttr + private sealed class CustAttr { private readonly ConstructorInfo? m_con; private readonly byte[]? m_binaryAttribute; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs index d31c719182d0d..9368fc25d340b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs @@ -552,14 +552,11 @@ public bool IsValidToken(int token) #endregion } - internal class MetadataException : Exception + internal sealed class MetadataException : Exception { private int m_hr; internal MetadataException(int hr) { m_hr = hr; } - public override string ToString() - { - return string.Format("MetadataException HResult = {0:x}.", m_hr); - } + public override string ToString() => $"MetadataException HResult = {m_hr:x}."; } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs index 1a7444ebae8ee..05805072cd7cf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.CoreCLR.cs @@ -6,7 +6,7 @@ namespace System.Resources { - internal partial class ManifestBasedResourceGroveler + internal sealed partial class ManifestBasedResourceGroveler { // Internal version of GetSatelliteAssembly that avoids throwing FileNotFoundException private static Assembly? InternalGetSatelliteAssembly(Assembly mainAssembly, diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs index 99b391172ddf4..9bb4628a3be64 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CrossLoaderAllocatorHashHelpers.cs @@ -10,7 +10,7 @@ namespace System.Runtime.CompilerServices /// data. /// [StructLayout(LayoutKind.Sequential)] - internal class LAHashDependentHashTracker + internal sealed class LAHashDependentHashTracker { private GCHandle _dependentHandle; private IntPtr _loaderAllocator; @@ -27,7 +27,7 @@ internal class LAHashDependentHashTracker /// to LAHashDependentHashTracker's /// [StructLayout(LayoutKind.Sequential)] - internal class LAHashKeyToTrackers + internal sealed class LAHashKeyToTrackers { private object? _trackerOrTrackerSet; private object? _laLocalKeyValueStore; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs index a5ce5ed1dc39b..82f99197a6a7e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GCHeapHash.cs @@ -10,7 +10,7 @@ namespace System.Runtime.CompilerServices /// by C++ runtime code which manages its memory in the GC heap. /// [StructLayout(LayoutKind.Sequential)] - internal class GCHeapHash + internal sealed class GCHeapHash { private Array? _data; private int _count; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 661b3d51be0dd..63f9108ee0e9b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -132,15 +132,13 @@ public static int OffsetToStringData // This method ensures that there is sufficient stack to execute the average Framework function. // If there is not enough stack, then it throws System.InsufficientExecutionStackException. - // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack - // below. + // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack. [MethodImpl(MethodImplOptions.InternalCall)] public static extern void EnsureSufficientExecutionStack(); // This method ensures that there is sufficient stack to execute the average Framework function. // If there is not enough stack, then it return false. - // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack - // below. + // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack. [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool TryEnsureSufficientExecutionStack(); @@ -364,7 +362,7 @@ private static unsafe void DispatchTailCalls( } // Helper class to assist with unsafe pinning of arbitrary objects. // It's used by VM code. - internal class RawData + internal sealed class RawData { public byte Data; } @@ -377,7 +375,7 @@ internal class RawData // The BaseSize of an array includes all the fields before the array data, // including the sync block and method table. The reference to RawData.Data // points at the number of components, skipping over these two pointer-sized fields. - internal class RawArrayData + internal sealed class RawArrayData { public uint Length; // Array._numComponents padded to IntPtr #if TARGET_64BIT diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsInfo.cs index ac434ad00d9bc..12035fd6e8eaa 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComEventsInfo.cs @@ -6,7 +6,7 @@ namespace System.Runtime.InteropServices { [SupportedOSPlatform("windows")] - internal class ComEventsInfo + internal sealed class ComEventsInfo { private ComEventsSink? _sinks; private readonly object _rcw; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumVariantViewOfEnumerator.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumVariantViewOfEnumerator.cs index 818b4b5bc11cf..c6a27d62c095f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumVariantViewOfEnumerator.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumVariantViewOfEnumerator.cs @@ -6,7 +6,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { - internal class EnumVariantViewOfEnumerator : ComTypes.IEnumVARIANT, ICustomAdapter + internal sealed class EnumVariantViewOfEnumerator : ComTypes.IEnumVARIANT, ICustomAdapter { public EnumVariantViewOfEnumerator(IEnumerator enumerator) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableToDispatchMarshaler.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableToDispatchMarshaler.cs index 3bd63b33612f8..adc6338feb544 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableToDispatchMarshaler.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableToDispatchMarshaler.cs @@ -7,7 +7,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { [SupportedOSPlatform("windows")] - internal class EnumerableToDispatchMarshaler : ICustomMarshaler + internal sealed class EnumerableToDispatchMarshaler : ICustomMarshaler { private static readonly EnumerableToDispatchMarshaler s_enumerableToDispatchMarshaler = new EnumerableToDispatchMarshaler(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableViewOfDispatch.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableViewOfDispatch.cs index a64da6919c2ed..d9d941138ca60 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableViewOfDispatch.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumerableViewOfDispatch.cs @@ -6,7 +6,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { - internal class EnumerableViewOfDispatch : ICustomAdapter, System.Collections.IEnumerable + internal sealed class EnumerableViewOfDispatch : ICustomAdapter, System.Collections.IEnumerable { // Reserved DISPID slot for getting an enumerator from an IDispatch-implementing COM interface. private const int DISPID_NEWENUM = -4; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorToEnumVariantMarshaler.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorToEnumVariantMarshaler.cs index b4718f5e7c285..997d22d343f09 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorToEnumVariantMarshaler.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorToEnumVariantMarshaler.cs @@ -8,7 +8,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { [SupportedOSPlatform("windows")] - internal class EnumeratorToEnumVariantMarshaler : ICustomMarshaler + internal sealed class EnumeratorToEnumVariantMarshaler : ICustomMarshaler { private static readonly EnumeratorToEnumVariantMarshaler s_enumeratorToEnumVariantMarshaler = new EnumeratorToEnumVariantMarshaler(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorViewOfEnumVariant.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorViewOfEnumVariant.cs index f0b725a7da0ea..a62fad1a0dc54 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorViewOfEnumVariant.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/EnumeratorViewOfEnumVariant.cs @@ -5,7 +5,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { - internal class EnumeratorViewOfEnumVariant : ICustomAdapter, System.Collections.IEnumerator + internal sealed class EnumeratorViewOfEnumVariant : ICustomAdapter, System.Collections.IEnumerator { private readonly IEnumVARIANT _enumVariantObject; private bool _fetchedLastObject; diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/ExpandoToDispatchExMarshaler.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/ExpandoToDispatchExMarshaler.cs index 07e7802e9d2eb..047bc24ef873a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/ExpandoToDispatchExMarshaler.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/ExpandoToDispatchExMarshaler.cs @@ -3,7 +3,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { - internal class ExpandoToDispatchExMarshaler : ICustomMarshaler + internal sealed class ExpandoToDispatchExMarshaler : ICustomMarshaler { private static readonly ExpandoToDispatchExMarshaler s_ExpandoToDispatchExMarshaler = new ExpandoToDispatchExMarshaler(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/TypeToTypeInfoMarshaler.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/TypeToTypeInfoMarshaler.cs index da82567e8c38d..afcd88075d8fc 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/TypeToTypeInfoMarshaler.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/CustomMarshalers/TypeToTypeInfoMarshaler.cs @@ -3,7 +3,7 @@ namespace System.Runtime.InteropServices.CustomMarshalers { - internal class TypeToTypeInfoMarshaler : ICustomMarshaler + internal sealed class TypeToTypeInfoMarshaler : ICustomMarshaler { private static readonly TypeToTypeInfoMarshaler s_typeToTypeInfoMarshaler = new TypeToTypeInfoMarshaler(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 7dfcb3a5e593a..6e99db321a05d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -209,8 +209,13 @@ internal static bool HasElementType(RuntimeType type) return outHandles; } - internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter) + internal static object CreateInstanceForAnotherGenericParameter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] RuntimeType type, + RuntimeType genericParameter) { + Debug.Assert(type.GetConstructor(Type.EmptyTypes) is ConstructorInfo c && c.IsPublic, + $"CreateInstanceForAnotherGenericParameter requires {nameof(type)} to have a public parameterless constructor so it can be annotated for trimming without preserving private constructors."); + object? instantiatedObject = null; IntPtr typeHandle = genericParameter.GetTypeHandleInternal().Value; @@ -224,8 +229,14 @@ internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type return instantiatedObject!; } - internal static object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter1, RuntimeType genericParameter2) + internal static object CreateInstanceForAnotherGenericParameter( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] RuntimeType type, + RuntimeType genericParameter1, + RuntimeType genericParameter2) { + Debug.Assert(type.GetConstructor(Type.EmptyTypes) is ConstructorInfo c && c.IsPublic, + $"CreateInstanceForAnotherGenericParameter requires {nameof(type)} to have a public parameterless constructor so it can be annotated for trimming without preserving private constructors."); + object? instantiatedObject = null; IntPtr* pTypeHandles = stackalloc IntPtr[] @@ -746,7 +757,7 @@ internal RuntimeMethodHandleInternal(IntPtr value) internal IntPtr m_handle; } - internal class RuntimeMethodInfoStub : IRuntimeMethodInfo + internal sealed class RuntimeMethodInfoStub : IRuntimeMethodInfo { public RuntimeMethodInfoStub(RuntimeMethodHandleInternal methodHandleValue, object keepalive) { @@ -1095,7 +1106,7 @@ RuntimeFieldHandleInternal Value } [StructLayout(LayoutKind.Sequential)] - internal class RuntimeFieldInfoStub : IRuntimeFieldInfo + internal sealed class RuntimeFieldInfoStub : IRuntimeFieldInfo { // These unused variables are used to ensure that this class has the same layout as RuntimeFieldInfo #pragma warning disable 414, 169 diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 415592ef78a02..a1d174667db10 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -134,7 +134,7 @@ public void Add(T item) } } - internal class RuntimeTypeCache + internal sealed class RuntimeTypeCache { private const int MAXNAMELEN = 1024; @@ -203,7 +203,7 @@ public uint GetHashToMatch() } } - private class MemberInfoCache where T : MemberInfo + private sealed class MemberInfoCache where T : MemberInfo { #region Private Data Members @@ -1939,18 +1939,18 @@ internal static void ValidateGenericArguments(MemberInfo definition, RuntimeType { RuntimeType[]? typeContext = null; RuntimeType[]? methodContext = null; - RuntimeType[] genericParamters; + RuntimeType[] genericParameters; if (definition is Type) { RuntimeType genericTypeDefinition = (RuntimeType)definition; - genericParamters = genericTypeDefinition.GetGenericArgumentsInternal(); + genericParameters = genericTypeDefinition.GetGenericArgumentsInternal(); typeContext = genericArguments; } else { RuntimeMethodInfo genericMethodDefinition = (RuntimeMethodInfo)definition; - genericParamters = genericMethodDefinition.GetGenericArgumentsInternal(); + genericParameters = genericMethodDefinition.GetGenericArgumentsInternal(); methodContext = genericArguments; RuntimeType? declaringType = (RuntimeType?)genericMethodDefinition.DeclaringType; @@ -1963,7 +1963,7 @@ internal static void ValidateGenericArguments(MemberInfo definition, RuntimeType for (int i = 0; i < genericArguments.Length; i++) { Type genericArgument = genericArguments[i]; - Type genericParameter = genericParamters[i]; + Type genericParameter = genericParameters[i]; if (!RuntimeTypeHandle.SatisfiesConstraints(genericParameter.GetTypeHandleInternal().GetTypeChecked(), typeContext, methodContext, genericArgument.GetTypeHandleInternal().GetTypeChecked())) @@ -3920,7 +3920,7 @@ namespace System.Reflection // reliable in the presence of asynchronous exceptions. internal struct CerHashtable where K : class { - private class Table + private sealed class Table { // Note that m_keys and m_values arrays are immutable to allow lock-free reads. A new instance // of CerHashtable has to be allocated to grow the size of the hashtable. diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs index 06df433df4660..d856d1d9231b3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/ClrThreadPoolPreAllocatedOverlapped.cs @@ -95,5 +95,7 @@ unsafe void IDeferredDisposable.OnFinalRelease(bool disposed) } } } + + internal bool IsUserObject(byte[]? buffer) => _overlapped.IsUserObject(buffer); } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs index 728954377dff8..53abea5f0d16d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Overlapped.cs @@ -132,6 +132,8 @@ internal sealed unsafe class OverlappedData return AllocateNativeOverlapped(); } + internal bool IsUserObject(byte[]? buffer) => ReferenceEquals(_userObject, buffer); + [MethodImpl(MethodImplOptions.InternalCall)] private extern NativeOverlapped* AllocateNativeOverlapped(); @@ -258,6 +260,8 @@ public static unsafe void Free(NativeOverlapped* nativeOverlappedPtr) OverlappedData.GetOverlappedFromNative(nativeOverlappedPtr)._overlapped._overlappedData = null; OverlappedData.FreeNativeOverlapped(nativeOverlappedPtr); } + + internal bool IsUserObject(byte[]? buffer) => _overlappedData!.IsUserObject(buffer); } #endregion class Overlapped diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 1b79747dc0c9a..c1a968ed9f994 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -119,8 +119,6 @@ private void StartCallback() [MethodImpl(MethodImplOptions.InternalCall)] private static extern void SleepInternal(int millisecondsTimeout); - public static void Sleep(int millisecondsTimeout) => SleepInternal(millisecondsTimeout); - [DllImport(RuntimeHelpers.QCall)] internal static extern void UninterruptibleSleep0(); @@ -221,9 +219,6 @@ public ThreadPriority Priority [MethodImpl(MethodImplOptions.InternalCall)] private extern void SetPriorityNative(int priority); - /// Returns the operating system identifier for the current thread. - internal static ulong CurrentOSThreadId => GetCurrentOSThreadId(); - [DllImport(RuntimeHelpers.QCall)] private static extern ulong GetCurrentOSThreadId(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs index e13cd3afe2474..ca4975f59229e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs @@ -8,7 +8,7 @@ namespace System.Threading { - internal partial class TimerQueue + internal sealed partial class TimerQueue { #region interface to native per-AppDomain timer diff --git a/src/coreclr/ToolBox/SOS/CMakeLists.txt b/src/coreclr/ToolBox/SOS/CMakeLists.txt index 1474c5466f0a6..a8b5112894774 100644 --- a/src/coreclr/ToolBox/SOS/CMakeLists.txt +++ b/src/coreclr/ToolBox/SOS/CMakeLists.txt @@ -1 +1 @@ -_install(FILES SOS_README.md DESTINATION .) +install(FILES SOS_README.md DESTINATION .) diff --git a/src/coreclr/ToolBox/SOS/DacTableGen/DacTableGen.csproj b/src/coreclr/ToolBox/SOS/DacTableGen/DacTableGen.csproj index 1217406b7b1f3..956711339c6f6 100644 --- a/src/coreclr/ToolBox/SOS/DacTableGen/DacTableGen.csproj +++ b/src/coreclr/ToolBox/SOS/DacTableGen/DacTableGen.csproj @@ -8,22 +8,21 @@ false - - - - - - "$([MSBuild]::NormalizePath('$(Pkgvswhere)','tools','vswhere.exe'))" - + + <_MsDiaSubDir>$(BuildArchitecture) <_MsDiaSubDir Condition="'$(BuildArchitecture)' == 'x86'" /> diff --git a/src/coreclr/ToolBox/superpmi/mcs/CMakeLists.txt b/src/coreclr/ToolBox/superpmi/mcs/CMakeLists.txt index e928c48d0cad2..56dcaff658cc5 100644 --- a/src/coreclr/ToolBox/superpmi/mcs/CMakeLists.txt +++ b/src/coreclr/ToolBox/superpmi/mcs/CMakeLists.txt @@ -47,7 +47,7 @@ set(MCS_SOURCES ../superpmi-shared/spmidumphelper.cpp ) -_add_executable(mcs +add_executable_clr(mcs ${MCS_SOURCES} ) target_precompile_headers(mcs PRIVATE "$<$:standardpch.h>") @@ -66,7 +66,6 @@ else() ${STATIC_MT_CPP_LIB} ) - _install (FILES $ DESTINATION PDB) endif(CLR_CMAKE_HOST_UNIX) -_install (TARGETS mcs DESTINATION .) +install_clr(TARGETS mcs DESTINATIONS .) diff --git a/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp b/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp index f5f9d7786a745..a40707846887d 100644 --- a/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp +++ b/src/coreclr/ToolBox/superpmi/mcs/verbjitflags.cpp @@ -24,6 +24,26 @@ int verbJitFlags::DoWork(const char* nameOfInput) mc->repGetJitFlags(&corJitFlags, sizeof(corJitFlags)); unsigned long long rawFlags = corJitFlags.GetFlagsRaw(); + // We co-opt unused flag bits to note if there's pgo data, + // and if so, what kind + // + bool hasEdgeProfile = false; + bool hasClassProfile = false; + if (mc->hasPgoData(hasEdgeProfile, hasClassProfile)) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_PGO); + + if (hasEdgeProfile) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_EDGE_PROFILE); + } + + if (hasClassProfile) + { + rawFlags |= 1ULL << (EXTRA_JIT_FLAGS::HAS_CLASS_PROFILE); + } + } + int index = flagMap.GetIndex(rawFlags); if (index == -1) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/logging.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/logging.cpp index 86da8f3e4ef92..1a1ef7019435c 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/logging.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/logging.cpp @@ -173,6 +173,12 @@ void Logger::LogVprintf( __debugbreak(); } + // Early out if we're not logging at this level. + if ((level & GetLogLevel()) == 0) + { + return; + } + // Capture this first to make the timestamp more accurately reflect the actual time of logging time_t timestamp = time(nullptr); @@ -221,64 +227,61 @@ void Logger::LogVprintf( EnterCriticalSection(&s_critSec); - if (level & GetLogLevel()) - { - // Sends error messages to stderr instead out stdout - FILE* dest = (level <= LOGLEVEL_WARNING) ? stderr : stdout; + // Sends error messages to stderr instead out stdout + FILE* dest = (level <= LOGLEVEL_WARNING) ? stderr : stdout; - if (level < LOGLEVEL_INFO) - fprintf(dest, "%s: ", logLevelStr); + if (level < LOGLEVEL_INFO) + fprintf(dest, "%s: ", logLevelStr); - fprintf(dest, "%s\n", fullMsg); + fprintf(dest, "%s\n", fullMsg); - if (s_logFile != INVALID_HANDLE_VALUE) - { + if (s_logFile != INVALID_HANDLE_VALUE) + { #ifndef TARGET_UNIX // TODO: no localtime_s() or strftime() in PAL - tm timeInfo; - errno_t err = localtime_s(&timeInfo, ×tamp); - if (err != 0) - { - fprintf(stderr, "WARNING: [Logger::LogVprintf] localtime failed with error %d.\n", err); - goto CleanUp; - } - - size_t timeStrBuffSize = 20 * sizeof(char); - char* timeStr = (char*)malloc(timeStrBuffSize); // Use malloc so we can realloc if necessary - - // This particular format string should always generate strings of the same size, but - // for the sake of robustness, we shouldn't rely on that assumption. - while (strftime(timeStr, timeStrBuffSize, "%Y-%m-%d %H:%M:%S", &timeInfo) == 0) - { - timeStrBuffSize *= 2; - timeStr = (char*)realloc(timeStr, timeStrBuffSize); - } + tm timeInfo; + errno_t err = localtime_s(&timeInfo, ×tamp); + if (err != 0) + { + fprintf(stderr, "WARNING: [Logger::LogVprintf] localtime failed with error %d.\n", err); + goto CleanUp; + } + + size_t timeStrBuffSize = 20 * sizeof(char); + char* timeStr = (char*)malloc(timeStrBuffSize); // Use malloc so we can realloc if necessary + + // This particular format string should always generate strings of the same size, but + // for the sake of robustness, we shouldn't rely on that assumption. + while (strftime(timeStr, timeStrBuffSize, "%Y-%m-%d %H:%M:%S", &timeInfo) == 0) + { + timeStrBuffSize *= 2; + timeStr = (char*)realloc(timeStr, timeStrBuffSize); + } #else // TARGET_UNIX - const char* timeStr = ""; + const char* timeStr = ""; #endif // TARGET_UNIX - const char logEntryFmtStr[] = "%s - %s [%s:%d] - %s - %s\r\n"; - size_t logEntryBuffSize = sizeof(logEntryFmtStr) + strlen(timeStr) + strlen(function) + strlen(file) + 10 + - strlen(logLevelStr) + strlen(fullMsg); + const char logEntryFmtStr[] = "%s - %s [%s:%d] - %s - %s\r\n"; + size_t logEntryBuffSize = sizeof(logEntryFmtStr) + strlen(timeStr) + strlen(function) + strlen(file) + 10 + + strlen(logLevelStr) + strlen(fullMsg); - char* logEntry = new char[logEntryBuffSize]; - sprintf_s(logEntry, logEntryBuffSize, logEntryFmtStr, timeStr, function, file, line, logLevelStr, fullMsg); + char* logEntry = new char[logEntryBuffSize]; + sprintf_s(logEntry, logEntryBuffSize, logEntryFmtStr, timeStr, function, file, line, logLevelStr, fullMsg); - DWORD bytesWritten; + DWORD bytesWritten; - if (!WriteFile(s_logFile, logEntry, (DWORD)logEntryBuffSize - 1, &bytesWritten, nullptr)) - fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to write to log file. GetLastError()=%u\n", - GetLastError()); + if (!WriteFile(s_logFile, logEntry, (DWORD)logEntryBuffSize - 1, &bytesWritten, nullptr)) + fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to write to log file. GetLastError()=%u\n", + GetLastError()); - if (!FlushFileBuffers(s_logFile)) - fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to flush log file. GetLastError()=%u\n", - GetLastError()); + if (!FlushFileBuffers(s_logFile)) + fprintf(stderr, "WARNING: [Logger::LogVprintf] Failed to flush log file. GetLastError()=%u\n", + GetLastError()); - delete[] logEntry; + delete[] logEntry; #ifndef TARGET_UNIX - free((void*)timeStr); + free((void*)timeStr); #endif // !TARGET_UNIX - } } #ifndef TARGET_UNIX diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp index 0a7da7e4b56c2..7c7296b803634 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.cpp @@ -2685,8 +2685,11 @@ void MethodContext::dmpGetArgNext(DWORDLONG key, DWORDLONG value) } CORINFO_ARG_LIST_HANDLE MethodContext::repGetArgNext(CORINFO_ARG_LIST_HANDLE args) { - CORINFO_ARG_LIST_HANDLE temp = (CORINFO_ARG_LIST_HANDLE)GetArgNext->Get(CastHandle(args)); - DEBUG_REP(dmpGetArgNext(CastHandle(args), CastHandle(temp))); + DWORDLONG key = CastHandle(args); + AssertCodeMsg(GetArgNext != nullptr, EXCEPTIONCODE_MC, "Didn't find %016llx", key); + AssertCodeMsg(GetArgNext->GetIndex(key) != -1, EXCEPTIONCODE_MC, "Didn't find %016llx", key); + CORINFO_ARG_LIST_HANDLE temp = (CORINFO_ARG_LIST_HANDLE)GetArgNext->Get(key); + DEBUG_REP(dmpGetArgNext(key, CastHandle(temp))); return temp; } void MethodContext::recGetMethodSig(CORINFO_METHOD_HANDLE ftn, CORINFO_SIG_INFO* sig, CORINFO_CLASS_HANDLE memberParent) @@ -6765,6 +6768,44 @@ int MethodContext::dumpMD5HashToBuffer(BYTE* pBuffer, int bufLen, char* hash, in return m_hash.HashBuffer(pBuffer, bufLen, hash, hashLen); } +bool MethodContext::hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile) +{ + hasEdgeProfile = false; + hasClassProfile = false; + + // Obtain the Method Info structure for this method + CORINFO_METHOD_INFO info; + unsigned flags = 0; + repCompileMethod(&info, &flags); + + if ((GetPgoInstrumentationResults != nullptr) && + (GetPgoInstrumentationResults->GetIndex(CastHandle(info.ftn)) != -1)) + { + ICorJitInfo::PgoInstrumentationSchema* schema = nullptr; + UINT32 schemaCount = 0; + BYTE* schemaData = nullptr; + HRESULT pgoHR = repGetPgoInstrumentationResults(info.ftn, &schema, &schemaCount, &schemaData); + + if (SUCCEEDED(pgoHR)) + { + for (UINT32 i = 0; i < schemaCount; i++) + { + hasEdgeProfile |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::EdgeIntCount); + hasClassProfile |= (schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramCount); + + if (hasEdgeProfile && hasClassProfile) + { + break; + } + } + + return true; + } + } + + return false; +} + MethodContext::Environment MethodContext::cloneEnvironment() { MethodContext::Environment env; diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h index 4851d36966953..d71f8d35f3b33 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/methodcontext.h @@ -43,6 +43,21 @@ const char* toString(CorInfoType cit); #define METHOD_IDENTITY_INFO_SIZE 0x10000 // We assume that the METHOD_IDENTITY_INFO_SIZE will not exceed 64KB +// Special "jit flags" for noting some method context features + +enum EXTRA_JIT_FLAGS +{ + HAS_PGO = 63, + HAS_EDGE_PROFILE = 62, + HAS_CLASS_PROFILE = 61 +}; + +// Asserts to catch changes in corjit flags definitions. + +static_assert((int)EXTRA_JIT_FLAGS::HAS_PGO == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED36, "Jit Flags Mismatch"); +static_assert((int)EXTRA_JIT_FLAGS::HAS_EDGE_PROFILE == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED35, "Jit Flags Mismatch"); +static_assert((int)EXTRA_JIT_FLAGS::HAS_CLASS_PROFILE == (int)CORJIT_FLAGS::CorJitFlag::CORJIT_FLAG_UNUSED34, "Jit Flags Mismatch"); + class MethodContext { public: @@ -82,6 +97,8 @@ class MethodContext int dumpMethodIdentityInfoToBuffer(char* buff, int len, bool ignoreMethodName = false, CORINFO_METHOD_INFO* optInfo = nullptr, unsigned optFlags = 0); int dumpMethodMD5HashToBuffer(char* buff, int len, bool ignoreMethodName = false, CORINFO_METHOD_INFO* optInfo = nullptr, unsigned optFlags = 0); + bool hasPgoData(bool& hasEdgeProfile, bool& hasClassProfile); + void recGlobalContext(const MethodContext& other); void dmpEnvironment(DWORD key, const Agnostic_Environment& value); diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp index 3530d369507ec..da63826e750b6 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/spmidumphelper.cpp @@ -279,6 +279,12 @@ std::string SpmiDumpHelper::DumpJitFlags(unsigned long long flags) AddFlag(NO_INLINING); + // "Extra jit flag" support + // + AddFlagNumeric(HAS_PGO, EXTRA_JIT_FLAGS::HAS_PGO); + AddFlagNumeric(HAS_EDGE_PROFILE, EXTRA_JIT_FLAGS::HAS_EDGE_PROFILE); + AddFlagNumeric(HAS_CLASS_PROFILE, EXTRA_JIT_FLAGS::HAS_CLASS_PROFILE); + #undef AddFlag #undef AddFlagNumeric diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shared/spmiutil.cpp index 0d52ae35ed1ab..efbe6f13e1cd1 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shared/spmiutil.cpp @@ -156,58 +156,76 @@ void ReplaceIllegalCharacters(WCHAR* fileName) // All lengths in this function exclude the terminal NULL. WCHAR* GetResultFileName(const WCHAR* folderPath, const WCHAR* fileName, const WCHAR* extension) { - const size_t folderPathLength = wcslen(folderPath); - const size_t fileNameLength = wcslen(fileName); const size_t extensionLength = wcslen(extension); - const size_t maxPathLength = MAX_PATH - 50; // subtract 50 because excel doesn't like paths longer then 230. + const size_t fileNameLength = wcslen(fileName); const size_t randomStringLength = 8; + const size_t maxPathLength = MAX_PATH - 50; + bool appendRandomString = false; - size_t fullPathLength = folderPathLength + 1 + extensionLength; - bool appendRandomString = false; + // See how long the folder part is, and start building the file path with the folder part. + // + WCHAR* fullPath = new WCHAR[MAX_PATH]; + fullPath[0] = W('\0'); + const size_t folderPathLength = GetFullPathNameW(folderPath, MAX_PATH, (LPWSTR)fullPath, NULL); - if (fileNameLength > 0) - { - fullPathLength += fileNameLength; - } - else + if (folderPathLength == 0) { - fullPathLength += randomStringLength; - appendRandomString = true; + LogError("GetResultFileName - can't resolve folder path '%ws'", folderPath); + return nullptr; } - size_t charsToDelete = 0; + // Account for the folder, directory separator and extension. + // + size_t fullPathLength = folderPathLength + 1 + extensionLength; - if (fullPathLength > maxPathLength) + // If we won't have room for a minimal file name part, bail. + // + if ((fullPathLength + randomStringLength) > maxPathLength) { - // The path name is too long; creating the file will fail. This can happen because we use the command line, - // which for ngen includes lots of environment variables, for example. - // Shorten the file name and add a random string to the end to avoid collisions. + LogError("GetResultFileName - folder path '%ws' length + minimal file name exceeds limit %d", fullPath, maxPathLength); + return nullptr; + } - charsToDelete = fullPathLength - maxPathLength + randomStringLength; + // Now figure out the file name part. + // + const size_t maxFileNameLength = maxPathLength - fullPathLength; + size_t usableFileNameLength = 0; - if (fileNameLength >= charsToDelete) - { - appendRandomString = true; - fullPathLength = maxPathLength; - } - else - { - LogError("GetResultFileName - path to the output file is too long '%ws\\%ws.%ws(%d)'", folderPath, fileName, extension, fullPathLength); - return nullptr; - } + if (fileNameLength == 0) + { + // No file name provided. Use random string. + // + fullPathLength += randomStringLength; + appendRandomString = true; + } + else if (fileNameLength < maxFileNameLength) + { + // Reasonable length file name, use as is. + // + usableFileNameLength = fileNameLength; + fullPathLength += fileNameLength; + appendRandomString = false; + } + else + { + // Overly long file name, truncate and add random string. + // + usableFileNameLength = maxFileNameLength - randomStringLength; + fullPathLength += maxFileNameLength; + appendRandomString = true; } - WCHAR* fullPath = new WCHAR[fullPathLength + 1]; - fullPath[0] = W('\0'); - wcsncat_s(fullPath, fullPathLength + 1, folderPath, folderPathLength); + // Append the file name part + // wcsncat_s(fullPath, fullPathLength + 1, DIRECTORY_SEPARATOR_STR_W, 1); + wcsncat_s(fullPath, fullPathLength + 1, fileName, usableFileNameLength); - if (fileNameLength > charsToDelete) - { - wcsncat_s(fullPath, fullPathLength + 1, fileName, fileNameLength - charsToDelete); - ReplaceIllegalCharacters(fullPath + folderPathLength + 1); - } + // Clean up anything in the file part that can't be in a file name. + // + ReplaceIllegalCharacters(fullPath + folderPathLength + 1); + // Append random string, if we're using it. + // if (appendRandomString) { unsigned randomNumber = 0; @@ -223,6 +241,8 @@ WCHAR* GetResultFileName(const WCHAR* folderPath, const WCHAR* fileName, const W wcsncat_s(fullPath, fullPathLength + 1, randomString, randomStringLength); } + // Append extension + // wcsncat_s(fullPath, fullPathLength + 1, extension, extensionLength); return fullPath; diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt index 27ab46758f821..1eb640c4cb9c0 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/CMakeLists.txt @@ -39,7 +39,7 @@ if (CLR_CMAKE_TARGET_WIN32) list(APPEND SUPERPMI_SHIM_COLLECTOR_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-collector.def) endif (CLR_CMAKE_TARGET_WIN32) -_add_library(superpmi-shim-collector +add_library_clr(superpmi-shim-collector SHARED ${SUPERPMI_SHIM_COLLECTOR_SOURCES} ) @@ -59,8 +59,6 @@ else() ${STATIC_MT_CRT_LIB} ${STATIC_MT_CPP_LIB} ) - - _install (FILES $ DESTINATION PDB) endif(CLR_CMAKE_HOST_UNIX) -_install (PROGRAMS $ DESTINATION .) +install_clr(TARGETS superpmi-shim-collector DESTINATIONS .) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 9f5f1e912a479..85cca8a129d77 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1710,6 +1710,13 @@ void interceptor_ICJI::getCallInfo( param.flags = flags; param.pResult = pResult; +#ifdef HOST_UNIX + // We don't seem to be able to capture the exception code in PAL exceptions when thrown + // from crossgen2. So assume there will be some error, then set it to zero (no error) + // if the `getCallInfo` call doesn't throw. + param.exceptionCode = 1; +#endif // HOST_UNIX + PAL_TRY(Param*, pOuterParam, ¶m) { PAL_TRY(Param*, pParam, pOuterParam) @@ -1717,6 +1724,9 @@ void interceptor_ICJI::getCallInfo( pParam->pThis->mc->cr->AddCall("getCallInfo"); pParam->pThis->original_ICorJitInfo->getCallInfo(pParam->pResolvedToken, pParam->pConstrainedResolvedToken, pParam->callerHandle, pParam->flags, pParam->pResult); +#ifdef HOST_UNIX + pParam->exceptionCode = 0; +#endif // HOST_UNIX } PAL_EXCEPT_FILTER(FilterSuperPMIExceptions_CaptureExceptionAndContinue) { diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp index 1b0363ec8593f..1731ecf3d4dff 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-collector/superpmi-shim-collector.cpp @@ -27,6 +27,7 @@ WCHAR* g_DefaultRealJitPath = nullptr; MethodContext* g_globalContext = nullptr; WCHAR* g_debugRecStr = nullptr; WCHAR* g_debugRepStr = nullptr; +bool g_initialized = false; void SetDefaultPaths() { @@ -102,6 +103,28 @@ void SetDebugVariables() } } +void InitializeShim() +{ + if (g_initialized) + { + return; + } + +#ifdef HOST_UNIX + if (0 != PAL_InitializeDLL()) + { + fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n"); + exit(1); + } +#endif // HOST_UNIX + + Logger::Initialize(); + SetLogFilePath(); + Logger::OpenLogFile(g_logFilePath); + + g_initialized = true; +} + extern "C" #ifdef HOST_UNIX DLLEXPORT // For Win32 PAL LoadLibrary emulation @@ -112,17 +135,7 @@ extern "C" switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: -#ifdef HOST_UNIX - if (0 != PAL_InitializeDLL()) - { - fprintf(stderr, "Error: Fail to PAL_InitializeDLL\n"); - exit(1); - } -#endif // HOST_UNIX - - Logger::Initialize(); - SetLogFilePath(); - Logger::OpenLogFile(g_logFilePath); + InitializeShim(); break; case DLL_PROCESS_DETACH: @@ -142,6 +155,9 @@ extern "C" extern "C" DLLEXPORT void __stdcall jitStartup(ICorJitHost* host) { + // crossgen2 doesn't invoke DllMain on Linux/Mac (under PAL), so optionally do initialization work here. + InitializeShim(); + SetDefaultPaths(); SetLibName(); SetDebugVariables(); diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt index 57a42a5ba031b..602a6751b02da 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-counter/CMakeLists.txt @@ -41,7 +41,7 @@ if (CLR_CMAKE_TARGET_WIN32) list(APPEND SUPERPMI_SHIM_COUNTER_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-counter.def) endif (CLR_CMAKE_TARGET_WIN32) -_add_library(superpmi-shim-counter +add_library_clr(superpmi-shim-counter SHARED ${SUPERPMI_SHIM_COUNTER_SOURCES} ) @@ -61,8 +61,6 @@ else() ${STATIC_MT_CRT_LIB} ${STATIC_MT_CPP_LIB} ) - - _install (FILES $ DESTINATION PDB) endif(CLR_CMAKE_HOST_UNIX) -_install (PROGRAMS $ DESTINATION .) +install_clr(TARGETS superpmi-shim-counter DESTINATIONS .) diff --git a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt index 58afa789de1a1..b6f4d52ea4cab 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt +++ b/src/coreclr/ToolBox/superpmi/superpmi-shim-simple/CMakeLists.txt @@ -40,7 +40,7 @@ if (CLR_CMAKE_TARGET_WIN32) list(APPEND SUPERPMI_SHIM_SIMPLE_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/superpmi-shim-simple.def) endif (CLR_CMAKE_TARGET_WIN32) -_add_library(superpmi-shim-simple +add_library_clr(superpmi-shim-simple SHARED ${SUPERPMI_SHIM_SIMPLE_SOURCES} ) @@ -60,8 +60,6 @@ else() ${STATIC_MT_CRT_LIB} ${STATIC_MT_CPP_LIB} ) - - _install (FILES $ DESTINATION PDB) endif(CLR_CMAKE_HOST_UNIX) -_install (PROGRAMS $ DESTINATION .) +install_clr(TARGETS superpmi-shim-simple DESTINATIONS .) diff --git a/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt b/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt index 3bc6ca865572f..92cda6d5600fe 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt +++ b/src/coreclr/ToolBox/superpmi/superpmi/CMakeLists.txt @@ -41,7 +41,7 @@ set(SUPERPMI_SOURCES ../superpmi-shared/spmidumphelper.cpp ) -_add_executable(superpmi +add_executable_clr(superpmi ${SUPERPMI_SOURCES} ) @@ -61,8 +61,6 @@ else() ${STATIC_MT_CRT_LIB} ${STATIC_MT_CPP_LIB} ) - - _install (FILES $ DESTINATION PDB) endif(CLR_CMAKE_HOST_UNIX) -_install (TARGETS superpmi DESTINATION .) +install_clr(TARGETS superpmi DESTINATIONS .) diff --git a/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp b/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp index 0646d3adc6383..35a40f9d2b9af 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/commandline.cpp @@ -100,6 +100,10 @@ void CommandLine::DumpHelp(const char* program) printf(" If 'workerCount' is not specified, the number of workers used is\n"); printf(" the number of processors on the machine.\n"); printf("\n"); + printf(" -failureLimit \n"); + printf(" For a positive 'limit' number, replay and asm diffs will exit if it sees more than 'limit' failures.\n"); + printf(" Otherwise, all methods will be compiled.\n"); + printf("\n"); printf(" -skipCleanup\n"); printf(" Skip deletion of temporary files created by child SuperPMI processes with -parallel.\n"); printf("\n"); @@ -471,6 +475,24 @@ bool CommandLine::Parse(int argc, char* argv[], /* OUT */ Options* o) } } } + else if ((_strnicmp(&argv[i][1], "failureLimit", argLen) == 0)) + { + if (++i >= argc) + { + DumpHelp(argv[0]); + return false; + } + + o->failureLimit = atoi(argv[i]); + + if (o->failureLimit < 1) + { + LogError( + "Incorrect limit specified for -failureLimit. Limit must be > 0."); + DumpHelp(argv[0]); + return false; + } + } else if ((_stricmp(&argv[i][1], "skipCleanup") == 0)) { o->skipCleanup = true; diff --git a/src/coreclr/ToolBox/superpmi/superpmi/commandline.h b/src/coreclr/ToolBox/superpmi/superpmi/commandline.h index ac237c9afd419..d5404b1263da6 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/commandline.h +++ b/src/coreclr/ToolBox/superpmi/superpmi/commandline.h @@ -31,6 +31,7 @@ class CommandLine , skipCleanup(false) , workerCount(-1) , indexCount(-1) + , failureLimit(-1) , indexes(nullptr) , hash(nullptr) , methodStatsTypes(nullptr) @@ -60,6 +61,7 @@ class CommandLine bool skipCleanup; // In /parallel mode, do we skip cleanup of temporary files? Used for debugging /parallel. int workerCount; // Number of workers to use for /parallel mode. -1 (or 1) means don't use parallel mode. int indexCount; // If indexCount is -1 and hash points to nullptr it means compile all. + int failureLimit; // Number of failures after which bail out the replay/asmdiffs. int* indexes; char* hash; char* methodStatsTypes; diff --git a/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp b/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp index ba8584617de20..1eb1a7c2f5101 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/parallelsuperpmi.cpp @@ -210,6 +210,7 @@ void ProcessChildStdOut(const CommandLine::Options& o, int* jitted, int* failed, int* excluded, + int* missing, int* diffs, bool* usageError) { @@ -253,13 +254,13 @@ void ProcessChildStdOut(const CommandLine::Options& o, } else if (strncmp(buff, g_AllFormatStringFixedPrefix, strlen(g_AllFormatStringFixedPrefix)) == 0) { - int childLoaded = 0, childJitted = 0, childFailed = 0, childExcluded = 0; + int childLoaded = 0, childJitted = 0, childFailed = 0, childExcluded = 0, childMissing = 0; if (o.applyDiff) { int childDiffs = 0; int converted = sscanf_s(buff, g_AsmDiffsSummaryFormatString, &childLoaded, &childJitted, &childFailed, - &childExcluded, &childDiffs); - if (converted != 5) + &childExcluded, &childMissing, &childDiffs); + if (converted != 6) { LogError("Couldn't parse status message: \"%s\"", buff); continue; @@ -269,8 +270,8 @@ void ProcessChildStdOut(const CommandLine::Options& o, else { int converted = - sscanf_s(buff, g_SummaryFormatString, &childLoaded, &childJitted, &childFailed, &childExcluded); - if (converted != 4) + sscanf_s(buff, g_SummaryFormatString, &childLoaded, &childJitted, &childFailed, &childExcluded, &childMissing); + if (converted != 5) { LogError("Couldn't parse status message: \"%s\"", buff); continue; @@ -281,6 +282,7 @@ void ProcessChildStdOut(const CommandLine::Options& o, *jitted += childJitted; *failed += childFailed; *excluded += childExcluded; + *missing += childMissing; } } @@ -616,14 +618,14 @@ int doParallelSuperPMI(CommandLine::Options& o) bool usageError = false; // variable to flag if we hit a usage error in SuperPMI - int loaded = 0, jitted = 0, failed = 0, excluded = 0, diffs = 0; + int loaded = 0, jitted = 0, failed = 0, excluded = 0, missing = 0, diffs = 0; // Read the stderr files and log them as errors // Read the stdout files and parse them for counts and log any MISSING or ISSUE errors for (int i = 0; i < o.workerCount; i++) { ProcessChildStdErr(arrStdErrorPath[i]); - ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &excluded, &diffs, &usageError); + ProcessChildStdOut(o, arrStdOutputPath[i], &loaded, &jitted, &failed, &excluded, &missing, &diffs, &usageError); if (usageError) break; } @@ -644,11 +646,11 @@ int doParallelSuperPMI(CommandLine::Options& o) { if (o.applyDiff) { - LogInfo(g_AsmDiffsSummaryFormatString, loaded, jitted, failed, excluded, diffs); + LogInfo(g_AsmDiffsSummaryFormatString, loaded, jitted, failed, excluded, missing, diffs); } else { - LogInfo(g_SummaryFormatString, loaded, jitted, failed, excluded); + LogInfo(g_SummaryFormatString, loaded, jitted, failed, excluded, missing); } } diff --git a/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp b/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp index 2b741a45f1eea..819e586dc289f 100644 --- a/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp +++ b/src/coreclr/ToolBox/superpmi/superpmi/superpmi.cpp @@ -25,8 +25,8 @@ extern int doParallelSuperPMI(CommandLine::Options& o); // There must be a single, fixed prefix common to all strings, to ease the determination of when // to parse the string fully. const char* const g_AllFormatStringFixedPrefix = "Loaded "; -const char* const g_SummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d"; -const char* const g_AsmDiffsSummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d Diffs %d"; +const char* const g_SummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d Missing %d"; +const char* const g_AsmDiffsSummaryFormatString = "Loaded %d Jitted %d FailedCompile %d Excluded %d Missing %d Diffs %d"; //#define SuperPMI_ChewMemory 0x7FFFFFFF //Amount of address space to consume on startup @@ -244,6 +244,7 @@ int __cdecl main(int argc, char* argv[]) int matchCount = 0; int failToReplayCount = 0; int errorCount = 0; + int errorCount2 = 0; int missingCount = 0; int index = 0; int excludedCount = 0; @@ -384,8 +385,14 @@ int __cdecl main(int argc, char* argv[]) if (res2 == JitInstance::RESULT_ERROR) { - LogError("JIT2 main method %d of size %d failed to load and compile correctly.", + errorCount2++; + LogError("Method %d of size %d failed to load and compile correctly by JIT2.", reader->GetMethodContextIndex(), mc->methodSize); + if (errorCount2 == o.failureLimit) + { + LogError("More than %d methods compilation failed by JIT2. Skip compiling remaining methods.", o.failureLimit); + break; + } } // Methods that don't compile due to missing JIT-EE information @@ -534,6 +541,11 @@ int __cdecl main(int argc, char* argv[]) errorCount++; LogError("main method %d of size %d failed to load and compile correctly.", reader->GetMethodContextIndex(), mc->methodSize); + if (errorCount == o.failureLimit) + { + LogError("More than %d methods failed. Skip compiling remaining methods.", o.failureLimit); + break; + } if ((o.reproName != nullptr) && (o.indexCount == -1)) { char buff[500]; @@ -576,11 +588,11 @@ int __cdecl main(int argc, char* argv[]) if (o.applyDiff) { LogInfo(g_AsmDiffsSummaryFormatString, loadedCount, jittedCount, failToReplayCount, excludedCount, - jittedCount - failToReplayCount - matchCount); + missingCount, jittedCount - failToReplayCount - matchCount); } else { - LogInfo(g_SummaryFormatString, loadedCount, jittedCount, failToReplayCount, excludedCount); + LogInfo(g_SummaryFormatString, loadedCount, jittedCount, failToReplayCount, excludedCount, missingCount); } st2.Stop(); @@ -603,11 +615,11 @@ int __cdecl main(int argc, char* argv[]) SpmiResult result = SpmiResult::Success; - if (errorCount > 0) + if ((errorCount > 0) || (errorCount2 > 0)) { result = SpmiResult::Error; } - else if (o.applyDiff && matchCount != jittedCount) + else if (o.applyDiff && (matchCount != jittedCount - missingCount)) { result = SpmiResult::Diffs; } diff --git a/src/coreclr/_build-commons.sh b/src/coreclr/_build-commons.sh index 8bac206f28ff4..9b1ee88d52a1a 100755 --- a/src/coreclr/_build-commons.sh +++ b/src/coreclr/_build-commons.sh @@ -23,7 +23,7 @@ handle_arguments() { ;; *) - handle_arguments_local "$1" + handle_arguments_local "$1" "$2" ;; esac } diff --git a/src/coreclr/binder/CMakeLists.txt b/src/coreclr/binder/CMakeLists.txt index 208f1214dd0da..f04c647f88f4b 100644 --- a/src/coreclr/binder/CMakeLists.txt +++ b/src/coreclr/binder/CMakeLists.txt @@ -13,11 +13,9 @@ set(BINDER_COMMON_SOURCES clrprivbindercoreclr.cpp coreclrbindercommon.cpp failurecache.cpp - fusionassemblyname.cpp stringlexer.cpp textualidentityparser.cpp utils.cpp - variables.cpp ) set(BINDER_COMMON_HEADERS @@ -42,15 +40,12 @@ set(BINDER_COMMON_HEADERS inc/coreclrbindercommon.h inc/failurecache.hpp inc/failurecachehashtraits.hpp - inc/fusionassemblyname.hpp - inc/fusionhelpers.hpp inc/loadcontext.hpp inc/loadcontext.inl inc/stringlexer.hpp inc/stringlexer.inl inc/textualidentityparser.hpp inc/utils.hpp - inc/variables.hpp ) set(BINDER_SOURCES diff --git a/src/coreclr/binder/applicationcontext.cpp b/src/coreclr/binder/applicationcontext.cpp index f865b4240490d..dd02857652b2f 100644 --- a/src/coreclr/binder/applicationcontext.cpp +++ b/src/coreclr/binder/applicationcontext.cpp @@ -17,7 +17,6 @@ #include "failurecache.hpp" #include "assemblyidentitycache.hpp" #include "utils.hpp" -#include "variables.hpp" #include "ex.h" #include "clr/fs/path.h" using namespace clr::fs; diff --git a/src/coreclr/binder/assembly.cpp b/src/coreclr/binder/assembly.cpp index d4948c4d1ae8b..09c195c0f8d09 100644 --- a/src/coreclr/binder/assembly.cpp +++ b/src/coreclr/binder/assembly.cpp @@ -182,9 +182,9 @@ namespace BINDER_SPACE return (pAsmName == nullptr ? nullptr : (LPCWSTR)pAsmName->GetSimpleName()); } - HRESULT Assembly::BindAssemblyByName(IAssemblyName * pIAssemblyName, ICLRPrivAssembly ** ppAssembly) + HRESULT Assembly::BindAssemblyByName(AssemblyNameData *pAssemblyNameData, ICLRPrivAssembly ** ppAssembly) { - return (m_pBinder == NULL) ? E_FAIL : m_pBinder->BindAssemblyByName(pIAssemblyName, ppAssembly); + return (m_pBinder == NULL) ? E_FAIL : m_pBinder->BindAssemblyByName(pAssemblyNameData, ppAssembly); } HRESULT Assembly::GetBinderID(UINT_PTR *pBinderId) diff --git a/src/coreclr/binder/assemblybinder.cpp b/src/coreclr/binder/assemblybinder.cpp index aa29465af824e..c4a2e85d8f5ac 100644 --- a/src/coreclr/binder/assemblybinder.cpp +++ b/src/coreclr/binder/assemblybinder.cpp @@ -20,26 +20,20 @@ #include "bindresult.inl" #include "failurecache.hpp" #include "utils.hpp" -#include "variables.hpp" #include "stringarraylist.h" #include "configuration.h" -#define APP_DOMAIN_LOCKED_UNLOCKED 0x02 -#define APP_DOMAIN_LOCKED_CONTEXT 0x04 - #ifndef IMAGE_FILE_MACHINE_ARM64 #define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian #endif -BOOL IsCompilationProcess(); - #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) #include "clrprivbindercoreclr.h" -#include "clrprivbinderassemblyloadcontext.h" // Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, - IAssemblyName *pIAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, - BINDER_SPACE::AssemblyName *pAssemblyName, ICLRPrivAssembly **ppLoadedAssembly); + BINDER_SPACE::AssemblyName *pAssemblyName, + CLRPrivBinderCoreCLR *pTPABinder, + ICLRPrivAssembly **ppLoadedAssembly); #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) @@ -112,12 +106,13 @@ namespace BINDER_SPACE return true; } + const WCHAR* s_httpURLPrefix = W("http://"); HRESULT URLToFullPath(PathString &assemblyPath) { HRESULT hr = S_OK; SString::Iterator pos = assemblyPath.Begin(); - if (assemblyPath.MatchCaseInsensitive(pos, g_BinderVariables->httpURLPrefix)) + if (assemblyPath.MatchCaseInsensitive(pos, s_httpURLPrefix)) { // HTTP downloads are unsupported hr = FUSION_E_CODE_DOWNLOAD_DISABLED; @@ -186,23 +181,6 @@ namespace BINDER_SPACE #endif // !CROSSGEN_COMPILE }; - /* static */ - HRESULT AssemblyBinder::Startup() - { - STATIC_CONTRACT_NOTHROW; - - HRESULT hr = S_OK; - - // This should only be called once - _ASSERTE(g_BinderVariables == NULL); - g_BinderVariables = new Variables(); - IF_FAIL_GO(g_BinderVariables->Init()); - - Exit: - return hr; - } - - HRESULT AssemblyBinder::TranslatePEToArchitectureType(DWORD *pdwPAFlags, PEKIND *PeKind) { HRESULT hr = S_OK; @@ -296,6 +274,7 @@ namespace BINDER_SPACE if (szCodeBase == NULL) { + _ASSERTE(pAssemblyName != NULL); IF_FAIL_GO(BindByName(pApplicationContext, pAssemblyName, false, // skipFailureCaching @@ -372,9 +351,6 @@ namespace BINDER_SPACE Assembly **ppSystemAssembly, bool fBindToNativeImage) { - // Indirect check that binder was initialized. - _ASSERTE(g_BinderVariables != NULL); - HRESULT hr = S_OK; _ASSERTE(ppSystemAssembly != NULL); @@ -468,9 +444,6 @@ namespace BINDER_SPACE SString& cultureName, Assembly** ppSystemAssembly) { - // Indirect check that binder was initialized. - _ASSERTE(g_BinderVariables != NULL); - HRESULT hr = S_OK; _ASSERTE(ppSystemAssembly != NULL); @@ -781,8 +754,7 @@ namespace BINDER_SPACE if (!tpaListAssembly) { - SString &culture = pRequestedAssemblyName->GetCulture(); - if (culture.IsEmpty() || culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + if (pRequestedAssemblyName->IsNeutralCulture()) { dwIncludeFlags |= AssemblyName::EXCLUDE_CULTURE; } @@ -912,10 +884,10 @@ namespace BINDER_SPACE // names as platform ones. HRESULT hr = S_OK; - SString& simpleNameRef = pRequestedAssemblyName->GetSimpleName(); + const SString& simpleNameRef = pRequestedAssemblyName->GetSimpleName(); SString& cultureRef = pRequestedAssemblyName->GetCulture(); - _ASSERTE(!cultureRef.IsEmpty() && !cultureRef.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)); + _ASSERTE(!pRequestedAssemblyName->IsNeutralCulture()); ReleaseHolder pAssembly; SString fileName; @@ -956,7 +928,7 @@ namespace BINDER_SPACE bool useNativeImages, Assembly **ppAssembly) { - SString &simpleName = pRequestedAssemblyName->GetSimpleName(); + const SString &simpleName = pRequestedAssemblyName->GetSimpleName(); BinderTracing::PathSource pathSource = useNativeImages ? BinderTracing::PathSource::AppNativeImagePaths : BinderTracing::PathSource::AppPaths; // Loop through the binding paths looking for a matching assembly for (DWORD i = 0; i < pBindingPaths->GetCount(); i++) @@ -1045,17 +1017,16 @@ namespace BINDER_SPACE { HRESULT hr = S_OK; - SString &culture = pRequestedAssemblyName->GetCulture(); bool fPartialMatchOnTpa = false; - if (!culture.IsEmpty() && !culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + if (!pRequestedAssemblyName->IsNeutralCulture()) { IF_FAIL_GO(BindSatelliteResource(pApplicationContext, pRequestedAssemblyName, pBindResult)); } else { ReleaseHolder pTPAAssembly; - SString& simpleName = pRequestedAssemblyName->GetSimpleName(); + const SString& simpleName = pRequestedAssemblyName->GetSimpleName(); // Is assembly in the bundle? // Single-file bundle contents take precedence over TPA. @@ -1436,7 +1407,6 @@ namespace BINDER_SPACE #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) HRESULT AssemblyBinder::BindUsingHostAssemblyResolver(/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, /* in */ AssemblyName *pAssemblyName, - /* in */ IAssemblyName *pIAssemblyName, /* in */ CLRPrivBinderCoreCLR *pTPABinder, /* out */ Assembly **ppAssembly) { @@ -1446,8 +1416,8 @@ HRESULT AssemblyBinder::BindUsingHostAssemblyResolver(/* in */ INT_PTR pManagedA // RuntimeInvokeHostAssemblyResolver will perform steps 2-4 of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName. ICLRPrivAssembly *pLoadedAssembly = NULL; - hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, pIAssemblyName, - pTPABinder, pAssemblyName, &pLoadedAssembly); + hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, + pAssemblyName, pTPABinder, &pLoadedAssembly); if (SUCCEEDED(hr)) { _ASSERTE(pLoadedAssembly != NULL); @@ -1467,9 +1437,6 @@ HRESULT AssemblyBinder::BindUsingPEImage(/* in */ ApplicationContext *pApplicat { HRESULT hr = E_FAIL; - // Indirect check that binder was initialized. - _ASSERTE(g_BinderVariables != NULL); - LONG kContextVersion = 0; BindResult bindResult; diff --git a/src/coreclr/binder/assemblyname.cpp b/src/coreclr/binder/assemblyname.cpp index 571898d75f0d0..d7da9c73a7124 100644 --- a/src/coreclr/binder/assemblyname.cpp +++ b/src/coreclr/binder/assemblyname.cpp @@ -12,11 +12,7 @@ // ============================================================ #include "assemblyname.hpp" -#include "assembly.hpp" #include "utils.hpp" -#include "variables.hpp" - -#include "fusionassemblyname.hpp" #include "textualidentityparser.hpp" @@ -24,6 +20,12 @@ #include "ex.h" +namespace +{ + // See https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names#specifying-assembly-names + const WCHAR* s_neutralCulture = W("neutral"); +} + namespace BINDER_SPACE { AssemblyName::AssemblyName() @@ -35,11 +37,6 @@ namespace BINDER_SPACE AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); } - AssemblyName::~AssemblyName() - { - // Nothing to do here - } - HRESULT AssemblyName::Init(IMDInternalImport *pIMetaDataAssemblyImport, PEKIND PeKind, mdAssemblyRef mdar /* = 0 */, @@ -166,138 +163,34 @@ namespace BINDER_SPACE return hr; } - HRESULT AssemblyName::Init(SString &assemblyDisplayName) + HRESULT AssemblyName::Init(const AssemblyNameData &data) { - return TextualIdentityParser::Parse(assemblyDisplayName, this); - } - - HRESULT AssemblyName::Init(IAssemblyName *pIAssemblyName) - { - HRESULT hr = S_OK; - - _ASSERTE(pIAssemblyName != NULL); - - EX_TRY + DWORD flags = data.IdentityFlags; + m_simpleName.SetUTF8(data.Name); + m_version.SetFeatureVersion(data.MajorVersion, data.MinorVersion); + m_version.SetServiceVersion(data.BuildNumber, data.RevisionNumber); + m_cultureOrLanguage.SetUTF8(data.Culture); + + m_publicKeyOrTokenBLOB.Set(data.PublicKeyOrToken, data.PublicKeyOrTokenLength); + if ((flags & BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY) != 0) { - { - // Set the simpleName - StackSString simpleName; - hr = fusion::util::GetSimpleName(pIAssemblyName, simpleName); - IF_FAIL_GO(hr); - SetSimpleName(simpleName); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME); - } - - // Display version - DWORD dwVersionParts[4] = {0,0,0,0}; - DWORD cbVersionSize = sizeof(dwVersionParts[0]); - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_MAJOR_VERSION, static_cast(&dwVersionParts[0]), &cbVersionSize); - IF_FAIL_GO(hr); - if ((hr == S_OK) && (cbVersionSize != 0)) - { - // Property is present - loop to get the individual version details - for(DWORD i = 0; i < 4; i++) - { - cbVersionSize = sizeof(dwVersionParts[i]); - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_MAJOR_VERSION+i, static_cast(&dwVersionParts[i]), &cbVersionSize); - IF_FAIL_GO(hr); - } - - m_version.SetFeatureVersion(dwVersionParts[0], dwVersionParts[1]); - m_version.SetServiceVersion(dwVersionParts[2], dwVersionParts[3]); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION); - } - - { - // Display culture - StackSString culture; - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CULTURE, culture); - IF_FAIL_GO(hr); - if (hr == S_OK) - { - SetCulture(culture); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE); - } - } - - { - // Display public key token - NewArrayHolder pPublicKeyToken; - DWORD cbPublicKeyToken = 0; - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_PUBLIC_KEY_TOKEN, static_cast(&pPublicKeyToken), &cbPublicKeyToken); - IF_FAIL_GO(hr); - if ((hr == S_OK) && (cbPublicKeyToken != 0)) - { - m_publicKeyOrTokenBLOB.Set(pPublicKeyToken, cbPublicKeyToken); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); - } - else - { - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); - } - } + // Convert public key to token + SBuffer publicKeyToken; + HRESULT hr = GetTokenFromPublicKey(m_publicKeyOrTokenBLOB, publicKeyToken); + if (FAILED(hr)) + return hr; + + m_publicKeyOrTokenBLOB.Set(publicKeyToken); + flags &= ~BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY; + flags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN; + } - // Display processor architecture - DWORD peKind = 0; - DWORD cbPeKind = sizeof(peKind); - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_ARCHITECTURE, static_cast(&peKind), &cbPeKind); - IF_FAIL_GO(hr); - if ((hr == S_OK) && (cbPeKind != 0)) - { - PEKIND PeKind = (PEKIND)peKind; - if (PeKind != peNone) - { - SetArchitecture(PeKind); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); - } - } + m_kProcessorArchitecture = data.ProcessorArchitecture; + m_kContentType = data.ContentType; - // Display retarget flag - BOOL fRetarget = FALSE; - DWORD cbRetarget = sizeof(fRetarget); - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_RETARGET, static_cast(&fRetarget), &cbRetarget); - IF_FAIL_GO(hr); - if ((hr == S_OK) && (cbRetarget != 0)) - { - if (fRetarget) - { - SetIsRetargetable(fRetarget); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE); - } - } + SetHave(flags); - // Display content type - DWORD dwContentType = AssemblyContentType_Default; - DWORD cbContentType = sizeof(dwContentType); - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CONTENT_TYPE, static_cast(&dwContentType), &cbContentType); - IF_FAIL_GO(hr); - if ((hr == S_OK) && (cbContentType != 0)) - { - if (dwContentType != AssemblyContentType_Default) - { - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE); - SetContentType((AssemblyContentType)dwContentType); - } - } - - { - // Display custom flag. Dont set it if it is not present since that will end up adding the "Custom=null" attribute - // in the displayname of the assembly that maybe generated using this AssemblyName instance. This could create conflict when - // the displayname is generated from the assembly directly as that will not have a "Custom" field set. - NewArrayHolder pCustomBLOB; - DWORD cbCustomBLOB = 0; - hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CUSTOM, static_cast(&pCustomBLOB), &cbCustomBLOB); - IF_FAIL_GO(hr); - if ((hr == S_OK) && (cbCustomBLOB != 0)) - { - m_customBLOB.Set(pCustomBLOB, cbCustomBLOB); - SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM); - } - } - } - EX_CATCH_HRESULT(hr); -Exit: - return hr; + return S_OK; } ULONG AssemblyName::AddRef() @@ -318,7 +211,13 @@ namespace BINDER_SPACE BOOL AssemblyName::IsCoreLib() { // TODO: Is this simple comparison enough? - return EqualsCaseInsensitive(GetSimpleName(), g_BinderVariables->corelib); + return SString::_wcsicmp(GetSimpleName().GetUnicode(), CoreLibName_W) == 0; + } + + bool AssemblyName::IsNeutralCulture() + { + return m_cultureOrLanguage.IsEmpty() + || SString::_wcsicmp(m_cultureOrLanguage.GetUnicode(), s_neutralCulture) == 0; } ULONG AssemblyName::Hash(DWORD dwIncludeFlags) @@ -353,7 +252,7 @@ namespace BINDER_SPACE dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CULTURE; } - dwHash ^= static_cast(HashCaseInsensitive(GetSimpleName())); + dwHash ^= static_cast(GetSimpleName().HashCaseInsensitive()); dwHash = _rotl(dwHash, 4); if (AssemblyIdentity::Have(dwUseIdentityFlags, @@ -386,7 +285,7 @@ namespace BINDER_SPACE if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE)) { - dwHash ^= static_cast(HashCaseInsensitive(GetNormalizedCulture())); + dwHash ^= static_cast(GetNormalizedCulture().HashCaseInsensitive()); dwHash = _rotl(dwHash, 4); } @@ -424,14 +323,14 @@ namespace BINDER_SPACE return (GetContentType() == pAssemblyName->GetContentType()); } - if (EqualsCaseInsensitive(GetSimpleName(), pAssemblyName->GetSimpleName()) && + if (GetSimpleName().EqualsCaseInsensitive(pAssemblyName->GetSimpleName()) && (GetContentType() == pAssemblyName->GetContentType())) { fEquals = TRUE; if ((dwIncludeFlags & EXCLUDE_CULTURE) == 0) { - fEquals = EqualsCaseInsensitive(GetNormalizedCulture(), pAssemblyName->GetNormalizedCulture()); + fEquals = GetNormalizedCulture().EqualsCaseInsensitive(pAssemblyName->GetNormalizedCulture()); } if (fEquals && (dwIncludeFlags & INCLUDE_PUBLIC_KEY_TOKEN) != 0) @@ -490,7 +389,7 @@ namespace BINDER_SPACE if (culture.IsEmpty()) { - culture = g_BinderVariables->cultureNeutral; + culture.SetLiteral(s_neutralCulture); } return culture; diff --git a/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp b/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp index 69d8d83372893..92d83d75b111a 100644 --- a/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp +++ b/src/coreclr/binder/clrprivbinderassemblyloadcontext.cpp @@ -42,11 +42,11 @@ HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByNameWorker(BINDER_SPACE: return hr; } -HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName *pIAssemblyName, +HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(AssemblyNameData *pAssemblyNameData, ICLRPrivAssembly **ppAssembly) { HRESULT hr = S_OK; - VALIDATE_ARG_RET(pIAssemblyName != nullptr && ppAssembly != nullptr); + VALIDATE_ARG_RET(pAssemblyNameData != nullptr && ppAssembly != nullptr); _ASSERTE(m_pTPABinder != NULL); @@ -54,7 +54,7 @@ HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName * ReleaseHolder pAssemblyName; SAFE_NEW(pAssemblyName, AssemblyName); - IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); + IF_FAIL_GO(pAssemblyName->Init(*pAssemblyNameData)); // When LoadContext needs to resolve an assembly reference, it will go through the following lookup order: // @@ -84,7 +84,7 @@ HRESULT CLRPrivBinderAssemblyLoadContext::BindAssemblyByName(IAssemblyName * // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly // that has been loaded. // - hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, pIAssemblyName, m_pTPABinder, &pCoreCLRFoundAssembly); + hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, m_pTPABinder, &pCoreCLRFoundAssembly); if (SUCCEEDED(hr)) { // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance. diff --git a/src/coreclr/binder/clrprivbindercoreclr.cpp b/src/coreclr/binder/clrprivbindercoreclr.cpp index 571e127d7dab2..fec66a7545880 100644 --- a/src/coreclr/binder/clrprivbindercoreclr.cpp +++ b/src/coreclr/binder/clrprivbindercoreclr.cpp @@ -4,7 +4,6 @@ #include "common.h" #include "assemblybinder.hpp" #include "clrprivbindercoreclr.h" -#include "variables.hpp" using namespace BINDER_SPACE; @@ -43,19 +42,33 @@ HRESULT CLRPrivBinderCoreCLR::BindAssemblyByNameWorker(BINDER_SPACE::AssemblyNam // ============================================================================ // CLRPrivBinderCoreCLR implementation // ============================================================================ -HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(IAssemblyName *pIAssemblyName, +HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(AssemblyNameData *pAssemblyNameData, ICLRPrivAssembly **ppAssembly) { HRESULT hr = S_OK; - VALIDATE_ARG_RET(pIAssemblyName != nullptr && ppAssembly != nullptr); + VALIDATE_ARG_RET(pAssemblyNameData != nullptr && ppAssembly != nullptr); *ppAssembly = nullptr; - ReleaseHolder pCoreCLRFoundAssembly; ReleaseHolder pAssemblyName; - SAFE_NEW(pAssemblyName, AssemblyName); - IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName)); + IF_FAIL_GO(pAssemblyName->Init(*pAssemblyNameData)); + + hr = BindUsingAssemblyName(pAssemblyName, ppAssembly); + +Exit: + return hr; +} + +HRESULT CLRPrivBinderCoreCLR::BindUsingAssemblyName(BINDER_SPACE::AssemblyName *pAssemblyName, + ICLRPrivAssembly **ppAssembly) +{ + HRESULT hr = S_OK; + VALIDATE_ARG_RET(pAssemblyName != nullptr && ppAssembly != nullptr); + + *ppAssembly = nullptr; + + ReleaseHolder pCoreCLRFoundAssembly; hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, false /* excludeAppPaths */); @@ -78,8 +91,7 @@ HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(IAssemblyName *pIAssemblyNa // should be run even if the managed default ALC has not yet been used. (For non-satellite assemblies, any // additional logic comes through a user-defined event handler which would have initialized the managed ALC, // so if the managed ALC is not set yet, there is no additional logic to run) - SString &culture = pAssemblyName->GetCulture(); - if (!culture.IsEmpty() && !culture.EqualsCaseInsensitive(g_BinderVariables->cultureNeutral)) + if (!pAssemblyName->IsNeutralCulture()) { // Make sure the managed default ALC is initialized. GCX_COOP(); @@ -94,7 +106,7 @@ HRESULT CLRPrivBinderCoreCLR::BindAssemblyByName(IAssemblyName *pIAssemblyNa if (pManagedAssemblyLoadContext != NULL) { - hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, pIAssemblyName, + hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, NULL, &pCoreCLRFoundAssembly); if (SUCCEEDED(hr)) { @@ -160,7 +172,7 @@ HRESULT CLRPrivBinderCoreCLR::BindUsingPEImage( /* in */ PEImage *pPEImage, { // Ensure we are not being asked to bind to a TPA assembly // - SString& simpleName = pAssemblyName->GetSimpleName(); + const SString& simpleName = pAssemblyName->GetSimpleName(); SimpleNameToFileNameMap* tpaMap = GetAppContext()->GetTpaList(); if (tpaMap->LookupPtr(simpleName.GetUnicode()) != NULL) { @@ -211,32 +223,20 @@ HRESULT CLRPrivBinderCoreCLR::SetupBindingPaths(SString &sTrustedPlatformAssemb // See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind // and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath // for an example of how they're used. -HRESULT CLRPrivBinderCoreCLR::Bind(SString &assemblyDisplayName, - LPCWSTR wszCodeBase, +HRESULT CLRPrivBinderCoreCLR::Bind(LPCWSTR wszCodeBase, PEAssembly *pParentAssembly, BOOL fNgenExplicitBind, BOOL fExplicitBindToNativeImage, ICLRPrivAssembly **ppAssembly) { HRESULT hr = S_OK; - VALIDATE_ARG_RET(ppAssembly != NULL); - - AssemblyName assemblyName; - - ReleaseHolder pAssemblyName; - - if (!assemblyDisplayName.IsEmpty()) - { - // AssemblyDisplayName can be empty if wszCodeBase is specified. - SAFE_NEW(pAssemblyName, AssemblyName); - IF_FAIL_GO(pAssemblyName->Init(assemblyDisplayName)); - } + VALIDATE_ARG_RET(wszCodeBase != NULL && ppAssembly != NULL); EX_TRY { ReleaseHolder pAsm; hr = AssemblyBinder::BindAssembly(&m_appContext, - pAssemblyName, + NULL, wszCodeBase, pParentAssembly, fNgenExplicitBind, @@ -252,7 +252,6 @@ HRESULT CLRPrivBinderCoreCLR::Bind(SString &assemblyDisplayName, } EX_CATCH_HRESULT(hr); -Exit: return hr; } diff --git a/src/coreclr/binder/coreclrbindercommon.cpp b/src/coreclr/binder/coreclrbindercommon.cpp index 13ae1e52a577a..ebe3137c77f14 100644 --- a/src/coreclr/binder/coreclrbindercommon.cpp +++ b/src/coreclr/binder/coreclrbindercommon.cpp @@ -10,17 +10,6 @@ using namespace BINDER_SPACE; -//============================================================================= -// Init code -//----------------------------------------------------------------------------- -/* static */ -HRESULT CCoreCLRBinderHelper::Init() -{ - STATIC_CONTRACT_NOTHROW; - - return AssemblyBinder::Startup(); -} - HRESULT CCoreCLRBinderHelper::DefaultBinderSetupContext(DWORD dwAppDomainId,CLRPrivBinderCoreCLR **ppTPABinder) { HRESULT hr = S_OK; diff --git a/src/coreclr/binder/fusionassemblyname.cpp b/src/coreclr/binder/fusionassemblyname.cpp deleted file mode 100644 index 6da1cc3a3b2bb..0000000000000 --- a/src/coreclr/binder/fusionassemblyname.cpp +++ /dev/null @@ -1,714 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// FusionAssemblyName.cpp -// -// Implements the CAssemblyName class -// -// ============================================================ - -#include -#include -#include "strongnameinternal.h" - -#include "fusionhelpers.hpp" -#include "fusionassemblyname.hpp" - -#include -#include "shlwapi.h" - -#include "assemblyidentity.hpp" -#include "textualidentityparser.hpp" - -// --------------------------------------------------------------------------- -// CPropertyArray ctor -// --------------------------------------------------------------------------- -CPropertyArray::CPropertyArray() -{ - _dwSig = 0x504f5250; /* 'PORP' */ - memset(&_rProp, 0, ASM_NAME_MAX_PARAMS * sizeof(FusionProperty)); -} - -// --------------------------------------------------------------------------- -// CPropertyArray dtor -// --------------------------------------------------------------------------- -CPropertyArray::~CPropertyArray() -{ - for (DWORD i = 0; i < ASM_NAME_MAX_PARAMS; i++) - { - if (_rProp[i].cb > sizeof(DWORD)) - { - if (_rProp[i].pv != NULL) - { - FUSION_DELETE_ARRAY((LPBYTE) _rProp[i].pv); - _rProp[i].pv = NULL; - } - } - } -} - -// --------------------------------------------------------------------------- -// CPropertyArray::Set -// --------------------------------------------------------------------------- -HRESULT CPropertyArray::Set(DWORD PropertyId, - LPCVOID pvProperty, DWORD cbProperty) -{ - HRESULT hr = S_OK; - FusionProperty *pItem = NULL; - - pItem = &(_rProp[PropertyId]); - - if (!cbProperty && !pvProperty) - { - if (pItem->cb > sizeof(DWORD)) - { - if (pItem->pv != NULL) - FUSION_DELETE_ARRAY((LPBYTE) pItem->pv); - } - pItem->pv = NULL; - } - else if (cbProperty > sizeof(DWORD)) - { - LPBYTE ptr = NEW(BYTE[cbProperty]); - if (!ptr) - { - hr = E_OUTOFMEMORY; - goto exit; - } - - if (pItem->cb > sizeof(DWORD)) - FUSION_DELETE_ARRAY((LPBYTE) pItem->pv); - - memcpy(ptr, pvProperty, cbProperty); - pItem->pv = ptr; - } - else - { - if (pItem->cb > sizeof(DWORD)) - FUSION_DELETE_ARRAY((LPBYTE) pItem->pv); - - memcpy(&(pItem->pv), pvProperty, cbProperty); - -#ifdef _DEBUG - if (PropertyId == ASM_NAME_ARCHITECTURE) { - PEKIND pe = * ((PEKIND *)pvProperty); - _ASSERTE(pe != peInvalid); - } -#endif - } - pItem->cb = cbProperty; - -exit: - return hr; -} - -// --------------------------------------------------------------------------- -// CPropertyArray::Get -// --------------------------------------------------------------------------- -HRESULT CPropertyArray::Get(DWORD PropertyId, - LPVOID pvProperty, LPDWORD pcbProperty) -{ - HRESULT hr = S_OK; - FusionProperty *pItem; - - _ASSERTE(pcbProperty); - - if (PropertyId >= ASM_NAME_MAX_PARAMS - || (!pvProperty && *pcbProperty)) - { - _ASSERTE(!"Invalid Argument! Passed in NULL buffer with size non-zero!"); - hr = E_INVALIDARG; - goto exit; - } - - pItem = &(_rProp[PropertyId]); - - if (pItem->cb > *pcbProperty) - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); - else if (pItem->cb && pvProperty) - memcpy(pvProperty, (pItem->cb > sizeof(DWORD) ? - pItem->pv : (LPBYTE) &(pItem->pv)), pItem->cb); - - *pcbProperty = pItem->cb; - -exit: - return hr; -} - -// --------------------------------------------------------------------------- -// CPropertyArray::operator [] -// Wraps DWORD optimization test. -// --------------------------------------------------------------------------- -FusionProperty CPropertyArray::operator [] (DWORD PropertyId) -{ - FusionProperty prop; - - prop.pv = _rProp[PropertyId].cb > sizeof(DWORD) ? - _rProp[PropertyId].pv : &(_rProp[PropertyId].pv); - - prop.cb = _rProp[PropertyId].cb; - - return prop; -} - -// --------------------------------------------------------------------------- -// CAssemblyName::AddRef -// --------------------------------------------------------------------------- -STDMETHODIMP_(ULONG) -CAssemblyName::AddRef() -{ - return InterlockedIncrement(&_cRef); -} - -// --------------------------------------------------------------------------- -// CAssemblyName::Release -// --------------------------------------------------------------------------- -STDMETHODIMP_(ULONG) -CAssemblyName::Release() -{ - ULONG ulRef = InterlockedDecrement(&_cRef); - if (ulRef == 0) - { - delete this; - } - - return ulRef; -} - -// --------------------------------------------------------------------------- -// CAssemblyName::QueryInterface -// --------------------------------------------------------------------------- -STDMETHODIMP -CAssemblyName::QueryInterface(REFIID riid, void** ppv) -{ - HRESULT hr = S_OK; - - BEGIN_ENTRYPOINT_NOTHROW; - - if (!ppv) - { - hr = E_POINTER; - goto Exit; - } - - if ( IsEqualIID(riid, IID_IUnknown) - || IsEqualIID(riid, IID_IAssemblyName) - ) - { - *ppv = static_cast (this); - AddRef(); - hr = S_OK; - goto Exit; - } - else - { - *ppv = NULL; - hr = E_NOINTERFACE; - goto Exit; - } - - Exit: - END_ENTRYPOINT_NOTHROW; - - return hr; -} - -// --------------------------------------------------------------------------- -// CAssemblyName::SetProperty -// --------------------------------------------------------------------------- -STDMETHODIMP -CAssemblyName::SetProperty(DWORD PropertyId, - LPCVOID pvProperty, - DWORD cbProperty) -{ - HRESULT hr = S_OK; - - BEGIN_ENTRYPOINT_NOTHROW; - - hr = SetPropertyInternal(PropertyId, pvProperty, cbProperty); - - END_ENTRYPOINT_NOTHROW; - return hr; -} - -// --------------------------------------------------------------------------- -// CAssemblyName::GetProperty -// --------------------------------------------------------------------------- -STDMETHODIMP -CAssemblyName::GetProperty(DWORD PropertyId, - LPVOID pvProperty, LPDWORD pcbProperty) -{ - HRESULT hr = S_OK; - - BEGIN_ENTRYPOINT_NOTHROW; - - // Retrieve the property. - switch(PropertyId) - { - case ASM_NAME_NULL_PUBLIC_KEY_TOKEN: - case ASM_NAME_NULL_PUBLIC_KEY: - { - hr = (_fPublicKeyToken && !_rProp[PropertyId].cb) ? S_OK : S_FALSE; - break; - } - case ASM_NAME_NULL_CUSTOM: - { - hr = (_fCustom && !_rProp[PropertyId].cb) ? S_OK : S_FALSE; - break; - } - default: - { - hr = _rProp.Get(PropertyId, pvProperty, pcbProperty); - break; - } - } - - END_ENTRYPOINT_NOTHROW; - - return hr; -} - -// --------------------------------------------------------------------------- -// CAssemblyName::SetPropertyInternal -// --------------------------------------------------------------------------- -HRESULT CAssemblyName::SetPropertyInternal(DWORD PropertyId, - LPCVOID pvProperty, - DWORD cbProperty) -{ - HRESULT hr = S_OK; - LPBYTE pbSN = NULL; - DWORD cbSN = 0; - - if (PropertyId >= ASM_NAME_MAX_PARAMS - || (!pvProperty && cbProperty)) - { - _ASSERTE(!"Invalid Argument! Passed in NULL buffer with size non-zero!"); - hr = E_INVALIDARG; - goto exit; - } - - // - make this a switch statement. - if (PropertyId == ASM_NAME_MAJOR_VERSION || - PropertyId == ASM_NAME_MINOR_VERSION || - PropertyId == ASM_NAME_BUILD_NUMBER || - PropertyId == ASM_NAME_REVISION_NUMBER) - { - if (cbProperty > sizeof(WORD)) { - hr = E_INVALIDARG; - goto exit; - } - } - - // Check if public key is being set and if so, - // set the public key token if not already set. - if (PropertyId == ASM_NAME_PUBLIC_KEY) - { - // If setting true public key, generate hash. - if (pvProperty && cbProperty) - { - // Generate the public key token from the pk. - if (FAILED(hr = StrongNameTokenFromPublicKey((LPBYTE) pvProperty, cbProperty, &pbSN, &cbSN))) - goto exit; - - // Set the public key token property. - if (FAILED(hr = SetPropertyInternal(ASM_NAME_PUBLIC_KEY_TOKEN, pbSN, cbSN))) - goto exit; - } - // Otherwise expect call to reset property. - else if (!cbProperty) - { - if (FAILED(hr = SetPropertyInternal(ASM_NAME_PUBLIC_KEY_TOKEN, pvProperty, cbProperty))) - goto exit; - } - - } - // Setting NULL public key clears values in public key, - // public key token and sets public key token flag. - else if (PropertyId == ASM_NAME_NULL_PUBLIC_KEY) - { - pvProperty = NULL; - cbProperty = 0; - hr = SetPropertyInternal(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, pvProperty, cbProperty); - goto exit; - } - // Setting or clearing public key token. - else if (PropertyId == ASM_NAME_PUBLIC_KEY_TOKEN) - { - // Defensive: invalid sized public key tokens should be avoided. - if (cbProperty > PUBLIC_KEY_TOKEN_LEN) - { - hr = SetPropertyInternal(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, NULL, 0); - hr = E_INVALIDARG; - goto exit; - } - - if (pvProperty && cbProperty) - _fPublicKeyToken = TRUE; - else if (!cbProperty) - _fPublicKeyToken = FALSE; - } - // Setting NULL public key token clears public key token and - // sets public key token flag. - else if (PropertyId == ASM_NAME_NULL_PUBLIC_KEY_TOKEN) - { - _fPublicKeyToken = TRUE; - pvProperty = NULL; - cbProperty = 0; - PropertyId = ASM_NAME_PUBLIC_KEY_TOKEN; - } - else if (PropertyId == ASM_NAME_CUSTOM) - { - if (pvProperty && cbProperty) - _fCustom = TRUE; - else if (!cbProperty) - _fCustom = FALSE; - } - else if (PropertyId == ASM_NAME_NULL_CUSTOM) - { - _fCustom = TRUE; - pvProperty = NULL; - cbProperty = 0; - PropertyId = ASM_NAME_CUSTOM; - } - - // Setting "neutral" as the culture is the same as "" culture (meaning - // culture-invariant). - else if (PropertyId == ASM_NAME_CULTURE) { - if (pvProperty && !FusionCompareStringI((LPWSTR)pvProperty, W("neutral"))) { - pvProperty = (void *)W(""); - cbProperty = sizeof(W("")); - } - } - - // Set property on array. - hr = _rProp.Set(PropertyId, pvProperty, cbProperty); - -exit: - // Free memory allocated by crypto wrapper. - if (pbSN) { - StrongNameFreeBuffer(pbSN); - } - - return hr; -} - -// --------------------------------------------------------------------------- -// CreateAssemblyNameObject -// --------------------------------------------------------------------------- - -// This is not external for CoreCLR -STDAPI -CreateAssemblyNameObject( - LPASSEMBLYNAME *ppAssemblyName, - LPCOLESTR szAssemblyName) -{ - - HRESULT hr = S_OK; - - BEGIN_ENTRYPOINT_NOTHROW; - - CAssemblyName *pName = NULL; - - if (!ppAssemblyName) - { - hr = E_INVALIDARG; - goto exit; - } - - pName = NEW(CAssemblyName); - if (!pName) - { - hr = E_OUTOFMEMORY; - goto exit; - } - - hr = pName->Parse((LPWSTR)szAssemblyName); - if (FAILED(hr)) - { - SAFERELEASE(pName); - goto exit; - } - - *ppAssemblyName = pName; - -exit: - END_ENTRYPOINT_NOTHROW; - return hr; -} - -// --------------------------------------------------------------------------- -// CAssemblyName constructor -// --------------------------------------------------------------------------- -CAssemblyName::CAssemblyName() -{ - _dwSig = 0x454d414e; /* 'EMAN' */ - _fPublicKeyToken = FALSE; - _fCustom = TRUE; - _cRef = 1; -} - -// --------------------------------------------------------------------------- -// CAssemblyName::Parse -// --------------------------------------------------------------------------- -HRESULT CAssemblyName::Parse(__in_z LPCWSTR szDisplayName) -{ - HRESULT hr = S_OK; - - if (!(szDisplayName && *szDisplayName)) - { - hr = E_INVALIDARG; - goto exit; - } - - EX_TRY { - BINDER_SPACE::AssemblyIdentity assemblyIdentity; - SString displayName(szDisplayName); - - // Parse the textual identity - hr = BINDER_SPACE::TextualIdentityParser::Parse(displayName, &assemblyIdentity); - if (FAILED(hr)) { - goto exit; - } - - // Set name. - hr = SetProperty(ASM_NAME_NAME, - (LPVOID) assemblyIdentity.m_simpleName.GetUnicode(), - (assemblyIdentity.m_simpleName.GetCount() + 1) * sizeof(WCHAR)); - if (FAILED(hr)) { - goto exit; - } - - // Set version. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION)) { - WORD wVersionPart = 0; - - wVersionPart = (WORD) assemblyIdentity.m_version.GetMajor(); - hr = SetProperty(ASM_NAME_MAJOR_VERSION, &wVersionPart, sizeof(WORD)); - if (FAILED(hr)) { - goto exit; - } - - wVersionPart = (WORD) assemblyIdentity.m_version.GetMinor(); - hr = SetProperty(ASM_NAME_MINOR_VERSION, &wVersionPart, sizeof(WORD)); - if (FAILED(hr)) { - goto exit; - } - - wVersionPart = (WORD) assemblyIdentity.m_version.GetBuild(); - hr = SetProperty(ASM_NAME_BUILD_NUMBER, &wVersionPart, sizeof(WORD)); - if (FAILED(hr)) { - goto exit; - } - - wVersionPart = (WORD) assemblyIdentity.m_version.GetRevision(); - hr = SetProperty(ASM_NAME_REVISION_NUMBER, &wVersionPart, sizeof(WORD)); - if (FAILED(hr)) { - goto exit; - } - } - - // Set culture. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE)) { - hr = SetProperty(ASM_NAME_CULTURE, - (LPVOID) assemblyIdentity.m_cultureOrLanguage.GetUnicode(), - (assemblyIdentity.m_cultureOrLanguage.GetCount()+1) * sizeof(WCHAR)); - if (FAILED(hr)) { - goto exit; - } - } - - // Set public key (token) or NULL flag. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) { - SBuffer &publicKeyBuffer = assemblyIdentity.m_publicKeyOrTokenBLOB; - const void *pBytes = publicKeyBuffer; - - // This also computes and sets the public key token. - hr = SetProperty(ASM_NAME_PUBLIC_KEY, (void *) pBytes, publicKeyBuffer.GetSize()); - if (FAILED(hr)) { - goto exit; - } - } - else if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) { - SBuffer &publicKeyTokenBuffer = assemblyIdentity.m_publicKeyOrTokenBLOB; - const void *pBytes = publicKeyTokenBuffer; - - hr = SetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, - (LPVOID) pBytes, - publicKeyTokenBuffer.GetSize()); - if (FAILED(hr)) { - goto exit; - } - } - else if (assemblyIdentity. - Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL)) { - hr = SetProperty(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, NULL, 0); - if (FAILED(hr)) { - goto exit; - } - } - - // Set architecture. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) { - PEKIND peKind = assemblyIdentity.m_kProcessorArchitecture; - - hr = SetProperty(ASM_NAME_ARCHITECTURE, (LPVOID) &peKind, sizeof(PEKIND)); - if(FAILED(hr)) { - goto exit; - } - } - - // Set retargetable flag. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) { - BOOL fRetarget = TRUE; - - if (FAILED(hr = SetProperty(ASM_NAME_RETARGET, &fRetarget, sizeof(BOOL)))) { - goto exit; - } - } - - // Set content type. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) { - DWORD dwContentType = assemblyIdentity.m_kContentType; - - hr = SetProperty(ASM_NAME_CONTENT_TYPE, &dwContentType, sizeof(dwContentType)); - IfFailGoto(hr, exit); - } - - // Set custom or NULL flag. - if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM)) { - SBuffer &customBuffer = assemblyIdentity.m_customBLOB; - const void *pBytes = customBuffer; - - hr = SetProperty(ASM_NAME_CUSTOM, (void *) pBytes, customBuffer.GetSize()); - if (FAILED(hr)) { - goto exit; - } - } - else if (assemblyIdentity.Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL)) { - hr = SetProperty(ASM_NAME_NULL_CUSTOM, NULL, 0); - if (FAILED(hr)) { - goto exit; - } - } - } - EX_CATCH_HRESULT(hr); - - exit: - return hr; -} - -namespace fusion -{ - namespace util - { - namespace priv - { - inline bool IsNullProperty(DWORD dwProperty) - { - LIMITED_METHOD_CONTRACT; - return dwProperty == ASM_NAME_NULL_PUBLIC_KEY_TOKEN || - dwProperty == ASM_NAME_NULL_PUBLIC_KEY || - dwProperty == ASM_NAME_NULL_CUSTOM; - } - } - - // Non-allocating helper. - HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PVOID pBuf, DWORD *pcbBuf) - { - LIMITED_METHOD_CONTRACT; - HRESULT hr = S_OK; - - _ASSERTE(pName != nullptr && pcbBuf != nullptr); - if (pName == nullptr || pcbBuf == nullptr) - { - return E_INVALIDARG; - } - - hr = pName->GetProperty(dwProperty, pBuf, pcbBuf); - IfFailRet(hr); - - // Zero-length non-null property means there is no value. - if (hr == S_OK && *pcbBuf == 0 && !priv::IsNullProperty(dwProperty)) - { - hr = S_FALSE; - } - - return hr; - } - - // Allocating helper. - HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PBYTE * ppBuf, DWORD *pcbBuf) - { - LIMITED_METHOD_CONTRACT; - HRESULT hr = S_OK; - - _ASSERTE(ppBuf != nullptr && (*ppBuf == nullptr || pcbBuf != nullptr)); - if (ppBuf == nullptr || (*ppBuf != nullptr && pcbBuf == nullptr)) - { - return E_INVALIDARG; - } - - DWORD cbBuf = 0; - if (pcbBuf == nullptr) - pcbBuf = &cbBuf; - - hr = GetProperty(pName, dwProperty, *ppBuf, pcbBuf); - - // No provided buffer constitutes a request for one to be allocated. - if (*ppBuf == nullptr) - { - // If it's a null property, allocate a single-byte array to provide consistency. - if (hr == S_OK && priv::IsNullProperty(dwProperty)) - { - *ppBuf = new (nothrow) BYTE[1]; - IfNullRet(*ppBuf); - } - // Great, get the value. - else if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) - { - NewArrayHolder pBuf = new (nothrow) BYTE[*pcbBuf]; - IfNullRet(pBuf); - hr = pName->GetProperty(dwProperty, pBuf, pcbBuf); - IfFailRet(hr); - *ppBuf = pBuf.Extract(); - hr = S_OK; - } - } - - return hr; - } - - HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, SString & ssVal) - { - LIMITED_METHOD_CONTRACT; - HRESULT hr = S_OK; - - _ASSERTE(pName != nullptr); - if (pName == nullptr) - { - return E_INVALIDARG; - } - - DWORD cbSize = 0; - hr = GetProperty(pName, dwProperty, static_cast(nullptr), &cbSize); - - if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) - { - EX_TRY - { - PWSTR wzNameBuf = ssVal.OpenUnicodeBuffer(cbSize / sizeof(WCHAR) - 1); - hr = GetProperty(pName, dwProperty, reinterpret_cast(wzNameBuf), &cbSize); - ssVal.CloseBuffer(); - IfFailThrow(hr); - ssVal.Normalize(); - } - EX_CATCH_HRESULT(hr); - IfFailRet(hr); - } - - return hr; - } - } -} - diff --git a/src/coreclr/binder/inc/assembly.hpp b/src/coreclr/binder/inc/assembly.hpp index 3f5fb87594e22..5f5efb6a25c4f 100644 --- a/src/coreclr/binder/inc/assembly.hpp +++ b/src/coreclr/binder/inc/assembly.hpp @@ -72,8 +72,8 @@ namespace BINDER_SPACE LPCWSTR GetSimpleName(); STDMETHOD(BindAssemblyByName)( - IAssemblyName * pIAssemblyName, - ICLRPrivAssembly ** ppAssembly); + /* [in] */ AssemblyNameData *pAssemblyNameData, + /* [retval][out] */ ICLRPrivAssembly **ppAssembly); STDMETHOD(GetAvailableImageTypes)(PDWORD pdwImageTypes); diff --git a/src/coreclr/binder/inc/assemblybinder.hpp b/src/coreclr/binder/inc/assemblybinder.hpp index 6fc2f2dc4574c..2ba11b7ac0183 100644 --- a/src/coreclr/binder/inc/assemblybinder.hpp +++ b/src/coreclr/binder/inc/assemblybinder.hpp @@ -16,19 +16,17 @@ #include "bindertypes.hpp" #include "bindresult.hpp" -#include "coreclrbindercommon.h" #include "bundle.h" -class CLRPrivBinderAssemblyLoadContext; class CLRPrivBinderCoreCLR; +class PEAssembly; +class PEImage; namespace BINDER_SPACE { class AssemblyBinder { public: - static HRESULT Startup(); - // See code:BINDER_SPACE::AssemblyBinder::GetAssembly for info on fNgenExplicitBind // and fExplicitBindToNativeImage, and see code:CEECompileInfo::LoadAssemblyByPath // for an example of how they're used. @@ -60,7 +58,6 @@ namespace BINDER_SPACE #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin, /* in */ AssemblyName *pAssemblyName, - /* in */ IAssemblyName *pIAssemblyName, /* in */ CLRPrivBinderCoreCLR *pTPABinder, /* out */ Assembly **ppAssembly); diff --git a/src/coreclr/binder/inc/assemblyidentity.hpp b/src/coreclr/binder/inc/assemblyidentity.hpp index e1fdec87b2617..cc0916c8780af 100644 --- a/src/coreclr/binder/inc/assemblyidentity.hpp +++ b/src/coreclr/binder/inc/assemblyidentity.hpp @@ -87,7 +87,7 @@ namespace BINDER_SPACE DWORD m_dwIdentityFlags; }; - class AssemblyIdentityUTF8 : public AssemblyIdentity + class AssemblyIdentityUTF8 final : public AssemblyIdentity { public: AssemblyIdentityUTF8() diff --git a/src/coreclr/binder/inc/assemblyname.hpp b/src/coreclr/binder/inc/assemblyname.hpp index fb66d9830e0a3..6bec598a1646d 100644 --- a/src/coreclr/binder/inc/assemblyname.hpp +++ b/src/coreclr/binder/inc/assemblyname.hpp @@ -19,35 +19,38 @@ namespace BINDER_SPACE { - class AssemblyName : protected AssemblyIdentity + class AssemblyName final : public AssemblyIdentity { public: typedef enum { - INCLUDE_DEFAULT = 0x00, - INCLUDE_VERSION = 0x01, - INCLUDE_ARCHITECTURE = 0x02, - INCLUDE_RETARGETABLE = 0x04, - INCLUDE_CONTENT_TYPE = 0x08, - INCLUDE_PUBLIC_KEY_TOKEN = 0x10, - EXCLUDE_CULTURE = 0x20 + INCLUDE_DEFAULT = 0x00, + INCLUDE_VERSION = 0x01, + INCLUDE_ARCHITECTURE = 0x02, + INCLUDE_RETARGETABLE = 0x04, + INCLUDE_CONTENT_TYPE = 0x08, + INCLUDE_PUBLIC_KEY_TOKEN = 0x10, + EXCLUDE_CULTURE = 0x20, + INCLUDE_ALL = INCLUDE_VERSION + | INCLUDE_ARCHITECTURE + | INCLUDE_RETARGETABLE + | INCLUDE_CONTENT_TYPE + | INCLUDE_PUBLIC_KEY_TOKEN, } INCLUDE_FLAGS; AssemblyName(); - ~AssemblyName(); HRESULT Init(/* in */ IMDInternalImport *pIMetaDataAssemblyImport, /* in */ PEKIND PeKind, /* in */ mdAssemblyRef mda = 0, /* in */ BOOL fIsDefinition = TRUE); - HRESULT Init(/* in */ SString &assemblyDisplayName); - HRESULT Init(/* in */ IAssemblyName *pIAssemblyName); + HRESULT Init(/* in */ const AssemblyNameData &data); ULONG AddRef(); ULONG Release(); // Getters/Setters - inline SString &GetSimpleName(); + inline const SString &GetSimpleName(); inline void SetSimpleName(SString &simpleName); inline AssemblyVersion *GetVersion(); inline void SetVersion(/* in */ AssemblyVersion *pAssemblyVersion); @@ -63,9 +66,8 @@ namespace BINDER_SPACE inline BOOL GetIsDefinition(); inline void SetIsDefinition(BOOL fIsDefinition); - inline void SetHave(DWORD dwIdentityFlags); - BOOL IsCoreLib(); + bool IsNeutralCulture(); ULONG Hash(/* in */ DWORD dwIncludeFlags); BOOL Equals(/* in */ AssemblyName *pAssemblyName, diff --git a/src/coreclr/binder/inc/assemblyname.inl b/src/coreclr/binder/inc/assemblyname.inl index 11d5dbbe86905..201136f19768e 100644 --- a/src/coreclr/binder/inc/assemblyname.inl +++ b/src/coreclr/binder/inc/assemblyname.inl @@ -14,7 +14,7 @@ #ifndef __BINDER__ASSEMBLY_NAME_INL__ #define __BINDER__ASSEMBLY_NAME_INL__ -SString &AssemblyName::GetSimpleName() +const SString &AssemblyName::GetSimpleName() { return m_simpleName; } @@ -125,9 +125,4 @@ void AssemblyName::SetIsDefinition(BOOL fIsDefinition) } } -void AssemblyName::SetHave(DWORD dwIdentityFlags) -{ - AssemblyIdentity::SetHave(dwIdentityFlags); -} - #endif diff --git a/src/coreclr/binder/inc/bindertypes.hpp b/src/coreclr/binder/inc/bindertypes.hpp index 08159ebc84048..101ef3c8d9593 100644 --- a/src/coreclr/binder/inc/bindertypes.hpp +++ b/src/coreclr/binder/inc/bindertypes.hpp @@ -17,17 +17,6 @@ #include "clrtypes.h" #include "sstring.h" -#include "fusionhelpers.hpp" - -extern void DECLSPEC_NORETURN ThrowOutOfMemory(); - -#ifndef S_TRUE -#define S_TRUE S_OK -#endif - -class PEImage; -class PEAssembly; - namespace BINDER_SPACE { class AssemblyVersion; @@ -40,8 +29,65 @@ namespace BINDER_SPACE class ApplicationContext; class BindResult; - class FailureCache; - class AssemblyBinder; +}; + +typedef enum __AssemblyContentType +{ + AssemblyContentType_Default = 0, + AssemblyContentType_WindowsRuntime = 0x1, + AssemblyContentType_Invalid = 0xffffffff, +} AssemblyContentType; + +typedef enum __ASM_DISPLAY_FLAGS +{ + ASM_DISPLAYF_VERSION = 0x1, + ASM_DISPLAYF_CULTURE = 0x2, + ASM_DISPLAYF_PUBLIC_KEY_TOKEN = 0x4, + ASM_DISPLAYF_PUBLIC_KEY = 0x8, + ASM_DISPLAYF_CUSTOM = 0x10, + ASM_DISPLAYF_PROCESSORARCHITECTURE = 0x20, + ASM_DISPLAYF_LANGUAGEID = 0x40, + ASM_DISPLAYF_RETARGET = 0x80, + ASM_DISPLAYF_CONFIG_MASK = 0x100, + ASM_DISPLAYF_MVID = 0x200, + ASM_DISPLAYF_CONTENT_TYPE = 0x400, + ASM_DISPLAYF_FULL = ASM_DISPLAYF_VERSION + | ASM_DISPLAYF_CULTURE + | ASM_DISPLAYF_PUBLIC_KEY_TOKEN + | ASM_DISPLAYF_RETARGET + | ASM_DISPLAYF_PROCESSORARCHITECTURE + | ASM_DISPLAYF_CONTENT_TYPE, +} ASM_DISPLAY_FLAGS; + +typedef enum __PEKIND +{ + peNone = 0x00000000, + peMSIL = 0x00000001, + peI386 = 0x00000002, + peIA64 = 0x00000003, + peAMD64 = 0x00000004, + peARM = 0x00000005, + peARM64 = 0x00000006, + peInvalid = 0xffffffff, +} PEKIND; + +struct AssemblyNameData +{ + LPCSTR Name; + LPCSTR Culture; + + const BYTE* PublicKeyOrToken; + DWORD PublicKeyOrTokenLength; + + DWORD MajorVersion; + DWORD MinorVersion; + DWORD BuildNumber; + DWORD RevisionNumber; + + PEKIND ProcessorArchitecture; + AssemblyContentType ContentType; + + DWORD IdentityFlags; }; #define IF_FAIL_GO(expr) \ @@ -61,16 +107,6 @@ namespace BINDER_SPACE hr = hrValue; \ goto Exit; -#define IF_WIN32_ERROR_GO(expr) \ - if (!(expr)) \ - { \ - hr = HRESULT_FROM_GetLastError(); \ - goto Exit; \ - } - -#define NEW_CONSTR(Object, Constr) \ - (Object) = new (nothrow) Constr; - #define SAFE_NEW_CONSTR(Object, Constr) \ (Object) = new (nothrow) Constr; \ if ((Object) == NULL) \ diff --git a/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h b/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h index 3c1c406230209..4573575c1b7c6 100644 --- a/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h +++ b/src/coreclr/binder/inc/clrprivbinderassemblyloadcontext.h @@ -5,22 +5,13 @@ #ifndef __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ #define __CLRPRIVBINDERASSEMBLYLOADCONTEXT_H__ -#include "coreclrbindercommon.h" #include "applicationcontext.hpp" #include "clrprivbindercoreclr.h" #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) -namespace BINDER_SPACE -{ - class AssemblyIdentityUTF8; -}; - -class AppDomain; - -class Object; -class Assembly; class LoaderAllocator; +class PEImage; class CLRPrivBinderAssemblyLoadContext : public AssemblyLoadContext { @@ -30,7 +21,7 @@ class CLRPrivBinderAssemblyLoadContext : public AssemblyLoadContext // ICLRPrivBinder functions //------------------------------------------------------------------------- STDMETHOD(BindAssemblyByName)( - /* [in] */ IAssemblyName *pIAssemblyName, + /* [in] */ struct AssemblyNameData *pAssemblyNameData, /* [retval][out] */ ICLRPrivAssembly **ppAssembly); STDMETHOD(GetLoaderAllocator)( @@ -74,7 +65,7 @@ class CLRPrivBinderAssemblyLoadContext : public AssemblyLoadContext // A strong GC handle to the managed AssemblyLoadContext. This handle is set when the unload of the AssemblyLoadContext is initiated // to keep the managed AssemblyLoadContext alive until the unload is finished. - // We still keep the weak handle pointing to the same managed AssemblyLoadContext so that native code can use the handle above + // We still keep the weak handle pointing to the same managed AssemblyLoadContext so that native code can use the handle above // to refer to it during the whole lifetime of the AssemblyLoadContext. INT_PTR m_ptrManagedStrongAssemblyLoadContext; diff --git a/src/coreclr/binder/inc/clrprivbindercoreclr.h b/src/coreclr/binder/inc/clrprivbindercoreclr.h index bf11bcf113f96..ff47c561d0948 100644 --- a/src/coreclr/binder/inc/clrprivbindercoreclr.h +++ b/src/coreclr/binder/inc/clrprivbindercoreclr.h @@ -5,14 +5,11 @@ #ifndef __CLR_PRIV_BINDER_CORECLR_H__ #define __CLR_PRIV_BINDER_CORECLR_H__ -#include "coreclrbindercommon.h" #include "applicationcontext.hpp" #include "assemblyloadcontext.h" -namespace BINDER_SPACE -{ - class AssemblyIdentityUTF8; -}; +class PEAssembly; +class PEImage; class CLRPrivBinderCoreCLR : public AssemblyLoadContext { @@ -22,7 +19,7 @@ class CLRPrivBinderCoreCLR : public AssemblyLoadContext // ICLRPrivBinder functions //------------------------------------------------------------------------- STDMETHOD(BindAssemblyByName)( - /* [in] */ IAssemblyName *pIAssemblyName, + /* [in] */ struct AssemblyNameData *pAssemblyNameData, /* [retval][out] */ ICLRPrivAssembly **ppAssembly); STDMETHOD(GetLoaderAllocator)( @@ -40,13 +37,15 @@ class CLRPrivBinderCoreCLR : public AssemblyLoadContext return &m_appContext; } - HRESULT Bind(SString &assemblyDisplayName, - LPCWSTR wszCodeBase, + HRESULT Bind(LPCWSTR wszCodeBase, PEAssembly *pParentAssembly, BOOL fNgenExplicitBind, BOOL fExplicitBindToNativeImage, ICLRPrivAssembly **ppAssembly); + HRESULT BindUsingAssemblyName(BINDER_SPACE::AssemblyName *pAssemblyName, + ICLRPrivAssembly **ppAssembly); + #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) HRESULT BindUsingPEImage( /* in */ PEImage *pPEImage, /* in */ BOOL fIsNativeImage, diff --git a/src/coreclr/binder/inc/coreclrbindercommon.h b/src/coreclr/binder/inc/coreclrbindercommon.h index 4fe3249363b61..d64b84e50b487 100644 --- a/src/coreclr/binder/inc/coreclrbindercommon.h +++ b/src/coreclr/binder/inc/coreclrbindercommon.h @@ -6,10 +6,8 @@ #define __CORECLR_BINDER_COMMON_H__ #include "clrprivbinding.h" -#include "internalunknownimpl.h" #include "applicationcontext.hpp" - namespace BINDER_SPACE { class AssemblyIdentityUTF8; @@ -21,10 +19,8 @@ class CLRPrivBinderCoreCLR; class CCoreCLRBinderHelper { public: - static HRESULT Init(); - static HRESULT DefaultBinderSetupContext(DWORD dwAppDomainId, - CLRPrivBinderCoreCLR **ppTPABinder); + CLRPrivBinderCoreCLR **ppTPABinder); // ABHI-TODO: The call indicates that this can come from a case where // pDomain->GetFusionContext() is null, hence this is static function diff --git a/src/coreclr/binder/inc/failurecachehashtraits.hpp b/src/coreclr/binder/inc/failurecachehashtraits.hpp index 0d9ad26156d12..1ffa96f56b198 100644 --- a/src/coreclr/binder/inc/failurecachehashtraits.hpp +++ b/src/coreclr/binder/inc/failurecachehashtraits.hpp @@ -15,7 +15,6 @@ #define __BINDER__FAILURE_CACHE_HASH_TRAITS_HPP__ #include "bindertypes.hpp" -#include "utils.hpp" #include "sstring.h" #include "shash.h" @@ -66,11 +65,11 @@ namespace BINDER_SPACE } static BOOL Equals(key_t pAssemblyNameOrPath1, key_t pAssemblyNameOrPath2) { - return EqualsCaseInsensitive(pAssemblyNameOrPath1, pAssemblyNameOrPath2); + return pAssemblyNameOrPath1.EqualsCaseInsensitive(pAssemblyNameOrPath2); } static count_t Hash(key_t pAssemblyNameOrPath) { - return HashCaseInsensitive(pAssemblyNameOrPath); + return pAssemblyNameOrPath.HashCaseInsensitive(); } static element_t Null() { diff --git a/src/coreclr/binder/inc/fusionassemblyname.hpp b/src/coreclr/binder/inc/fusionassemblyname.hpp deleted file mode 100644 index d5676c7c1c6f1..0000000000000 --- a/src/coreclr/binder/inc/fusionassemblyname.hpp +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// FusionAssemblyName.hpp -// -// Defines the CAssemblyName class -// -// ============================================================ - -#ifndef __FUSION_ASSEMBLY_NAME_HPP__ -#define __FUSION_ASSEMBLY_NAME_HPP__ - -#include "fusionhelpers.hpp" - -struct FusionProperty -{ - union { - LPVOID pv; - WCHAR* asStr; // For debugging. - }; - DWORD cb; -}; - -class CPropertyArray -{ - friend class CAssemblyName; -private: - DWORD _dwSig; - FusionProperty _rProp[ASM_NAME_MAX_PARAMS]; - -public: - CPropertyArray(); - ~CPropertyArray(); - - inline HRESULT Set(DWORD PropertyId, LPCVOID pvProperty, DWORD cbProperty); - inline HRESULT Get(DWORD PropertyId, LPVOID pvProperty, LPDWORD pcbProperty); - inline FusionProperty operator [] (DWORD dwPropId); -}; - -class CAssemblyName final : public IAssemblyName -{ -private: - DWORD _dwSig; - Volatile _cRef; - CPropertyArray _rProp; - BOOL _fPublicKeyToken; - BOOL _fCustom; - -public: - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID riid,void ** ppv); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - - // IAssemblyName methods - STDMETHOD(SetProperty)( - /* in */ DWORD PropertyId, - /* in */ LPCVOID pvProperty, - /* in */ DWORD cbProperty); - - STDMETHOD(GetProperty)( - /* in */ DWORD PropertyId, - /* out */ LPVOID pvProperty, - /* in out */ LPDWORD pcbProperty); - - HRESULT SetPropertyInternal(/* in */ DWORD PropertyId, - /* in */ LPCVOID pvProperty, - /* in */ DWORD cbProperty); - - CAssemblyName(); - - HRESULT Parse(LPCWSTR szDisplayName); -}; - -STDAPI -CreateAssemblyNameObject( - LPASSEMBLYNAME *ppAssemblyName, - LPCOLESTR szAssemblyName); - -namespace fusion -{ - namespace util - { - // Fills the provided buffer with the contents of the property. pcbBuf is - // set to be either the required buffer space when insufficient buffer is - // provided, or the number of bytes written. - // - // Returns S_FALSE if the property has not been set, regardless of the values of pBuf and pcbBuf. - HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PVOID pBuf, DWORD *pcbBuf); - - // Fills the provided buffer with the contents of the property. If no buffer is provided - // (*ppBuf == nullptr), then a buffer is allocated for the caller and ppBuf is set to point - // at the allocated buffer on return. pcbBuf is set to be either the required buffer space - // when insufficient buffer is provided, or the number of bytes written. - // - // Returns S_FALSE if the property has not been set, regardless of the values of pBuf and pcbBuf. - HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, PBYTE * ppBuf, DWORD *pcbBuf); - - // Fills the provided SString with the contents of the property. - // - // Returns S_FALSE if the property has not been set. - HRESULT GetProperty(IAssemblyName * pName, DWORD dwProperty, SString & ssVal); - - inline HRESULT GetSimpleName(IAssemblyName * pName, SString & ssName) - { return GetProperty(pName, ASM_NAME_NAME, ssName); } - } // namespace fusion.util -} // namespace fusion - - -#endif diff --git a/src/coreclr/binder/inc/fusionhelpers.hpp b/src/coreclr/binder/inc/fusionhelpers.hpp deleted file mode 100644 index e3e31af840677..0000000000000 --- a/src/coreclr/binder/inc/fusionhelpers.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// FusionHelpers.hpp -// -// Defines various legacy fusion types -// -// ============================================================ - -#ifndef __FUSION_HELPERS_HPP__ -#define __FUSION_HELPERS_HPP__ - -#include "clrtypes.h" -#include "sstring.h" - -#include "clrhost.h" -#include "shlwapi.h" -#include "winwrap.h" -#include "ex.h" -#include "fusion.h" - - -#include "peinformation.h" - -#define FUSION_NEW_SINGLETON(_type) new (nothrow) _type -#define FUSION_NEW_ARRAY(_type, _n) new (nothrow) _type[_n] -#define FUSION_DELETE_ARRAY(_ptr) if((_ptr)) delete [] (_ptr) -#define FUSION_DELETE_SINGLETON(_ptr) if((_ptr)) delete (_ptr) - -#define SAFEDELETE(p) if ((p) != NULL) { FUSION_DELETE_SINGLETON((p)); (p) = NULL; }; -#define SAFEDELETEARRAY(p) if ((p) != NULL) { FUSION_DELETE_ARRAY((p)); (p) = NULL; }; -#define SAFERELEASE(p) if ((p) != NULL) { (p)->Release(); (p) = NULL; }; - -#ifndef NEW -#define NEW(_type) FUSION_NEW_SINGLETON(_type) -#endif // !NEW - -#ifndef ARRAYSIZE -#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) -#endif // !ARRAYSIZE - -#define MAX_VERSION_DISPLAY_SIZE sizeof("65535.65535.65535.65535") - -#define ASM_DISPLAYF_DEFAULT (ASM_DISPLAYF_VERSION \ - |ASM_DISPLAYF_CULTURE \ - |ASM_DISPLAYF_PUBLIC_KEY_TOKEN \ - |ASM_DISPLAYF_RETARGET) - -#define SIGNATURE_BLOB_LENGTH 0x80 -#define SIGNATURE_BLOB_LENGTH_HASH 0x14 -#define MVID_LENGTH sizeof(GUID) - -#define PUBLIC_KEY_TOKEN_LEN 8 - -#define MAX_URL_LENGTH 2084 // same as INTERNET_MAX_URL_LENGTH - -// bit mask macro helpers -#define MAX_ID_FROM_MASK(size) ((size) << 3) -#define MASK_SIZE_FROM_ID(id) ((id) >> 3) -#define IS_IN_RANGE(id, size) ((id) <= ((size) << 3)) -#define IS_BIT_SET(id, mask) (mask[((id)-1)>>3] & (0x1 << (((id)-1)&0x7))) -#define SET_BIT(id, mask) (mask[((id)-1)>>3] |= (0x1<< (((id)-1)&0x7))) -#define UNSET_BIT(id, mask) (mask[((id)-1)>>3] &= (0xFF - (0x1<<(((id)-1)&0x7)))) - -inline -int FusionCompareStringI(LPCWSTR pwz1, LPCWSTR pwz2) -{ - return SString::_wcsicmp(pwz1, pwz2); -} - -#endif diff --git a/src/coreclr/binder/inc/utils.hpp b/src/coreclr/binder/inc/utils.hpp index 1c8ea114f5e6c..768cf4f576f21 100644 --- a/src/coreclr/binder/inc/utils.hpp +++ b/src/coreclr/binder/inc/utils.hpp @@ -18,21 +18,11 @@ namespace BINDER_SPACE { - inline BOOL EqualsCaseInsensitive(SString &a, SString &b) - { - return a.EqualsCaseInsensitive(b); - } - - inline ULONG HashCaseInsensitive(SString &string) - { - return string.HashCaseInsensitive(); - } - void MutateUrlToPath(SString &urlOrPath); // It is safe to use either A or B as CombinedPath. - void CombinePath(SString &pathA, - SString &pathB, + void CombinePath(const SString &pathA, + const SString &pathB, SString &combinedPath); HRESULT GetTokenFromPublicKey(SBuffer &publicKeyBLOB, diff --git a/src/coreclr/binder/inc/variables.hpp b/src/coreclr/binder/inc/variables.hpp deleted file mode 100644 index d060a691bbecc..0000000000000 --- a/src/coreclr/binder/inc/variables.hpp +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// Variables.hpp -// - - -// -// Defines the Variables class -// -// ============================================================ - -#ifndef __BINDER__VARIABLES_HPP__ -#define __BINDER__VARIABLES_HPP__ - -#include "bindertypes.hpp" - -namespace BINDER_SPACE -{ - class Variables - { - public: - Variables(); - ~Variables(); - - HRESULT Init(); - - // AssemblyBinder string constants - SString httpURLPrefix; - - // AssemblyName string constants - SString cultureNeutral; - SString corelib; - }; - - extern Variables *g_BinderVariables; -}; - -#endif diff --git a/src/coreclr/binder/textualidentityparser.cpp b/src/coreclr/binder/textualidentityparser.cpp index 939ffe9f468c0..5d85a0a71cf37 100644 --- a/src/coreclr/binder/textualidentityparser.cpp +++ b/src/coreclr/binder/textualidentityparser.cpp @@ -13,7 +13,6 @@ #include "textualidentityparser.hpp" #include "assemblyidentity.hpp" -#include "utils.hpp" #include "ex.h" @@ -94,11 +93,9 @@ namespace BINDER_SPACE } } - inline BOOL EqualsCaseInsensitive(SString &a, LPCWSTR wzB) + inline bool EqualsCaseInsensitive(const SString &a, LPCWSTR wzB) { - SString b(SString::Literal, wzB); - - return ::BINDER_SPACE::EqualsCaseInsensitive(a, b); + return SString::_wcsicmp(a.GetUnicode(), wzB) == 0; } BOOL ValidateHex(SString &publicKeyOrToken) diff --git a/src/coreclr/binder/utils.cpp b/src/coreclr/binder/utils.cpp index 21fc115a77662..916dd677f9106 100644 --- a/src/coreclr/binder/utils.cpp +++ b/src/coreclr/binder/utils.cpp @@ -74,8 +74,8 @@ namespace BINDER_SPACE } } - void CombinePath(SString &pathA, - SString &pathB, + void CombinePath(const SString &pathA, + const SString &pathB, SString &combinedPath) { SString platformPathSeparator(SString::Literal, GetPlatformPathSeparator()); diff --git a/src/coreclr/binder/variables.cpp b/src/coreclr/binder/variables.cpp deleted file mode 100644 index fbdd106b4dd2f..0000000000000 --- a/src/coreclr/binder/variables.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// ============================================================ -// -// Variables.cpp -// - - -// -// Implements the Variables class -// -// ============================================================ - -#include "variables.hpp" - -#include "ex.h" - -namespace BINDER_SPACE -{ - Variables *g_BinderVariables = NULL; - - Variables::Variables() - { - // Nothing to do here - } - - Variables::~Variables() - { - // Nothing to do here - } - - HRESULT Variables::Init() - { - HRESULT hr = S_OK; - - EX_TRY - { - // AssemblyBinder string constants - httpURLPrefix.SetLiteral(W("http://")); - - // AssemblyName string constants - cultureNeutral.SetLiteral(W("neutral")); - corelib.SetLiteral(CoreLibName_W); - } - EX_CATCH_HRESULT(hr); - - return hr; - } -}; diff --git a/src/coreclr/build-runtime.cmd b/src/coreclr/build-runtime.cmd index 7373015d11150..25cffd330c7df 100644 --- a/src/coreclr/build-runtime.cmd +++ b/src/coreclr/build-runtime.cmd @@ -8,25 +8,11 @@ set "__MsgPrefix=BUILD: " echo %__MsgPrefix%Starting Build at %TIME% set __ThisScriptFull="%~f0" -set __ThisScriptDir="%~dp0" - -call %__ThisScriptDir%\setup_vs_tools.cmd -if NOT '%ERRORLEVEL%' == '0' goto ExitWithError - -if defined VS160COMNTOOLS ( - set "__VSToolsRoot=%VS160COMNTOOLS%" - set "__VCToolsRoot=%VS160COMNTOOLS%\..\..\VC\Auxiliary\Build" - set __VSVersion=vs2019 -) else if defined VS150COMNTOOLS ( - set "__VSToolsRoot=%VS150COMNTOOLS%" - set "__VCToolsRoot=%VS150COMNTOOLS%\..\..\VC\Auxiliary\Build" - set __VSVersion=vs2017 -) :: Note that the msbuild project files (specifically, dir.proj) will use the following variables, if set: :: __BuildArch -- default: x64 :: __BuildType -- default: Debug -:: __TargetOS -- default: windows +:: __TargetOS -- default: windows :: __ProjectDir -- default: directory of the dir.props file :: __RepoRootDir -- default: directory two levels above the dir.props file :: __RootBinDir -- default: %__RepoRootDir%\artifacts\ @@ -82,16 +68,13 @@ set __BuildCrossArchNative=0 set __SkipCrossArchNative=0 set __SkipGenerateVersion=0 set __RestoreOptData=1 -set __BuildJit=1 -set __BuildPALTests=0 -set __BuildAllJits=1 -set __BuildRuntime=1 set __CrossArch= set __CrossArch2= set __CrossOS=0 set __PgoOptDataPath= set __CMakeArgs= -set __Ninja=0 +set __Ninja=1 +set __RequestedBuildComponents= @REM CMD has a nasty habit of eating "=" on the argument list, so passing: @REM -priority=1 @@ -165,13 +148,13 @@ if /i "%1" == "-skipnative" (set __BuildNative=0&set processedArgs=!pro if /i "%1" == "-skipcrossarchnative" (set __SkipCrossArchNative=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-skipgenerateversion" (set __SkipGenerateVersion=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-skiprestoreoptdata" (set __RestoreOptData=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-ninja" (set __Ninja=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +REM -ninja is a no-op option since Ninja is now the default generator on Windows. +if /i "%1" == "-ninja" (set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-msbuild" (set __Ninja=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-pgoinstrument" (set __PgoInstrument=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-enforcepgo" (set __EnforcePgo=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "-nopgooptimize" (set __PgoOptimize=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-skipjit" (set __BuildJit=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-skipalljits" (set __BuildAllJits=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) -if /i "%1" == "-skipruntime" (set __BuildRuntime=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "-component" (set __RequestedBuildComponents=%__RequestedBuildComponents%-%2&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop) REM TODO these are deprecated remove them eventually REM don't add more, use the - syntax instead @@ -207,6 +190,14 @@ if defined __Priority ( ) ) +:: Initialize VS environment +call %__RepoRootDir%\eng\native\init-vs-env.cmd +if NOT '%ERRORLEVEL%' == '0' goto ExitWithError + +if defined VCINSTALLDIR ( + set "__VCToolsRoot=%VCINSTALLDIR%Auxiliary\Build" +) + if defined __BuildAll goto BuildAll set /A __TotalSpecifiedBuildArch=__BuildArchX64 + __BuildArchX86 + __BuildArchArm + __BuildArchArm64 @@ -215,6 +206,9 @@ if %__TotalSpecifiedBuildArch% GTR 1 ( goto Usage ) +set __ProcessorArch=%PROCESSOR_ARCHITEW6432% +if "%__ProcessorArch%"=="" set __ProcessorArch=%PROCESSOR_ARCHITECTURE% + if %__BuildArchX64%==1 set __BuildArch=x64 if %__BuildArchX86%==1 ( set __BuildArch=x86 @@ -227,7 +221,7 @@ if %__BuildArchArm%==1 ( ) if %__BuildArchArm64%==1 ( set __BuildArch=arm64 - set __CrossArch=x64 + if /i not "%__ProcessorArch%"=="ARM64" set __CrossArch=x64 ) set /A __TotalSpecifiedBuildType=__BuildTypeDebug + __BuildTypeChecked + __BuildTypeRelease @@ -258,7 +252,7 @@ REM Determine if this is a cross-arch build. Only do cross-arch build if we're a if %__SkipCrossArchNative% EQU 0 ( if %__BuildNative% EQU 1 ( if /i "%__BuildArch%"=="arm64" ( - set __BuildCrossArchNative=1 + if defined __CrossArch set __BuildCrossArchNative=1 ) if /i "%__BuildArch%"=="arm" ( set __BuildCrossArchNative=1 @@ -285,7 +279,7 @@ set "__IntermediatesDir=%__RootBinDir%\obj\coreclr\%__TargetOS%.%__BuildArch%.%_ set "__LogsDir=%__RootBinDir%\log\!__BuildType!" set "__MsbuildDebugLogsDir=%__LogsDir%\MsbuildDebugLogs" set "__ArtifactsIntermediatesDir=%__RepoRootDir%\artifacts\obj\coreclr\" -if "%__Ninja%"=="1" (set "__IntermediatesDir=%__RootBinDir%\nmakeobj\%__TargetOS%.%__BuildArch%.%__BuildType%") +if "%__Ninja%"=="0" (set "__IntermediatesDir=%__IntermediatesDir%\ide") set "__PackagesBinDir=%__BinDir%\.nuget" set "__CrossComponentBinDir=%__BinDir%" set "__CrossCompIntermediatesDir=%__IntermediatesDir%\crossgen" @@ -422,7 +416,28 @@ if NOT DEFINED PYTHON ( goto ExitWithError ) -set __CMakeClrBuildSubsetArgs="-DCLR_CMAKE_BUILD_SUBSET_JIT=%__BuildJit%" "-DCLR_CMAKE_BUILD_SUBSET_ALLJITS=%__BuildAllJits%" "-DCLR_CMAKE_BUILD_SUBSET_RUNTIME=%__BuildRuntime%" +set __CMakeTarget= +for /f "delims=" %%a in ("-%__RequestedBuildComponents%-") do ( + set "string=%%a" + if not "!string:-jit-=!"=="!string!" ( + set __CMakeTarget=!__CMakeTarget! jit + ) + if not "!string:-alljits-=!"=="!string!" ( + set __CMakeTarget=!__CMakeTarget! alljits + ) + if not "!string:-runtime-=!"=="!string!" ( + set __CMakeTarget=!__CMakeTarget! runtime + ) + if not "!string:-paltests-=!"=="!string!" ( + set __CMakeTarget=!__CMakeTarget! paltests_install + ) + if not "!string:-iltools-=!"=="!string!" ( + set __CMakeTarget=!__CMakeTarget! iltools + ) +) +if [!__CMakeTarget!] == [] ( + set __CMakeTarget=install +) REM ========================================================================================= REM === @@ -454,7 +469,7 @@ if %__BuildCrossArchNative% EQU 1 ( set __ExtraCmakeArgs="-DCMAKE_BUILD_TYPE=!__BuildType!" ) - set __ExtraCmakeArgs=!__ExtraCmakeArgs! %__CMakeClrBuildSubsetArgs% "-DCLR_CROSS_COMPONENTS_BUILD=1" "-DCLR_CMAKE_TARGET_ARCH=%__BuildArch%" "-DCLR_CMAKE_TARGET_OS=%__TargetOS%" "-DCLR_CMAKE_PGO_INSTRUMENT=0" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=0" %__CMakeArgs% + set __ExtraCmakeArgs=!__ExtraCmakeArgs! "-DCLR_CROSS_COMPONENTS_BUILD=1" "-DCLR_CMAKE_TARGET_ARCH=%__BuildArch%" "-DCLR_CMAKE_TARGET_OS=%__TargetOS%" "-DCLR_CMAKE_PGO_INSTRUMENT=0" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=0" %__CMakeArgs% call "%__RepoRootDir%\eng\native\gen-buildsys.cmd" "%__ProjectDir%" "%__CrossCompIntermediatesDir%" %__VSVersion% %__CrossArch% !__ExtraCmakeArgs! if not !errorlevel! == 0 ( @@ -491,7 +506,7 @@ if %__BuildCrossArchNative% EQU 1 ( set __CmakeBuildToolArgs=/nologo /m !__Logging! ) - "%CMakePath%" --build %__CrossCompIntermediatesDir% --target install --config %__BuildType% -- !__CmakeBuildToolArgs! + "%CMakePath%" --build %__CrossCompIntermediatesDir% --target crosscomponents --config %__BuildType% -- !__CmakeBuildToolArgs! if not !errorlevel! == 0 ( set __exitCode=!errorlevel! @@ -528,7 +543,7 @@ if %__BuildCrossArchNative% EQU 1 ( set __ExtraCmakeArgs="-DCMAKE_BUILD_TYPE=!__BuildType!" ) - set __ExtraCmakeArgs=!__ExtraCmakeArgs! %__CMakeClrBuildSubsetArgs% "-DCLR_CROSS_COMPONENTS_BUILD=1" "-DCLR_CMAKE_TARGET_ARCH=%__BuildArch%" "-DCLR_CMAKE_TARGET_OS=%__TargetOS%" "-DCLR_CMAKE_PGO_INSTRUMENT=0" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=0" "-DCMAKE_SYSTEM_VERSION=10.0" %__CMakeArgs% + set __ExtraCmakeArgs=!__ExtraCmakeArgs! "-DCLR_CROSS_COMPONENTS_BUILD=1" "-DCLR_CMAKE_TARGET_ARCH=%__BuildArch%" "-DCLR_CMAKE_TARGET_OS=%__TargetOS%" "-DCLR_CMAKE_PGO_INSTRUMENT=0" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=0" "-DCMAKE_SYSTEM_VERSION=10.0" %__CMakeArgs% call "%__RepoRootDir%\eng\native\gen-buildsys.cmd" "%__ProjectDir%" "%__CrossComp2IntermediatesDir%" %__VSVersion% %__CrossArch2% !__ExtraCmakeArgs! if not !errorlevel! == 0 ( @@ -566,7 +581,7 @@ if %__BuildCrossArchNative% EQU 1 ( set __CmakeBuildToolArgs=/nologo /m !__Logging! ) - "%CMakePath%" --build %__CrossComp2IntermediatesDir% --target install --config %__BuildType% -- !__CmakeBuildToolArgs! + "%CMakePath%" --build %__CrossComp2IntermediatesDir% --target crosscomponents --config %__BuildType% -- !__CmakeBuildToolArgs! if not !errorlevel! == 0 ( set __exitCode=!errorlevel! @@ -603,19 +618,15 @@ if %__BuildNative% EQU 1 ( ) if /i "%__BuildArch%" == "arm64" ( set __VCBuildArch=x86_arm64 - set ___CrossBuildDefine="-DCLR_CMAKE_CROSS_ARCH=1" "-DCLR_CMAKE_CROSS_HOST_ARCH=%__CrossArch%" + if defined __CrossArch ( + set ___CrossBuildDefine="-DCLR_CMAKE_CROSS_ARCH=1" "-DCLR_CMAKE_CROSS_HOST_ARCH=%__CrossArch%" + ) ) echo %__MsgPrefix%Using environment: "%__VCToolsRoot%\vcvarsall.bat" !__VCBuildArch! call "%__VCToolsRoot%\vcvarsall.bat" !__VCBuildArch! @if defined _echo @echo on - if not defined VSINSTALLDIR ( - echo %__ErrMsgPrefix%%__MsgPrefix%Error: VSINSTALLDIR variable not defined. - goto ExitWithError - ) - if not exist "!VSINSTALLDIR!DIA SDK" goto NoDIA - if defined __SkipConfigure goto SkipConfigure echo %__MsgPrefix%Regenerating the Visual Studio solution @@ -624,7 +635,7 @@ if %__BuildNative% EQU 1 ( set __ExtraCmakeArgs="-DCMAKE_BUILD_TYPE=!__BuildType!" ) - set __ExtraCmakeArgs=!__ExtraCmakeArgs! !___CrossBuildDefine! %__CMakeClrBuildSubsetArgs% "-DCLR_CMAKE_PGO_INSTRUMENT=%__PgoInstrument%" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=%__PgoOptimize%" %__CMakeArgs% + set __ExtraCmakeArgs=!__ExtraCmakeArgs! !___CrossBuildDefine! "-DCLR_CMAKE_PGO_INSTRUMENT=%__PgoInstrument%" "-DCLR_CMAKE_OPTDATA_PATH=%__PgoOptDataPath%" "-DCLR_CMAKE_PGO_OPTIMIZE=%__PgoOptimize%" %__CMakeArgs% call "%__RepoRootDir%\eng\native\gen-buildsys.cmd" "%__ProjectDir%" "%__IntermediatesDir%" %__VSVersion% %__BuildArch% !__ExtraCmakeArgs! if not !errorlevel! == 0 ( echo %__ErrMsgPrefix%%__MsgPrefix%Error: failed to generate native component build project! @@ -660,7 +671,8 @@ if %__BuildNative% EQU 1 ( set __CmakeBuildToolArgs=/nologo /m !__Logging! ) - "%CMakePath%" --build %__IntermediatesDir% --target install --config %__BuildType% -- !__CmakeBuildToolArgs! + echo running "%CMakePath%" --build %__IntermediatesDir% --target %__CMakeTarget% --config %__BuildType% -- !__CmakeBuildToolArgs! + "%CMakePath%" --build %__IntermediatesDir% --target %__CMakeTarget% --config %__BuildType% -- !__CmakeBuildToolArgs! if not !errorlevel! == 0 ( set __exitCode=!errorlevel! @@ -712,6 +724,7 @@ REM ============================================================================ echo %__MsgPrefix%Build succeeded. Finished at %TIME% echo %__MsgPrefix%Product binaries are available at !__BinDir! + exit /b 0 REM ========================================================================================= @@ -845,11 +858,3 @@ echo -- builds all build types for x86 echo build -all -x64 -x86 -Checked -Release echo -- builds x64 and x86 architectures, Checked and Release build types for each exit /b 1 - -:NoDIA -echo Error: DIA SDK is missing at "%VSINSTALLDIR%DIA SDK". ^ -Did you install all the requirements for building on Windows, including the "Desktop Development with C++" workload? ^ -Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md ^ -Another possibility is that you have a parallel installation of Visual Studio and the DIA SDK is there. In this case it ^ -may help to copy its "DIA SDK" folder into "%VSINSTALLDIR%" manually, then try again. -exit /b 1 diff --git a/src/coreclr/build-runtime.sh b/src/coreclr/build-runtime.sh index 25cf3e06ea6b9..540cbc33bb070 100755 --- a/src/coreclr/build-runtime.sh +++ b/src/coreclr/build-runtime.sh @@ -21,11 +21,8 @@ export PYTHON usage_list+=("-nopgooptimize: do not use profile guided optimizations.") usage_list+=("-pgoinstrument: generate instrumented code for profile guided optimization enabled binaries.") usage_list+=("-skipcrossarchnative: Skip building cross-architecture native binaries.") -usage_list+=("-staticanalyzer: skip native image generation.") -usage_list+=("-skipjit: skip building jit.") -usage_list+=("-skipalljits: skip building crosstargetting jits.") -usage_list+=("-skipruntime: skip building runtime.") -usage_list+=("-paltests: build the pal tests.") +usage_list+=("-staticanalyzer: use scan_build static analyzer.") +usage_list+=("-component: Build individual components instead of the full project. Available options are 'jit', 'runtime', 'paltests', 'alljits', and 'iltools'. Can be specified multiple times.") setup_dirs_local() { @@ -99,7 +96,7 @@ build_cross_architecture_components() export __CMakeBinDir CROSSCOMPILE __CMakeArgs="-DCLR_CMAKE_TARGET_ARCH=$__BuildArch -DCLR_CROSS_COMPONENTS_BUILD=1 $__CMakeArgs" - build_native "$__TargetOS" "$__CrossArch" "$__ProjectRoot" "$intermediatesForBuild" "$__CMakeArgs" "cross-architecture components" + build_native "$__TargetOS" "$__CrossArch" "$__ProjectRoot" "$intermediatesForBuild" "crosscomponents" "$__CMakeArgs" "cross-architecture components" CROSSCOMPILE=1 export CROSSCOMPILE @@ -125,20 +122,9 @@ handle_arguments_local() { __StaticAnalyzer=1 ;; - skipjit|-skipjit) - __BuildJit=0 - ;; - - skipalljits|-skipalljits) - __BuildAllJits=0 - ;; - - skipruntime|-skipruntime) - __BuildRuntime=0 - ;; - - paltests|-paltests) - __BuildPALTests=1 + component|-component) + __RequestedBuildComponents="$__RequestedBuildComponents $2" + __ShiftArgs=1 ;; *) __UnprocessedBuildArgs="$__UnprocessedBuildArgs $1" @@ -192,9 +178,10 @@ __UseNinja=0 __VerboseBuild=0 __ValidateCrossArg=1 __CMakeArgs="" -__BuildPALTests=0 +__RequestedBuildComponents="" __BuildAllJits=1 __BuildRuntime=1 +__BuildILTools=1 source "$__ProjectRoot"/_build-commons.sh @@ -247,17 +234,24 @@ restore_optdata # Build the coreclr (native) components. __CMakeArgs="-DCLR_CMAKE_PGO_INSTRUMENT=$__PgoInstrument -DCLR_CMAKE_OPTDATA_PATH=$__PgoOptDataPath -DCLR_CMAKE_PGO_OPTIMIZE=$__PgoOptimize $__CMakeArgs" -__CMakeArgs="-DCLR_CMAKE_BUILD_SUBSET_JIT=$__BuildJit -DCLR_CMAKE_BUILD_SUBSET_ALLJITS=$__BuildAllJits -DCLR_CMAKE_BUILD_SUBSET_RUNTIME=$__BuildRuntime $__CMakeArgs" -__CMakeArgs="-DCLR_CMAKE_BUILD_TESTS=$__BuildPALTests $__CMakeArgs" if [[ "$__SkipConfigure" == 0 && "$__CodeCoverage" == 1 ]]; then __CMakeArgs="-DCLR_CMAKE_ENABLE_CODE_COVERAGE=1 $__CMakeArgs" fi +__CMakeTarget="" +if [[ -n "$__RequestedBuildComponents" ]]; then + __CMakeTarget=" $__RequestedBuildComponents " + __CMakeTarget="${__CMakeTarget// paltests / paltests_install }" +fi +if [[ -z "$__CMakeTarget" ]]; then + __CMakeTarget="install" +fi + if [[ "$__SkipNative" == 1 ]]; then echo "Skipping CoreCLR component build." else - build_native "$__TargetOS" "$__BuildArch" "$__ProjectRoot" "$__IntermediatesDir" "$__CMakeArgs" "CoreCLR component" + build_native "$__TargetOS" "$__BuildArch" "$__ProjectRoot" "$__IntermediatesDir" "$__CMakeTarget" "$__CMakeArgs" "CoreCLR component" # Build cross-architecture components if [[ "$__SkipCrossArchNative" != 1 ]]; then diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 9c774cd4aeb92..115031ff49b6c 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -1,4 +1,4 @@ -include(clrfeatures.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/clrfeatures.cmake) add_compile_definitions($<$>:DACCESS_COMPILE>) add_compile_definitions($<$>:CROSSGEN_COMPILE>) diff --git a/src/coreclr/components.cmake b/src/coreclr/components.cmake new file mode 100644 index 0000000000000..3d630bf14a6b6 --- /dev/null +++ b/src/coreclr/components.cmake @@ -0,0 +1,21 @@ +# Define all the individually buildable components of the CoreCLR build and their respective targets +add_component(jit) +add_component(alljits) +add_component(runtime) +add_component(paltests paltests_install) +add_component(iltools) + +# Define coreclr_all as the fallback component and make every component depend on this component. +# iltools should be a minimal subset, so don't add a dependency on coreclr_misc +set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME coreclr_misc) +add_component(coreclr_misc) +add_dependencies(jit coreclr_misc) +add_dependencies(alljits coreclr_misc) +add_dependencies(runtime coreclr_misc) +add_dependencies(paltests_install coreclr_misc) + +# The runtime build requires the clrjit and iltools builds +add_dependencies(runtime jit iltools) + +# The cross-components build is separate, so we don't need to add a dependency on coreclr_misc +add_component(crosscomponents) diff --git a/src/coreclr/crosscomponents.cmake b/src/coreclr/crosscomponents.cmake index c66531887daa0..e8d51914951e3 100644 --- a/src/coreclr/crosscomponents.cmake +++ b/src/coreclr/crosscomponents.cmake @@ -1,31 +1,39 @@ -add_definitions(-DCROSS_COMPILE) - -if(CLR_CMAKE_HOST_ARCH_AMD64 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_I386)) - set(FEATURE_CROSSBITNESS 1) -endif(CLR_CMAKE_HOST_ARCH_AMD64 AND (CLR_CMAKE_TARGET_ARCH_ARM OR CLR_CMAKE_TARGET_ARCH_I386)) +# Add targets to the crosscomponents subcomponent build if (CLR_CMAKE_HOST_OS STREQUAL CLR_CMAKE_TARGET_OS) - set (CLR_CROSS_COMPONENTS_LIST + install_clr (TARGETS clrjit jitinterface_${ARCH_HOST_NAME} + DESTINATIONS . + COMPONENT crosscomponents ) if(CLR_CMAKE_HOST_LINUX OR NOT FEATURE_CROSSBITNESS) - list (APPEND CLR_CROSS_COMPONENTS_LIST + install_clr (TARGETS crossgen + DESTINATIONS . + COMPONENT crosscomponents ) endif() if (CLR_CMAKE_TARGET_UNIX) - list (APPEND CLR_CROSS_COMPONENTS_LIST + install_clr (TARGETS clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} + DESTINATIONS . + COMPONENT crosscomponents ) endif(CLR_CMAKE_TARGET_UNIX) endif() if(NOT CLR_CMAKE_HOST_LINUX AND NOT CLR_CMAKE_HOST_OSX AND NOT FEATURE_CROSSBITNESS) - list (APPEND CLR_CROSS_COMPONENTS_LIST + install_clr (TARGETS mscordaccore mscordbi + DESTINATIONS . + COMPONENT crosscomponents ) endif() + +if (CLR_CMAKE_TARGET_WIN32 AND NOT CLR_CMAKE_CROSS_ARCH) + add_dependencies(crosscomponents InjectResource GenClrDebugResource) +endif() diff --git a/src/coreclr/crossgen-corelib.cmd b/src/coreclr/crossgen-corelib.cmd index 882404d697ae1..52bbb6bd46bc2 100644 --- a/src/coreclr/crossgen-corelib.cmd +++ b/src/coreclr/crossgen-corelib.cmd @@ -7,9 +7,6 @@ set "__MsgPrefix=CROSSGEN-CORELIB: " echo %__MsgPrefix%Starting Build at %TIME% -set __ThisScriptFull="%~f0" -set __ThisScriptDir=%~dp0 - :: Note that the msbuild project files (specifically, dir.proj) will use the following variables, if set: :: __BuildArch -- default: x64 :: __BuildType -- default: Debug @@ -133,39 +130,15 @@ if not exist "%__BinDir%" md "%__BinDir%" if not exist "%__IntermediatesDir%" md "%__IntermediatesDir%" if not exist "%__LogsDir%" md "%__LogsDir%" - -call "%__ThisScriptDir%"\setup_vs_tools.cmd -if NOT '%ERRORLEVEL%' == '0' goto ExitWithError - -if defined VS160COMNTOOLS ( - set "__VSToolsRoot=%VS160COMNTOOLS%" - set "__VCToolsRoot=%VS160COMNTOOLS%\..\..\VC\Auxiliary\Build" - set __VSVersion=vs2019 -) else if defined VS150COMNTOOLS ( - set "__VSToolsRoot=%VS150COMNTOOLS%" - set "__VCToolsRoot=%VS150COMNTOOLS%\..\..\VC\Auxiliary\Build" - set __VSVersion=vs2017 -) - REM Need VC native tools environment for the host arch to find Microsoft.DiaSymReader.Native in the Visual Studio install. -set __VCBuildArch=x86_amd64 -if /i "%__BuildArch%" == "x86" ( set __VCBuildArch=x86 ) -if /i "%__BuildArch%" == "arm" ( - set __VCBuildArch=x86_arm -) -if /i "%__BuildArch%" == "arm64" ( - set __VCBuildArch=x86_arm64 -) +call %__RepoRootDir%\eng\native\init-vs-env.cmd %__BuildArch% +if NOT '%ERRORLEVEL%' == '0' goto ExitWithError -echo %__MsgPrefix%Using environment: "%__VCToolsRoot%\vcvarsall.bat" !__VCBuildArch! -call "%__VCToolsRoot%\vcvarsall.bat" !__VCBuildArch! @if defined _echo @echo on -if not defined VSINSTALLDIR ( - echo %__ErrMsgPrefix%%__MsgPrefix%Error: VSINSTALLDIR variable not defined. - goto ExitWithError +if defined VCINSTALLDIR ( + set "__VCToolsRoot=%VCINSTALLDIR%Auxiliary\Build" ) -if not exist "!VSINSTALLDIR!DIA SDK" goto NoDIA echo %__MsgPrefix%Generating native image of System.Private.CoreLib for %__TargetOS%.%__BuildArch%.%__BuildType%. Logging to "%__CrossGenCoreLibLog%". if exist "%__CrossGenCoreLibLog%" del "%__CrossGenCoreLibLog%" @@ -257,11 +230,3 @@ exit /b 1 :ExitWithCode exit /b !__exitCode! - -:NoDIA -echo Error: DIA SDK is missing at "%VSINSTALLDIR%DIA SDK". ^ -Did you install all the requirements for building on Windows, including the "Desktop Development with C++" workload? ^ -Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md ^ -Another possibility is that you have a parallel installation of Visual Studio and the DIA SDK is there. In this case it ^ -may help to copy its "DIA SDK" folder into "%VSINSTALLDIR%" manually, then try again. -exit /b 1 diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index 8687641d08632..2399255b78d0f 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -40,6 +40,11 @@ true + + + + + System.Private.CoreLib $([MSBuild]::NormalizePath('$(BinDir)', 'IL', '$(CoreLibAssemblyName).dll')) @@ -53,7 +58,7 @@ @@ -70,11 +75,17 @@ + + + + $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', '$(CrossDir)', 'crossgen2', 'crossgen2.dll')) $(CrossGenDllCmd) -o:$(CoreLibOutputPath) $(CrossGenDllCmd) -r:$([MSBuild]::NormalizePath('$(BinDir)', 'IL', '*.dll')) $(CrossGenDllCmd) --targetarch:$(TargetArchitecture) + @(OptimizationMibcFiles->'-m:%(Identity)', ' ') + $(CrossGenDllCmd) $(MibcArgs) --embed-pgo-data $(CrossGenDllCmd) -O $(CrossGenDllCmd) $(CoreLibInputPath) @@ -88,7 +99,7 @@ - call $([MSBuild]::NormalizePath('$(RepoRoot)', 'src', 'coreclr', 'setup_vs_tools.cmd')) && + call $([MSBuild]::NormalizePath('$(RepoRoot)', 'eng', 'native', 'init-vs-env.cmd')) && $(CrossGen1Cmd) /out "$(CoreLibOutputPath)" $(CrossGenDllCmd) "$(CoreLibInputPath)" diff --git a/src/coreclr/debug/createdump/CMakeLists.txt b/src/coreclr/debug/createdump/CMakeLists.txt index 2e55b9a991f1e..5e6eda6ee95f6 100644 --- a/src/coreclr/debug/createdump/CMakeLists.txt +++ b/src/coreclr/debug/createdump/CMakeLists.txt @@ -19,7 +19,7 @@ if(CLR_CMAKE_HOST_WIN32) createdump.rc ) - _add_executable(createdump + add_executable_clr(createdump ${CREATEDUMP_SOURCES} ) @@ -60,13 +60,13 @@ else(CLR_CMAKE_HOST_WIN32) ) if(CLR_CMAKE_HOST_OSX) - _add_executable(createdump + add_executable_clr(createdump crashinfomac.cpp threadinfomac.cpp ${CREATEDUMP_SOURCES} ) else() - _add_executable(createdump + add_executable_clr(createdump crashinfounix.cpp threadinfounix.cpp ${CREATEDUMP_SOURCES} @@ -86,4 +86,4 @@ endif(CLR_CMAKE_HOST_OSX) endif(CLR_CMAKE_HOST_WIN32) -install_clr(TARGETS createdump ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS createdump DESTINATIONS . sharedFramework COMPONENT runtime) diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index bccbb8553a916..4592b0d253a55 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -16,10 +16,8 @@ CrashInfo::CrashInfo(pid_t pid) : m_task = 0; #else m_auxvValues.fill(0); -#ifndef HAVE_PROCESS_VM_READV m_fd = -1; #endif -#endif } CrashInfo::~CrashInfo() diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 9bdf63be49e78..46b0825c0a7e3 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -45,9 +45,8 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, #ifdef __APPLE__ vm_map_t m_task; // the mach task for the process #else -#ifndef HAVE_PROCESS_VM_READV + bool m_canUseProcVmReadSyscall; int m_fd; // /proc//mem handle -#endif #endif std::string m_coreclrPath; // the path of the coreclr module or empty if none #ifdef __APPLE__ @@ -112,7 +111,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, void VisitModule(uint64_t baseAddress, std::string& moduleName); void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr); bool EnumerateModuleMappings(); -#endif +#endif bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType); bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess); bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess); diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index d300d343d54a0..93a55394a5d8a 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -8,7 +8,6 @@ bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name); bool CrashInfo::Initialize() { -#ifndef HAVE_PROCESS_VM_READV char memPath[128]; _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem", m_pid); @@ -18,12 +17,13 @@ CrashInfo::Initialize() fprintf(stderr, "open(%s) FAILED %d (%s)\n", memPath, errno, strerror(errno)); return false; } -#endif // Get the process info if (!GetStatus(m_pid, &m_ppid, &m_tgid, &m_name)) { return false; } + + m_canUseProcVmReadSyscall = true; return true; } @@ -39,13 +39,11 @@ CrashInfo::CleanupAndResumeProcess() waitpid(thread->Tid(), &waitStatus, __WALL); } } -#ifndef HAVE_PROCESS_VM_READV if (m_fd != -1) { close(m_fd); m_fd = -1; } -#endif } // @@ -253,7 +251,7 @@ CrashInfo::GetDSOInfo() int phnum = m_auxvValues[AT_PHNUM]; assert(m_auxvValues[AT_PHENT] == sizeof(Phdr)); assert(phnum != PN_XNUM); - return EnumerateElfInfo(phdrAddr, phnum); + return EnumerateElfInfo(phdrAddr, phnum); } // @@ -334,17 +332,31 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r { assert(buffer != nullptr); assert(read != nullptr); + *read = 0; #ifdef HAVE_PROCESS_VM_READV - iovec local{ buffer, size }; - iovec remote{ address, size }; - *read = process_vm_readv(m_pid, &local, 1, &remote, 1, 0); -#else - assert(m_fd != -1); - *read = pread64(m_fd, buffer, size, (off64_t)address); + if (m_canUseProcVmReadSyscall) + { + iovec local{ buffer, size }; + iovec remote{ address, size }; + *read = process_vm_readv(m_pid, &local, 1, &remote, 1, 0); + } + + if (!m_canUseProcVmReadSyscall || (*read == (size_t)-1 && errno == EPERM)) #endif + { + // If we've failed, avoid going through expensive syscalls + // After all, the use of process_vm_readv is largely as a + // performance optimization. + m_canUseProcVmReadSyscall = false; + assert(m_fd != -1); + *read = pread64(m_fd, buffer, size, (off64_t)address); + } + if (*read == (size_t)-1) { + int readErrno = errno; + TRACE_VERBOSE("ReadProcessMemory FAILED, addr: %" PRIA PRIx ", size: %zu, ERRNO %d: %s\n", address, size, readErrno, strerror(readErrno)); return false; } return true; diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 424cff1b91481..71e35abec93e5 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7084,7 +7084,7 @@ HRESULT ClrDataAccess::VerifyDlls() // Note that we check this knob every time because it may be handy to turn it on in // the environment mid-flight. DWORD dwAssertDefault = m_fEnableDllVerificationAsserts ? 1 : 0; - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault)) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgDACAssertOnMismatch, dwAssertDefault)) { // Output a nice error message that contains the timestamps in string format. time_t actualTime = timestamp; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index cef46813c247e..076662042084e 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -4253,6 +4253,16 @@ HRESULT DacDbiInterfaceImpl::IsModuleMapped(VMPTR_Module pModule, OUT BOOL *isMo return hr; } +bool DacDbiInterfaceImpl::MetadataUpdatesApplied() +{ + DD_ENTER_MAY_THROW; +#ifdef EnC_SUPPORTED + return g_metadataUpdatesApplied; +#else + return false; +#endif +} + // Helper to intialize a TargetBuffer from a MemoryRange // // Arguments: @@ -4777,6 +4787,17 @@ VMPTR_OBJECTHANDLE DacDbiInterfaceImpl::GetThreadObject(VMPTR_Thread vmThread) } } +void DacDbiInterfaceImpl::GetThreadAllocInfo(VMPTR_Thread vmThread, + DacThreadAllocInfo* threadAllocInfo) +{ + DD_ENTER_MAY_THROW; + + Thread * pThread = vmThread.GetDacPtr(); + gc_alloc_context* allocContext = pThread->GetAllocContext(); + threadAllocInfo->m_allocBytesSOH = (ULONG)(allocContext->alloc_bytes - (allocContext->alloc_limit - allocContext->alloc_ptr)); + threadAllocInfo->m_allocBytesUOH = (ULONG)allocContext->alloc_bytes_uoh; +} + // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread // according to the CorDebugThreadState. void DacDbiInterfaceImpl::SetDebugState(VMPTR_Thread vmThread, diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index e7a3dc9b3a09c..3088ed007a717 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -365,6 +365,8 @@ class DacDbiInterfaceImpl : HRESULT IsModuleMapped(VMPTR_Module pModule, OUT BOOL *isModuleMapped); + bool MetadataUpdatesApplied(); + // retrieves the list of COM interfaces implemented by vmObject, as it is known at // the time of the call (the list may change as new interface types become available // in the runtime) @@ -781,6 +783,9 @@ class DacDbiInterfaceImpl : // Return the object handle for the managed Thread object corresponding to the specified thread. VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread); + // Get the alocated bytes for this thread. + void GetThreadAllocInfo(VMPTR_Thread vmThread, DacThreadAllocInfo* threadAllocInfo); + // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread // according to the CorDebugThreadState. void SetDebugState(VMPTR_Thread vmThread, diff --git a/src/coreclr/debug/daccess/reimpl.cpp b/src/coreclr/debug/daccess/reimpl.cpp index 63beae098e6e1..0c94f50e35522 100644 --- a/src/coreclr/debug/daccess/reimpl.cpp +++ b/src/coreclr/debug/daccess/reimpl.cpp @@ -63,7 +63,7 @@ DacGetThread(ULONG32 osThread) return NULL; } -EXTERN_C Thread* GetThread() +Thread* GetThread() { // In dac mode it's unlikely that the thread calling dac // is actually the same "current thread" that the runtime cares @@ -75,6 +75,11 @@ EXTERN_C Thread* GetThread() return NULL; } +Thread* GetThreadNULLOk() +{ + return GetThread(); +} + BOOL DacGetThreadContext(Thread* thread, T_CONTEXT* context) { diff --git a/src/coreclr/debug/debug-pal/CMakeLists.txt b/src/coreclr/debug/debug-pal/CMakeLists.txt index da51b34fdf6d8..d2846f93b13f9 100644 --- a/src/coreclr/debug/debug-pal/CMakeLists.txt +++ b/src/coreclr/debug/debug-pal/CMakeLists.txt @@ -16,7 +16,7 @@ if(CLR_CMAKE_HOST_WIN32) include_directories(../../inc) #needed for warning control if(CLR_CMAKE_TARGET_WIN32) - set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-win32.c") + set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-pal-namedpipe.c") set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES LANGUAGE CXX) set(TWO_WAY_PIPE_SOURCES @@ -36,7 +36,7 @@ if(CLR_CMAKE_HOST_UNIX) add_definitions(-DPAL_IMPLEMENTATION) add_definitions(-D_POSIX_C_SOURCE=200809L) - set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-posix.c") + set (EVENTPIPE_PAL_SOURCES "${SHARED_EVENTPIPE_DIR}/ds-ipc-pal-socket.c") set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES LANGUAGE CXX) if (CMAKE_VERSION VERSION_GREATER 3.11 OR CMAKE_VERSION VERSION_EQUAL 3.11) set_source_files_properties(${EVENTPIPE_PAL_SOURCES} PROPERTIES COMPILE_OPTIONS -xc++) @@ -52,6 +52,6 @@ if(CLR_CMAKE_HOST_UNIX) endif(CLR_CMAKE_HOST_UNIX) -_add_library(debug-pal_obj OBJECT ${TWO_WAY_PIPE_SOURCES}) +add_library_clr(debug-pal_obj OBJECT ${TWO_WAY_PIPE_SOURCES}) add_library(debug-pal INTERFACE) target_sources(debug-pal INTERFACE $) diff --git a/src/coreclr/debug/di/eventredirectionpipeline.cpp b/src/coreclr/debug/di/eventredirectionpipeline.cpp index 2332ec25e39cc..382319dc8fe88 100644 --- a/src/coreclr/debug/di/eventredirectionpipeline.cpp +++ b/src/coreclr/debug/di/eventredirectionpipeline.cpp @@ -58,10 +58,10 @@ void EventRedirectionPipeline::Delete() void EventRedirectionPipeline::InitConfiguration() { // We need some config strings. See header for possible values. - m_DebuggerCmd.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectApplication); - m_AttachParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectAttachCmd); - m_CreateParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCreateCmd); - m_CommonParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCommonCmd); + m_DebuggerCmd = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectApplication); + m_AttachParams = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectAttachCmd); + m_CreateParams = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectCreateCmd); + m_CommonParams = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DbgRedirectCommonCmd); } @@ -108,11 +108,11 @@ HRESULT EventRedirectionPipeline::AttachDebuggerToTarget(LPCWSTR szOptions, DWOR // Initialize m_pBlock->m_versionCookie = EVENT_REDIRECTION_CURRENT_VERSION; - s.Printf(m_CommonParams.Value(), GetCurrentProcessId(), m_pBlock, szOptions, pidTarget); + s.Printf(m_CommonParams, GetCurrentProcessId(), m_pBlock, szOptions, pidTarget); lpCommandLine = s.GetUnicode(); - lpApplicationName = m_DebuggerCmd.Value(); // eg, something like L"c:\\debuggers_amd64\\windbg.exe"; + lpApplicationName = m_DebuggerCmd; // eg, something like L"c:\\debuggers_amd64\\windbg.exe"; // Initialize events. const BOOL kManualResetEvent = TRUE; @@ -283,7 +283,7 @@ HRESULT EventRedirectionPipeline::CreateProcessUnderDebugger( } // Attach the real debugger. - AttachDebuggerToTarget(m_CreateParams.Value(), lpProcessInformation->dwProcessId); + AttachDebuggerToTarget(m_CreateParams, lpProcessInformation->dwProcessId); m_dwProcessId = lpProcessInformation->dwProcessId; @@ -298,7 +298,7 @@ HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, co // Use redirected pipeline // Spin up debugger to attach to target. - return AttachDebuggerToTarget(m_AttachParams.Value(), processDescriptor.m_Pid); + return AttachDebuggerToTarget(m_AttachParams, processDescriptor.m_Pid); } // Detach diff --git a/src/coreclr/debug/di/eventredirectionpipeline.h b/src/coreclr/debug/di/eventredirectionpipeline.h index e219c182980a0..a8a0aeed8067f 100644 --- a/src/coreclr/debug/di/eventredirectionpipeline.h +++ b/src/coreclr/debug/di/eventredirectionpipeline.h @@ -102,7 +102,7 @@ class EventRedirectionPipeline : // The debugger application to launch. eg: // c:\debuggers_amd64\windbg.exe - ConfigStringHolder m_DebuggerCmd; + CLRConfigStringHolder m_DebuggerCmd; // The common format string for the command line. // This will get the following printf args: @@ -113,7 +113,7 @@ class EventRedirectionPipeline : // target debuggee (%d or %x): pid of the debuggee. // eg (for windbg): // -c ".load C:\vbl\ClrDbg\ndp\clr\src\Tools\strikeRS\objc\amd64\strikeRS.dll; !watch %x %p" %s -p %d - ConfigStringHolder m_CommonParams; + CLRConfigStringHolder m_CommonParams; // Command parameters for create case. // Note that we must always physically call CreateProcess on the debuggee so that we get the proper out-parameters @@ -128,12 +128,12 @@ class EventRedirectionPipeline : // to not create the break-in thread (which it can't do on a pre-initialized process). // eg: // "-WX -pb -pr" - ConfigStringHolder m_CreateParams; + CLRConfigStringHolder m_CreateParams; // command parameters for attach. The WFDE server will send a loader breakpoint. // eg: // "-WX" - ConfigStringHolder m_AttachParams; + CLRConfigStringHolder m_AttachParams; DWORD m_dwProcessId; }; diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index badcd863d47b3..791871946a1b2 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -14019,6 +14019,13 @@ void CordbWin32EventThread::AttachProcess() EX_TRY { + // Don't allow attach if any metadata/IL updates have been applied + if (pProcess->GetDAC()->MetadataUpdatesApplied()) + { + hr = CORDBG_E_ASSEMBLY_UPDATES_APPLIED; + goto LExit; + } + // Mark interop-debugging if (m_actionData.attachData.IsInteropDebugging()) { diff --git a/src/coreclr/debug/di/rsmain.cpp b/src/coreclr/debug/di/rsmain.cpp index 779a6eac56d2d..ff49f2186a40c 100644 --- a/src/coreclr/debug/di/rsmain.cpp +++ b/src/coreclr/debug/di/rsmain.cpp @@ -480,15 +480,15 @@ void CordbCommonBase::InitializeCommon() // StressLog will turn on stress logging for the entire runtime. // RSStressLog is only used here and only effects just the RS. fStressLog = - (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0) || + (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0) || (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_RSStressLog) != 0); if (fStressLog == true) { - unsigned facilities = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LF_ALL); - unsigned level = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); - unsigned bytesPerThread = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 2); - unsigned totalBytes = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); + unsigned facilities = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LF_ALL); + unsigned level = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); + unsigned bytesPerThread = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 2); + unsigned totalBytes = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, GetClrModuleBase()); } } diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index b6870e5606656..04a7fa21a1db4 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -9790,6 +9790,7 @@ HRESULT CordbEval::NewParameterizedObject(ICorDebugFunction * pConstructor, if (FAILED(hr)) { + delete [] pArgData; return hr; } } diff --git a/src/coreclr/debug/ee/canary.cpp b/src/coreclr/debug/ee/canary.cpp index 6ae9eafe53045..ef833b1de1e6e 100644 --- a/src/coreclr/debug/ee/canary.cpp +++ b/src/coreclr/debug/ee/canary.cpp @@ -309,7 +309,7 @@ void HelperCanary::ThreadProc() //----------------------------------------------------------------------------- void HelperCanary::TakeLocks() { - _ASSERTE(::GetThread() == NULL); // Canary Thread should always be outside the runtime. + _ASSERTE(::GetThreadNULLOk() == NULL); // Canary Thread should always be outside the runtime. _ASSERTE(m_CanaryThreadId == GetCurrentThreadId()); // Call new, which will take whatever standard heap locks there are. diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index aa81d1b9f168f..7f7fb109b402c 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -3249,7 +3249,7 @@ void DebuggerController::UnapplyTraceFlag(Thread *thread) // Either this is the helper thread, or we're manipulating our own context. _ASSERTE( ThisIsHelperThreadWorker() || - (thread == ::GetThread()) + (thread == ::GetThreadNULLOk()) ); CONTEXT *context = GetManagedStoppedCtx(thread); @@ -3324,7 +3324,7 @@ BOOL DebuggerController::DispatchExceptionHook(Thread *thread, MODE_ANY; // Filter context not set yet b/c we can only set it in COOP, and this may be in preemptive. - PRECONDITION(thread == ::GetThread()); + PRECONDITION(thread == ::GetThreadNULLOk()); PRECONDITION((g_pEEInterface->GetThreadFilterContext(thread) == NULL)); PRECONDITION(CheckPointer(pException)); } @@ -4537,7 +4537,7 @@ void DebuggerPatchSkip::DebuggerDetachClean() // 2. Create a "stack walking" implementation for native code and use it to get the current IP and // set the IP to the right place. - Thread *thread = GetThread(); + Thread *thread = GetThreadNULLOk(); if (thread != NULL) { BYTE *patchBypass = m_pSharedPatchBypassBuffer->PatchBypass; @@ -4672,7 +4672,7 @@ TP_RESULT DebuggerPatchSkip::TriggerExceptionHook(Thread *thread, CONTEXT * cont // toggled the GC mode underneath us. MODE_ANY; - PRECONDITION(GetThread() == thread); + PRECONDITION(GetThreadNULLOk() == thread); PRECONDITION(thread != NULL); PRECONDITION(CheckPointer(context)); } @@ -6597,8 +6597,6 @@ void DebuggerStepper::StepOut(FramePointer fp, StackTraceTicket ticket) "\n", fp.GetSPValue(), this )); Thread *thread = GetThread(); - - CONTEXT *context = g_pEEInterface->GetThreadFilterContext(thread); ControllerStackInfo info; @@ -7711,7 +7709,7 @@ bool DebuggerStepper::SendEvent(Thread *thread, bool fIpChanged) LOG((LF_CORDB, LL_INFO10000, "DS::SE m_fpStepInto:0x%x\n", m_fpStepInto.GetSPValue())); _ASSERTE(m_fReadyToSend); - _ASSERTE(GetThread() == thread); + _ASSERTE(GetThreadNULLOk() == thread); CONTEXT *context = g_pEEInterface->GetThreadFilterContext(thread); _ASSERTE(!ISREDIRECTEDTHREAD(thread)); diff --git a/src/coreclr/debug/ee/dactable.cpp b/src/coreclr/debug/ee/dactable.cpp index a5490ac144a0f..40b01df769d41 100644 --- a/src/coreclr/debug/ee/dactable.cpp +++ b/src/coreclr/debug/ee/dactable.cpp @@ -20,10 +20,6 @@ #include "../../vm/common.h" #include "../../vm/gcenv.h" #include "../../vm/ecall.h" -// This header include will need to be rmeoved as part of GitHub#12170. -// The only reason it's here now is that this file references the GC-private -// variable g_HandleTableMap. -#include "../../gc/objecthandle.h" #ifdef DEBUGGING_SUPPORTED diff --git a/src/coreclr/debug/ee/debugger.cpp b/src/coreclr/debug/ee/debugger.cpp index 095f5910d02f3..d8791362ca720 100644 --- a/src/coreclr/debug/ee/debugger.cpp +++ b/src/coreclr/debug/ee/debugger.cpp @@ -366,10 +366,10 @@ void Debugger::DoNotCallDirectlyPrivateLock(void) // Thread * pThread; bool fIsCooperative; - BEGIN_GETTHREAD_ALLOWED; + pThread = g_pEEInterface->GetThread(); fIsCooperative = (pThread != NULL) && (pThread->PreemptiveGCDisabled()); - END_GETTHREAD_ALLOWED; + if (m_fShutdownMode && !fIsCooperative) { // The big fear is that some other random thread will take the debugger-lock and then block on something else, @@ -778,7 +778,7 @@ CONTEXT * GetManagedLiveCtx(Thread * pThread) // We're in some Controller's Filter after hitting an exception. // We're not stopped. //_ASSERTE(!g_pDebugger->IsStopped()); <-- @todo - this fires, need to find out why. - _ASSERTE(GetThread() == pThread); + _ASSERTE(GetThreadNULLOk() == pThread); CONTEXT *pCtx = g_pEEInterface->GetThreadFilterContext(pThread); @@ -1048,11 +1048,11 @@ void Debugger::InitDebugEventCounting() memset(&g_iDbgDebuggerCounter, 0, DBG_DEBUGGER_MAX*sizeof(int)); // retrieve the possible counter for break point - LPWSTR wstrValue = NULL; + CLRConfigStringHolder wstrValue = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebuggerBreakPoint); // The string value is of the following format // =Count;=Count;....; // The string must end with ; - if ((wstrValue = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebuggerBreakPoint)) != NULL) + if (wstrValue != NULL) { LPSTR strValue; int cbReq; @@ -1108,75 +1108,10 @@ void Debugger::InitDebugEventCounting() // free the ansi buffer delete [] strValue; - REGUTIL::FreeConfigString(wstrValue); } #endif // _DEBUG } - -// This is a notification from the EE it's about to go to fiber mode. -// This is given *before* it actually goes to fiber mode. -HRESULT Debugger::SetFiberMode(bool isFiberMode) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - - // Notifications from EE never come on helper worker. - PRECONDITION(!ThisIsHelperThreadWorker()); - } - CONTRACTL_END; - - - Thread * pThread = ::GetThread(); - - m_pRCThread->m_pDCB->m_bHostingInFiber = isFiberMode; - - // If there is a debugger already attached, then we have a big problem. As of V2.0, the debugger - // does not support debugging processes with fibers in them. We set the unrecoverable state to - // indicate that we're in a bad state now. The debugger will notice this, and take appropiate action. - if (isFiberMode && CORDebuggerAttached()) - { - LOG((LF_CORDB, LL_INFO10, "Thread has entered fiber mode while debugger attached.\n")); - - EX_TRY - { - // We send up a MDA for two reasons: 1) we want to give the user some chance to see what went wrong, - // and 2) we want to get the Right Side to notice that we're in an unrecoverable error state now. - - SString szName(W("DebuggerFiberModeNotSupported")); - SString szDescription; - szDescription.LoadResource(CCompRC::Debugging, MDARC_DEBUGGER_FIBER_MODE_NOT_SUPPORTED); - SString szXML(W("")); - - // Sending any debug event will be a GC violation. - // However, if we're enabling fiber-mode while a debugger is attached, we're already doomed. - // Deadlocks and AVs are just around the corner. A Gc-violation is the least of our worries. - // We want to at least notify the debugger at all costs. - CONTRACT_VIOLATION(GCViolation); - - // As soon as we set unrecoverable error in the LS, the RS will pick it up and basically shut down. - // It won't dispatch any events. So we fire the MDA first, and then set unrecoverable error. - SendMDANotification(pThread, &szName, &szDescription, &szXML, (CorDebugMDAFlags) 0, FALSE); - - CORDBDebuggerSetUnrecoverableError(this, CORDBG_E_CANNOT_DEBUG_FIBER_PROCESS, false); - - // Fire the MDA again just to force the RS to sniff the LS and pick up that we're in an unrecoverable error. - // No harm done from dispatching an MDA twice. And - SendMDANotification(pThread, &szName, &szDescription, &szXML, (CorDebugMDAFlags) 0, FALSE); - - } - EX_CATCH - { - LOG((LF_CORDB, LL_INFO10, "Error sending MDA regarding fiber mode.\n")); - } - EX_END_CATCH(SwallowAllExceptions); - } - - return S_OK; -} - // Checks if the MethodInfos table has been allocated, and if not does so. // Throw on failure, so we always return HRESULT Debugger::CheckInitMethodInfoTable() @@ -1777,7 +1712,7 @@ void Debugger::SendCreateProcess(DebuggerLockHolder * pDbgLockHolder) // This ensures the debuggee is actually stopped at startup, and // this gives the debugger a chance to call SetDesiredNGENFlags before we // set s_fCanChangeNgenFlags to FALSE. - _ASSERTE(GetThread() != NULL); + _ASSERTE(GetThreadNULLOk() != NULL); SENDIPCEVENT_RAW_END; pDbgLockHolder->Acquire(); @@ -6154,7 +6089,7 @@ void Debugger::SendRawUserBreakpoint(Thread * pThread) GC_NOTRIGGER; MODE_PREEMPTIVE; - PRECONDITION(pThread == GetThread()); + PRECONDITION(pThread == GetThreadNULLOk()); PRECONDITION(ThreadHoldsLock()); @@ -7884,7 +7819,7 @@ void Debugger::ProcessAnyPendingEvals(Thread *pThread) DebuggerEval *pDE = pfe->pDE; _ASSERTE(pDE->m_evalDuringException); - _ASSERTE(pDE->m_thread == GetThread()); + _ASSERTE(pDE->m_thread == GetThreadNULLOk()); // Remove the pending eval from the hash. This ensures that if we take a first chance exception during the eval // that we can do another nested eval properly. @@ -7936,7 +7871,7 @@ bool Debugger::FirstChanceManagedException(Thread *pThread, SIZE_T currentIP, SI LOG((LF_CORDB, LL_INFO10000, "D::FCE: First chance exception, TID:0x%x, \n", GetThreadIdHelper(pThread))); - _ASSERTE(GetThread() != NULL); + _ASSERTE(GetThreadNULLOk() != NULL); #ifdef _DEBUG static ConfigDWORD d_fce; @@ -7980,7 +7915,7 @@ void Debugger::FirstChanceManagedExceptionCatcherFound(Thread *pThread, // @@@ // Implements DebugInterface // Call by EE/exception. Must be on managed thread - _ASSERTE(GetThread() != NULL); + _ASSERTE(GetThreadNULLOk() != NULL); // Quick check. if (!CORDebuggerAttached()) @@ -8048,7 +7983,7 @@ LONG Debugger::NotifyOfCHFFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID p { CONTRACTL { - if ((GetThread() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread())) + if ((GetThreadNULLOk() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread())) { NOTHROW; GC_NOTRIGGER; @@ -8080,7 +8015,7 @@ LONG Debugger::NotifyOfCHFFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID p // useful information for the debugger and, in fact, it may be a completely // internally handled runtime exception, so we should do nothing. // - if ((GetThread() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread())) + if ((GetThreadNULLOk() == NULL) || g_pEEInterface->IsThreadExceptionNull(GetThread())) { return EXCEPTION_CONTINUE_SEARCH; } @@ -8958,7 +8893,7 @@ void Debugger::SendUserBreakpoint(Thread * thread) MODE_ANY; PRECONDITION(thread != NULL); - PRECONDITION(thread == ::GetThread()); + PRECONDITION(thread == ::GetThreadNULLOk()); } CONTRACTL_END; @@ -14815,7 +14750,7 @@ HRESULT Debugger::UpdateAppDomainEntryInIPC(AppDomain *pAppDomain) CONTRACTL { NOTHROW; - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; diff --git a/src/coreclr/debug/ee/debugger.h b/src/coreclr/debug/ee/debugger.h index b4086fe50fe6d..374f4ded1e65e 100644 --- a/src/coreclr/debug/ee/debugger.h +++ b/src/coreclr/debug/ee/debugger.h @@ -326,16 +326,6 @@ class GCHolderEEInterface #define GCX_PREEMP_EEINTERFACE_TOGGLE_IFTHREAD_COND(cond) \ GCHolderEEInterface __gcCoop_onlyOneAllowedPerScope((cond)) - - -// There are still some APIs that call new that we call from the helper thread. -// These are unsafe operations, so we wrap them here. Each of these is a potential hang. -inline DWORD UnsafeGetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue) -{ - SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; - return REGUTIL::GetConfigDWORD_DontUse_(name, defValue); -} - inline DWORD UnsafeGetConfigDWORD(const CLRConfig::ConfigDWORDInfo & info) { SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; @@ -2351,7 +2341,6 @@ class Debugger : public DebugInterface if (g_fProcessDetach) return true; - BEGIN_GETTHREAD_ALLOWED; if (g_pEEInterface->GetThread()) { return (GetThreadIdHelper(g_pEEInterface->GetThread()) == m_mutexOwner); @@ -2360,7 +2349,6 @@ class Debugger : public DebugInterface { return (GetCurrentThreadId() == m_mutexOwner); } - END_GETTHREAD_ALLOWED; } #endif // _DEBUG_IMPL @@ -2514,7 +2502,6 @@ class Debugger : public DebugInterface BOOL ShouldAutoAttach(); BOOL FallbackJITAttachPrompt(); - HRESULT SetFiberMode(bool isFiberMode); HRESULT AddAppDomainToIPC (AppDomain *pAppDomain); HRESULT RemoveAppDomainFromIPC (AppDomain *pAppDomain); @@ -3912,7 +3899,7 @@ HANDLE OpenWin32EventOrThrow( if ((m_pRCThread == NULL) || !m_pRCThread->IsRCThreadReady()) { THROWS; } else { NOTHROW; } #define MAY_DO_HELPER_THREAD_DUTY_GC_TRIGGERS_CONTRACT \ - if ((m_pRCThread == NULL) || !m_pRCThread->IsRCThreadReady() || (GetThread() != NULL)) { GC_TRIGGERS; } else { GC_NOTRIGGER; } + if ((m_pRCThread == NULL) || !m_pRCThread->IsRCThreadReady() || (GetThreadNULLOk() != NULL)) { GC_TRIGGERS; } else { GC_NOTRIGGER; } #define GC_TRIGGERS_FROM_GETJITINFO if (GetThreadNULLOk() != NULL) { GC_TRIGGERS; } else { GC_NOTRIGGER; } diff --git a/src/coreclr/debug/ee/frameinfo.cpp b/src/coreclr/debug/ee/frameinfo.cpp index 011c7a1839c7d..d05a0db9489ce 100644 --- a/src/coreclr/debug/ee/frameinfo.cpp +++ b/src/coreclr/debug/ee/frameinfo.cpp @@ -1957,7 +1957,7 @@ bool ShouldSendUMLeafChain(Thread * pThread) // If we're tracing ourselves, we must be in managed code. // Native user code can't initiate a managed stackwalk. - if (pThread == GetThread()) + if (pThread == GetThreadNULLOk()) { return false; } @@ -2008,7 +2008,7 @@ bool PrepareLeafUMChain(DebuggerFrameData * pData, CONTEXT * pCtxTemp) // We need to get thread's context (InitRegDisplay will do that under the covers). // If this is our thread, we're in bad shape. Fortunately that should never happen. - _ASSERTE(thread != GetThread()); + _ASSERTE(thread != GetThreadNULLOk()); Thread::SuspendThreadResult str = thread->SuspendThread(); if (str != Thread::STR_Success) diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index 1f99f5f296658..2dc0beca1c0ad 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1065,6 +1065,17 @@ class IDacDbiInterface virtual VMPTR_OBJECTHANDLE GetThreadObject(VMPTR_Thread vmThread) = 0; + + // + // Get the allocation info corresponding to the specified thread. + // + // Arguments: + // vmThread - the specified thread + // threadAllocInfo - the allocated bytes from SOH and UOH so far on this thread + // + + virtual + void GetThreadAllocInfo(VMPTR_Thread vmThread, DacThreadAllocInfo* threadAllocInfo) = 0; // // Set and reset the TSNC_DebuggerUserSuspend bit on the state of the specified thread @@ -2736,6 +2747,9 @@ class IDacDbiInterface virtual HRESULT IsModuleMapped(VMPTR_Module pModule, OUT BOOL *isModuleMapped) = 0; + virtual + bool MetadataUpdatesApplied() = 0; + // The following tag tells the DD-marshalling tool to stop scanning. // END_MARSHAL diff --git a/src/coreclr/debug/inc/dacdbistructures.h b/src/coreclr/debug/inc/dacdbistructures.h index b515cd7f1f380..2dabaa48e2c23 100644 --- a/src/coreclr/debug/inc/dacdbistructures.h +++ b/src/coreclr/debug/inc/dacdbistructures.h @@ -784,5 +784,12 @@ struct MSLAYOUT DacSharedReJitInfo CORDB_ADDRESS m_rgInstrumentedMapEntries; }; +// These represent the allocated bytes so far on the thread. +struct MSLAYOUT DacThreadAllocInfo +{ + ULONG m_allocBytesSOH; + ULONG m_allocBytesUOH; +}; + #include "dacdbistructures.inl" #endif // DACDBISTRUCTURES_H_ diff --git a/src/coreclr/debug/inc/dbgtransportsession.h b/src/coreclr/debug/inc/dbgtransportsession.h index f90f4db12ca73..5c305b1781874 100644 --- a/src/coreclr/debug/inc/dbgtransportsession.h +++ b/src/coreclr/debug/inc/dbgtransportsession.h @@ -145,8 +145,8 @@ inline void DbgTransportLog(DbgTransportLogClass eClass, const char *szFormat, . if (s_dwLoggingEnabled == LE_Unknown) { - s_dwLoggingEnabled = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLog, LE_None); - s_dwLoggingClass = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_DbgTransportLogClass, LC_All); + s_dwLoggingEnabled = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTransportLog); + s_dwLoggingClass = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgTransportLogClass); } if ((s_dwLoggingEnabled & DBG_TRANSPORT_LOG_THIS_SIDE) && diff --git a/src/coreclr/debug/runtimeinfo/CMakeLists.txt b/src/coreclr/debug/runtimeinfo/CMakeLists.txt index a7811d6ce2c2e..ea59ecea8a317 100644 --- a/src/coreclr/debug/runtimeinfo/CMakeLists.txt +++ b/src/coreclr/debug/runtimeinfo/CMakeLists.txt @@ -6,12 +6,35 @@ set(RUNTIMEINFO_SOURCES add_library_clr(runtimeinfo STATIC ${RUNTIMEINFO_SOURCES}) -add_dependencies(runtimeinfo coreclr_module_index_header) +function(generate_module_index Target ModuleIndexFile) + if(CLR_CMAKE_HOST_WIN32) + set(scriptExt ".cmd") + else() + set(scriptExt ".sh") + endif() -if (NOT (CLR_CMAKE_TARGET_WIN32 AND (CLR_CMAKE_TARGET_ARCH_I386 OR CLR_CMAKE_TARGET_ARCH_ARM) AND CLR_CMAKE_HOST_ARCH_AMD64)) - add_dependencies(runtimeinfo mscordaccore_module_index_header) - add_dependencies(runtimeinfo mscordbi_module_index_header) + add_custom_command( + OUTPUT ${ModuleIndexFile} + COMMAND ${CLR_ENG_NATIVE_DIR}/genmoduleindex${scriptExt} $ ${ModuleIndexFile} + DEPENDS ${Target} + COMMENT "Generating ${Target} module index file -> ${ModuleIndexFile}" + ) + + set_source_files_properties( + ${ModuleIndexFile} + PROPERTIES GENERATED TRUE + ) + + add_dependencies(runtimeinfo ${Target}) + target_sources(runtimeinfo PRIVATE ${ModuleIndexFile}) +endfunction(generate_module_index) + +generate_module_index(coreclr ${CMAKE_CURRENT_BINARY_DIR}/runtimemoduleindex.h) +if(NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) + add_dependencies(runtimeinfo inject_debug_resources_coreclr) + generate_module_index(mscordaccore ${CMAKE_CURRENT_BINARY_DIR}/dacmoduleindex.h) + generate_module_index(mscordbi ${CMAKE_CURRENT_BINARY_DIR}/dbimoduleindex.h) endif() # publish runtimeinfo lib -_install(TARGETS runtimeinfo DESTINATION lib) +install_clr(TARGETS runtimeinfo DESTINATIONS lib COMPONENT runtime) diff --git a/src/coreclr/dlls/clretwrc/CMakeLists.txt b/src/coreclr/dlls/clretwrc/CMakeLists.txt index d2cb5cfae98e8..cc427efcde488 100644 --- a/src/coreclr/dlls/clretwrc/CMakeLists.txt +++ b/src/coreclr/dlls/clretwrc/CMakeLists.txt @@ -20,6 +20,6 @@ add_library_clr(clretwrc SHARED ) # add the install targets -install_clr(TARGETS clretwrc ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS clretwrc DESTINATIONS . sharedFramework COMPONENT runtime) add_dependencies(clretwrc eventing_headers) diff --git a/src/coreclr/dlls/dbgshim/CMakeLists.txt b/src/coreclr/dlls/dbgshim/CMakeLists.txt index 1092799c866c6..5f323b243399c 100644 --- a/src/coreclr/dlls/dbgshim/CMakeLists.txt +++ b/src/coreclr/dlls/dbgshim/CMakeLists.txt @@ -84,4 +84,4 @@ endif(CLR_CMAKE_HOST_WIN32) target_link_libraries(dbgshim ${DBGSHIM_LIBRARIES}) # add the install targets -install_clr(TARGETS dbgshim ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS dbgshim DESTINATIONS . sharedFramework COMPONENT runtime) diff --git a/src/coreclr/dlls/mscordac/CMakeLists.txt b/src/coreclr/dlls/mscordac/CMakeLists.txt index 91e4d66c9244e..327560a030db2 100644 --- a/src/coreclr/dlls/mscordac/CMakeLists.txt +++ b/src/coreclr/dlls/mscordac/CMakeLists.txt @@ -98,7 +98,7 @@ endif(CLR_CMAKE_HOST_WIN32) # Create object library to enable creation of proper dependency of mscordaccore.exp on mscordac.obj and # mscordaccore on both the mscordaccore.exp and mscordac.obj. -_add_library(mscordacobj OBJECT mscordac.cpp) +add_library_clr(mscordacobj OBJECT mscordac.cpp) add_library_clr(mscordaccore SHARED ${CLR_DAC_SOURCES} $) @@ -192,14 +192,8 @@ endif(CLR_CMAKE_HOST_UNIX) target_link_libraries(mscordaccore PRIVATE ${COREDAC_LIBRARIES}) -# Create the DAC module index header file containing the DAC build id -# for xplat and the timestamp/size on Windows. -if(FEATURE_SINGLE_FILE_DIAGNOSTICS) - generate_module_index(mscordaccore ${GENERATED_INCLUDE_DIR}/dacmoduleindex.h) -endif(FEATURE_SINGLE_FILE_DIAGNOSTICS) - # add the install targets -install_clr(TARGETS mscordaccore ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS mscordaccore DESTINATIONS . sharedFramework COMPONENT runtime) if(CLR_CMAKE_HOST_WIN32) set(LONG_NAME_HOST_ARCH ${CLR_CMAKE_HOST_ARCH}) @@ -216,5 +210,5 @@ if(CLR_CMAKE_HOST_WIN32) string(REGEX MATCH "#define VER_FILEVERSION[ \t]+[0-9]+(,[0-9]+)+" FILE_VERSION_LINE "${NATIVE_VERSION_HEADER}") string(REGEX MATCHALL "[0-9]+" FILE_VERSION_COMPONENTS "${FILE_VERSION_LINE}") list(JOIN FILE_VERSION_COMPONENTS "." FILE_VERSION) - install(FILES $ RENAME mscordaccore_${LONG_NAME_HOST_ARCH}_${LONG_NAME_TARGET_ARCH}_${FILE_VERSION}.dll DESTINATION sharedFramework) + install(FILES $ RENAME mscordaccore_${LONG_NAME_HOST_ARCH}_${LONG_NAME_TARGET_ARCH}_${FILE_VERSION}.dll DESTINATION sharedFramework COMPONENT runtime) endif() diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 911ba406042a8..eeba3c07e31d9 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -107,6 +107,7 @@ nativeStringResourceTable_mscorrc #FlushFileBuffers #FlushInstructionCache #FormatMessageW +#FreeEnvironmentStringsW #FreeLibrary #FileTimeToSystemTime #GetACP @@ -114,6 +115,7 @@ nativeStringResourceTable_mscorrc #GetCurrentProcess #GetCurrentProcessId #GetCurrentThreadId +#GetEnvironmentStringsW #GetEnvironmentVariableA #GetEnvironmentVariableW #GetFileAttributesExW diff --git a/src/coreclr/dlls/mscordbi/CMakeLists.txt b/src/coreclr/dlls/mscordbi/CMakeLists.txt index a0fae4948469f..cc4b5664e65ae 100644 --- a/src/coreclr/dlls/mscordbi/CMakeLists.txt +++ b/src/coreclr/dlls/mscordbi/CMakeLists.txt @@ -119,11 +119,5 @@ elseif(CLR_CMAKE_HOST_UNIX) endif(CLR_CMAKE_HOST_WIN32) -# Create the DBI module index header file containing the DBI build id -# for xplat and the timestamp/size on Windows. -if(FEATURE_SINGLE_FILE_DIAGNOSTICS) - generate_module_index(mscordbi ${GENERATED_INCLUDE_DIR}/dbimoduleindex.h) -endif(FEATURE_SINGLE_FILE_DIAGNOSTICS) - # add the install targets -install_clr(TARGETS mscordbi ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS mscordbi DESTINATIONS . sharedFramework COMPONENT runtime) diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index b22ccf1a6bc58..2df7852c304f9 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -173,12 +173,6 @@ target_sources(coreclr_static PUBLIC $) target_link_libraries(coreclr_static PUBLIC ${CORECLR_LIBRARIES} clrjit_static cee_wks_mergeable) target_compile_definitions(coreclr_static PUBLIC CORECLR_EMBEDDED) -# Create the runtime module index header file containing the coreclr build id -# for xplat and the timestamp/size on Windows. -if(FEATURE_SINGLE_FILE_DIAGNOSTICS) - generate_module_index(coreclr ${GENERATED_INCLUDE_DIR}/runtimemoduleindex.h) -endif(FEATURE_SINGLE_FILE_DIAGNOSTICS) - if(CLR_CMAKE_TARGET_WIN32) # Add dac table & debug resource to coreclr get_include_directories(INC_DIR) @@ -204,37 +198,55 @@ if(CLR_CMAKE_TARGET_WIN32) endif() add_custom_command( - DEPENDS coreclr singlefilehost mscordaccore mscordbi ${CLR_DIR}/debug/daccess/daccess.cpp - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources.timestamp + DEPENDS coreclr mscordaccore mscordbi ${CLR_DIR}/debug/daccess/daccess.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources_coreclr.timestamp COMMAND ${CMAKE_CXX_COMPILER} /P /EP /TP ${PREPROCESS_DEFINITIONS} ${INC_DIR} /Fi${CMAKE_CURRENT_BINARY_DIR}/daccess.i ${CLR_DIR}/debug/daccess/daccess.cpp # make and inject dactable for coreclr COMMAND cmd /c ${CLR_REPO_ROOT_DIR}/dotnet.cmd exec ${CMAKE_INSTALL_PREFIX}/DacTableGen/DacTableGen.dll /dac:${CMAKE_CURRENT_BINARY_DIR}/daccess.i /pdb:$ /dll:$ /bin:${CMAKE_CURRENT_BINARY_DIR}/wks.bin COMMAND InjectResource /bin:${CMAKE_CURRENT_BINARY_DIR}/wks.bin /dll:$ + # make CLRDEBUGINFO resource and inject into coreclr + COMMAND GenClrDebugResource /dac:$ /dbi:$ /sku:onecoreclr /out:${CMAKE_CURRENT_BINARY_DIR}/clrDebugResource.bin + COMMAND InjectResource /bin:${CMAKE_CURRENT_BINARY_DIR}/clrDebugResource.bin /dll:$ /name:CLRDEBUGINFO + + # inject MINIDUMP_AUXILIARY_PROVIDER into coreclr + COMMAND InjectResource /bin:${CMAKE_CURRENT_SOURCE_DIR}/dump_helper_resource.bin /dll:$ /name:MINIDUMP_AUXILIARY_PROVIDER + + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources_coreclr.timestamp + COMMENT Add dactable, debug resources, and dump helper resources to coreclr + ) + + if(NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) + add_custom_target(inject_debug_resources_coreclr ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources_coreclr.timestamp) + add_dependencies(runtime inject_debug_resources_coreclr) + endif() + + add_custom_command( + DEPENDS singlefilehost mscordaccore mscordbi ${CLR_DIR}/debug/daccess/daccess.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources_singlefilehost.timestamp + COMMAND ${CMAKE_CXX_COMPILER} /P /EP /TP ${PREPROCESS_DEFINITIONS} ${INC_DIR} /Fi${CMAKE_CURRENT_BINARY_DIR}/daccess.i ${CLR_DIR}/debug/daccess/daccess.cpp + # make and inject dactable for singlefilehost COMMAND cmd /c ${CLR_REPO_ROOT_DIR}/dotnet.cmd exec ${CMAKE_INSTALL_PREFIX}/DacTableGen/DacTableGen.dll /dac:${CMAKE_CURRENT_BINARY_DIR}/daccess.i /pdb:$ /dll:$ /bin:${CMAKE_CURRENT_BINARY_DIR}/sfh.bin COMMAND InjectResource /bin:${CMAKE_CURRENT_BINARY_DIR}/sfh.bin /dll:$ - # make CLRDEBUGINFO resource and inject into coreclr and singlefilehost + # make CLRDEBUGINFO resource and inject into singlefilehost COMMAND GenClrDebugResource /dac:$ /dbi:$ /sku:onecoreclr /out:${CMAKE_CURRENT_BINARY_DIR}/clrDebugResource.bin - COMMAND InjectResource /bin:${CMAKE_CURRENT_BINARY_DIR}/clrDebugResource.bin /dll:$ /name:CLRDEBUGINFO COMMAND InjectResource /bin:${CMAKE_CURRENT_BINARY_DIR}/clrDebugResource.bin /dll:$ /name:CLRDEBUGINFO - # inject MINIDUMP_AUXILIARY_PROVIDER into coreclr - COMMAND InjectResource /bin:${CMAKE_CURRENT_SOURCE_DIR}/dump_helper_resource.bin /dll:$ /name:MINIDUMP_AUXILIARY_PROVIDER - - COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources.timestamp - COMMENT Add dactable, debug resources, and dump helper resources to coreclr and singlefilehost + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources_singlefilehost.timestamp + COMMENT Add dactable, debug resources, and dump helper resources to singlefilehost ) if(NOT DEFINED CLR_CROSS_COMPONENTS_BUILD) - add_custom_target(inject_debug_resources ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources.timestamp) + add_custom_target(inject_debug_resources_singlefilehost ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/inject_debug_resources_singlefilehost.timestamp) + add_dependencies(runtime inject_debug_resources_singlefilehost) endif() endif(CLR_CMAKE_TARGET_WIN32) # add the install targets -install_clr(TARGETS coreclr ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS coreclr DESTINATIONS . sharedFramework COMPONENT runtime) # Enable profile guided optimization add_pgo(coreclr) diff --git a/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp b/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp index bce1fe53be6b1..6a0ba5a249947 100644 --- a/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp +++ b/src/coreclr/dlls/mscorpe/ceefilegenwriter.cpp @@ -174,51 +174,6 @@ const BYTE DllMainIA64Template[] = #define DllMainIA64TemplateSize sizeof(DllMainIA64Template) -#ifdef EMIT_FIXUPS - -// Emitted PEFIXUP structure looks like this -struct PEFIXUP -{ - WORD wType; - WORD wSpare; - DWORD rva; - DWORD rvaTarget; -}; - -// Following structure is used to store the reloc information which -// will be used at UpdateFixups time to get the final data from the section -// bytes to update the fixup information. -// -struct DBG_FIXUP -{ - WORD wType; - WORD wSpare; - - union - { - DWORD rva; - unsigned offset; - }; - - union - { - DWORD rvaTarget; - CeeSection * sectionSource; - }; -}; - -enum -{ - IMAGE_REL_I386_DIR24NB = 0x0081, // 24-bit base relative - IMAGE_REL_I386_FILEPOS = 0x0082, // 32-bit file relative - // all other relocation types are - // in winnt.h, for some reason - // this one is missing - IMAGE_REL_I386_DIR30NB = 0x0083, // 30-bit base relative -}; - -#endif // EMIT_FIXUPS - // Get the Symbol entry given the head and a 0-based index inline IMAGE_SYMBOL* GetSymbolEntry(IMAGE_SYMBOL* pHead, SIZE_T idx) { @@ -293,13 +248,6 @@ HRESULT CeeFileGenWriter::CreateNewInstanceEx(CCeeGen *pCeeFileGenFrom, hr = pPrivateGenWriter->allocateCorHeader(); // get COR header near front IfFailGo(hr); -#if 0 // Need to add this if we want to propagate the old COM+ header - if (seedFileName) - { - memcpy(m_corHeader, baseFileDecoder->ntHeaders32()->corHeader, sizeof(IMAGE_COR20_HEADER)); - } -#endif - //If we were passed a CCeeGen at the beginning, copy it's data now. if (pCeeFileGenFrom) { pCeeFileGenFrom->cloneInstance((CCeeGen*)pPrivateGenWriter); @@ -313,13 +261,6 @@ HRESULT CeeFileGenWriter::CreateNewInstanceEx(CCeeGen *pCeeFileGenFrom, // set il RVA to be after the preallocated sections pPEWriter->setIlRva(preallocatedOffset); -#ifdef EMIT_FIXUPS - if (createFlags & ICEE_CREATE_FILE_EMIT_FIXUPS) - { - pPrivateGenWriter->setEmitFixups(); - } -#endif - pPEWriter.SuppressRelease(); pPrivateGenWriter.SuppressRelease(); pGenWriter = pPrivateGenWriter; @@ -333,18 +274,12 @@ CeeFileGenWriter::CeeFileGenWriter() // ctor is protected m_outputFileName = NULL; m_resourceFileName = NULL; m_dllSwitch = false; - m_objSwitch = false; - m_libraryName = NULL; - m_libraryGuid = GUID_NULL; m_entryPoint = 0; m_comImageFlags = COMIMAGE_FLAGS_ILONLY; // ceegen PEs don't have native code m_iatOffset = 0; m_dllCount = 0; - m_dwMacroDefinitionSize = 0; - m_dwMacroDefinitionRVA = NULL; - m_dwManifestSize = 0; m_dwManifestRVA = NULL; @@ -359,15 +294,6 @@ CeeFileGenWriter::CeeFileGenWriter() // ctor is protected m_linked = false; m_fixed = false; -#ifdef EMIT_FIXUPS - - m_fEmitFixups = false; - m_fFixupsUpdated = false; - m_sectionFixups = NULL; - m_pDebugDir = NULL; - -#endif - } // CeeFileGenWriter::CeeFileGenWriter() //***************************************************************************** @@ -393,36 +319,12 @@ HRESULT CeeFileGenWriter::Cleanup() // virtual return CCeeGen::Cleanup(); } // HRESULT CeeFileGenWriter::Cleanup() -HRESULT CeeFileGenWriter::EmitMacroDefinitions(void *pData, DWORD cData) -{ - // OBSOLETE - m_dwMacroDefinitionSize = 0; - - return S_OK; -} // HRESULT CeeFileGenWriter::EmitMacroDefinitions() - HRESULT CeeFileGenWriter::link() { HRESULT hr = checkForErrors(); if (! SUCCEEDED(hr)) return hr; -#ifdef EMIT_FIXUPS - - // The fixups describe each relocation. Each fixup contains the relocation's - // type, source RVA, and target RVA. Since the reloc target can be filled - // in after the relocation creation, the fixup target RVA discovery needs to - // be deferred. - // At this point all bytes should be filled in, ensuring that the final - // target information is available. - // UpdateFixups is called at this point to discover the final relocation target info. - // - hr = UpdateFixups(); - if (! SUCCEEDED(hr)) - return hr; - -#endif - // Don't set this if SetManifestEntry was not called - zapper sets the // resource directory explicitly if (m_dwManifestSize != 0) @@ -457,8 +359,6 @@ HRESULT CeeFileGenWriter::link() m_corHeader->MinorRuntimeVersion = VAL16(COR_VERSION_MINOR); if (m_dllSwitch) getPEWriter().setCharacteristics(IMAGE_FILE_DLL); - if (m_objSwitch) - getPEWriter().clearCharacteristics(IMAGE_FILE_DLL | IMAGE_FILE_EXECUTABLE_IMAGE); m_corHeader->Flags = VAL32(m_comImageFlags); IMAGE_COR20_HEADER_FIELD(*m_corHeader, EntryPointToken) = VAL32(m_entryPoint); _ASSERTE(TypeFromToken(m_entryPoint) == mdtMethodDef || m_entryPoint == mdTokenNil || @@ -466,7 +366,7 @@ HRESULT CeeFileGenWriter::link() setDirectoryEntry(getCorHeaderSection(), IMAGE_DIRECTORY_ENTRY_COMHEADER, sizeof(IMAGE_COR20_HEADER), m_corHeaderOffset); if ((m_comImageFlags & COMIMAGE_FLAGS_IL_LIBRARY) == 0 - && !m_linked && !m_objSwitch) + && !m_linked) { hr = emitExeMain(); if (FAILED(hr)) @@ -508,7 +408,7 @@ HRESULT CeeFileGenWriter::fixup() } // remap the entry point if entry point token has been moved - if (pMapper != NULL && !m_objSwitch) + if (pMapper != NULL) { mdToken tk = m_entryPoint; pMapper->HasTokenMoved(tk, tk); @@ -558,10 +458,8 @@ HRESULT CeeFileGenWriter::generateImage(void **ppImage) outputFileName = W("output.ill"); else if (m_dllSwitch) outputFileName = W("output.dll"); - else if (m_objSwitch) - outputFileName = W("output.exe"); else - outputFileName = W("output.obj"); + outputFileName = W("output.exe"); } // output file name and ppImage are mutually exclusive @@ -611,50 +509,6 @@ HRESULT CeeFileGenWriter::setResourceFileName(__in LPWSTR fileName) return S_OK; } // HRESULT CeeFileGenWriter::setResourceFileName() -HRESULT CeeFileGenWriter::setLibraryName(__in LPWSTR libraryName) -{ - if (m_libraryName) - delete[] m_libraryName; - size_t len = wcslen(libraryName) + 1; - m_libraryName = (LPWSTR)new (nothrow) WCHAR[len]; - TESTANDRETURN(m_libraryName != NULL, E_OUTOFMEMORY); - wcscpy_s(m_libraryName, len, libraryName); - return S_OK; -} // HRESULT CeeFileGenWriter::setLibraryName() - -HRESULT CeeFileGenWriter::setLibraryGuid(__in LPWSTR libraryGuid) -{ - return IIDFromString(libraryGuid, &m_libraryGuid); -} // HRESULT CeeFileGenWriter::setLibraryGuid() - -HRESULT CeeFileGenWriter::emitLibraryName(IMetaDataEmit *emitter) -{ - HRESULT hr; - IfFailRet(emitter->SetModuleProps(m_libraryName)); - - // Set the GUID as a custom attribute, if it is not NULL_GUID. - if (m_libraryGuid != GUID_NULL) - { - static COR_SIGNATURE _SIG[] = INTEROP_GUID_SIG; - mdTypeRef tr; - mdMemberRef mr; - WCHAR wzGuid[40]; - BYTE rgCA[50]; - IfFailRet(emitter->DefineTypeRefByName(mdTypeRefNil, INTEROP_GUID_TYPE_W, &tr)); - IfFailRet(emitter->DefineMemberRef(tr, W(".ctor"), _SIG, sizeof(_SIG), &mr)); - StringFromGUID2(m_libraryGuid, wzGuid, lengthof(wzGuid)); - memset(rgCA, 0, sizeof(rgCA)); - // Tag is 0x0001 - rgCA[0] = 1; - // Length of GUID string is 36 characters. - rgCA[2] = 0x24; - // Convert 36 characters, skipping opening {, into 3rd byte of buffer. - WszWideCharToMultiByte(CP_ACP,0, wzGuid+1,36, reinterpret_cast(&rgCA[3]),36, 0,0); - hr = emitter->DefineCustomAttribute(1,mr,rgCA,41,0); - } - return (hr); -} // HRESULT CeeFileGenWriter::emitLibraryName() - HRESULT CeeFileGenWriter::setImageBase(size_t imageBase) { _ASSERTE(getPEWriter().isPE32()); @@ -1440,13 +1294,6 @@ HRESULT CeeFileGenWriter::setVTableEntry(ULONG size, ULONG offset) return setVTableEntry64(size,(void*)(ULONG_PTR)offset); } // HRESULT CeeFileGenWriter::setVTableEntry() -HRESULT CeeFileGenWriter::setEnCRvaBase(ULONG dataBase, ULONG rdataBase) -{ - setEnCMode(); - getPEWriter().setEnCRvaBase(dataBase, rdataBase); - return S_OK; -} // HRESULT CeeFileGenWriter::setEnCRvaBase() - HRESULT CeeFileGenWriter::computeSectionOffset(CeeSection §ion, __in char *ptr, unsigned *offset) { @@ -1482,444 +1329,3 @@ HRESULT CeeFileGenWriter::getCorHeader(IMAGE_COR20_HEADER **ppHeader) *ppHeader = m_corHeader; return S_OK; } // HRESULT CeeFileGenWriter::getCorHeader() - - -#ifdef EMIT_FIXUPS - -HRESULT CeeFileGenWriter::InitFixupSection() -{ - if (!m_fEmitFixups) - { - return(E_UNEXPECTED); - } - - HRESULT hr; - - hr = getSectionCreate(".fixups", - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, - &m_sectionFixups); - if (SUCCEEDED(hr)) - { - size_t cbDebugDir = sizeof(IMAGE_DEBUG_DIRECTORY); - hr = GetSectionBlock(m_sectionFixups, (ULONG) cbDebugDir, 32, (void **) &m_pDebugDir); - if (SUCCEEDED(hr)) - { - memset(m_pDebugDir, 0, cbDebugDir); - m_pDebugDir->Type = IMAGE_DEBUG_TYPE_FIXUP; - m_fFixupsUpdated = false; - - return(S_OK); - } - } - - m_pDebugDir = NULL; - m_sectionFixups = NULL; - m_fEmitFixups = false; - - return(E_FAIL); - -} // HRESULT CeeFileGenWriter::InitFixupSection() - -HRESULT CeeFileGenWriter::addFixup(CeeSection& sectionSource, unsigned offset, CeeSectionRelocType relocType, CeeSection * psectionTarget, CeeSectionRelocExtra *extra) -{ - if (!m_fEmitFixups) - { - return(S_OK); - } - - _ASSERTE(sizeof(DBG_FIXUP) == sizeof(PEFIXUP)); - _ASSERTE(m_fFixupsUpdated == false); - - DBG_FIXUP * pfixup; - - if (m_sectionFixups == NULL) - { - HRESULT hr = InitFixupSection(); - if (FAILED(hr)) - { - return(hr); - } - - // The fixup section begins with a IMAGE_DEBUG_DIRECTORY containing a - // IMAGE_DEBUG_TYPE_FIXUP directory entry, which describes the array - // of fixups which follows it. - - // The very first item of this array is aligned on a 32 bit boundary. - // All other fixup entries follow unaligned. - pfixup = (DBG_FIXUP *) m_sectionFixups->getBlock(sizeof(DBG_FIXUP), 32); - TESTANDRETURN(pfixup != NULL, E_OUTOFMEMORY); - - // Initialize the IMAGE_DEBUG_TYPE_FIXUP entry relocations -#ifdef HOST_64BIT - _ASSERTE(!"Base relocs are not yet implemented for 64-bit"); - m_pDebugDir->AddressOfRawData = 0; // @ToDo: srRelocAbsolutePtr can't take a 64-bit address -#else - m_pDebugDir->AddressOfRawData = (size_t) pfixup; - m_sectionFixups->addSectReloc(offsetof(IMAGE_DEBUG_DIRECTORY, AddressOfRawData), *m_sectionFixups, srRelocAbsolutePtr); -#endif - - m_pDebugDir->PointerToRawData = m_sectionFixups->computeOffset((char *) pfixup); - - m_sectionFixups->addSectReloc(offsetof(IMAGE_DEBUG_DIRECTORY, PointerToRawData), *m_sectionFixups, srRelocFilePos); - - unsigned offsetDir = m_sectionFixups->computeOffset((char *) m_pDebugDir); - setDirectoryEntry(*m_sectionFixups, IMAGE_DIRECTORY_ENTRY_DEBUG, sizeof(IMAGE_DEBUG_DIRECTORY), offsetDir); - -#ifdef TEST_EMIT_FIXUPS - TestEmitFixups(); -#endif - } - else - { - pfixup = (DBG_FIXUP *) m_sectionFixups->getBlock(sizeof(DBG_FIXUP), 1); - TESTANDRETURN(pfixup != NULL, E_OUTOFMEMORY); - } - - // Save off the relocation information for use later. The relocation's - // target information can be filled in later. - // The relocation target info is not always immediately available, so it needs - // to be extracted later, during the link phase. For now the relocation info - // is stored so the target can be extracted at link time in the UpdateFixups - // function. - // - unsigned offsetFixup = m_sectionFixups->computeOffset((char *) pfixup); - pfixup->wSpare = 0; - pfixup->wType = relocType; - _ASSERTE(pfixup->wType == relocType); - pfixup->offset = offset; - pfixup->sectionSource = §ionSource; - - m_pDebugDir->SizeOfData += sizeof(DBG_FIXUP); - - // Add a relocation for the fixup's source RVA field, (no fixup on this reloc) - m_sectionFixups->addSectReloc(offsetFixup + offsetof(DBG_FIXUP, rva), sectionSource, srRelocAbsolutePtr); - - // Add a relocation for the fixup's target RVA field. Correct target extracted - // later in UpdateFixups, (no fixup on this reloc) - CeeSectionRelocType tgtRelocType; - - switch (relocType) - { - case srRelocMapToken: - // not an RVA - tgtRelocType = srRelocMapToken; - break; - - case srRelocFilePos: - tgtRelocType = srRelocFilePos; - break; - - case srRelocHighAdj: - tgtRelocType = srRelocHighAdj; - break; - - default: - tgtRelocType = (relocType & srRelocPtr) ? srRelocAbsolutePtr : srRelocAbsolute; - break; - } - - if (psectionTarget != NULL) - { - m_sectionFixups->addSectReloc(offsetFixup + offsetof(DBG_FIXUP, rvaTarget), *psectionTarget, tgtRelocType, extra); - } - else - { - m_sectionFixups->addBaseReloc(offsetFixup + offsetof(DBG_FIXUP, rvaTarget), tgtRelocType, extra); - } - - return(S_OK); -} // HRESULT CeeFileGenWriter::addFixup() - -HRESULT CeeFileGenWriter::UpdateFixups() -{ - // This method extracts the correct relocation target. See addFixup method. - - if (!m_fEmitFixups || m_fFixupsUpdated) - { - return(S_OK); - } - m_fFixupsUpdated = true; // prevent UpdateFixups from being called again. - - size_t cfixups = m_pDebugDir->SizeOfData / sizeof(DBG_FIXUP); - _ASSERT(m_pDebugDir->SizeOfData % sizeof(DBG_FIXUP) == 0); - unsigned ibFixup = m_pDebugDir->PointerToRawData; - - for (size_t idx = 0; idx < cfixups; idx++, ibFixup += sizeof(DBG_FIXUP)) - { - DBG_FIXUP * pfixup = (DBG_FIXUP *) m_sectionFixups->computePointer(ibFixup); - CeeSection * sectionSource = pfixup->sectionSource; - CeeSectionRelocType relocType = (CeeSectionRelocType) pfixup->wType; - unsigned offset = pfixup->offset; - - // Get current data for replacing fixup contents - const DWORD * pdw = (DWORD *) sectionSource->computePointer(offset); - pfixup->rva = (DWORD) (UINT_PTR) pdw; - pfixup->rvaTarget = *pdw; - - switch (relocType) - { -#ifdef HOST_X86 - case srRelocAbsolute: - // Emitted bytes: RVA, offset relative to image base - // reloc src contains target offset relative to target section - if ((*pdw & 0xFF000000) == 0) - { - pfixup->wType = IMAGE_REL_I386_DIR32NB; - } - else - { - // MethodDesc::Fixup function creates a 24 bit RVA, where the - // high byte of the DWORD stores the flag value: METHOD_NEEDS_PRESTUB_RUN_FLAG. - // work around it by converting the type to 24 bits here - pfixup->wType = IMAGE_REL_I386_DIR24NB; - pfixup->rvaTarget = *pdw & 0x00FFFFFF; - } - break; - - case srRelocAbsolutePtr: - // Emitted bytes: RVA, offset relative to image base - // reloc src contains target pointer - pfixup->wType = IMAGE_REL_I386_DIR32NB; - break; - - case srRelocHighLow: - // Emitted bytes: full address of target - // reloc src contains target offset relative to target section - pfixup->wType = IMAGE_REL_I386_DIR32; - break; - - case srRelocHighLowPtr: - // Emitted bytes: full address of target - // reloc src contains target pointer - pfixup->wType = IMAGE_REL_I386_DIR32; - break; - - case srRelocRelative: - // Emitted bytes: value of reloc tgt - (reloc source + sizeof(DWORD)) - // reloc src contains offset relative to target section, minus sizeof(DWORD) - // the reloc type for pFixup->rvaTarget is srRelocAbsolute - // so contents of pFixup->rvaTarget need to be offset Target + sizeof(DWORD) - // which is offset Target == Source contents + sizeof(DWORD) == *pdw + sizeof(DWORD) - pfixup->wType = IMAGE_REL_I386_REL32; - pfixup->rvaTarget = *pdw + sizeof(DWORD); - break; - - case srRelocRelativePtr: - // Emitted bytes: value of reloc tgt - (reloc source + sizeof(DWORD)) - // reloc src contains disp, disp = pTarget - (pSource + sizeof(DWORD)) - // the reloc type for pFixup->rvaTarget is srRelocAbsolutePtr - // so contents of pFixup->rvaTarget need to be pTarget - // which is pTarget == pSource + sizeof(DWORD) + disp == pdw + 4 + *pdw - pfixup->wType = IMAGE_REL_I386_REL32; - pfixup->rvaTarget = (int) (INT_PTR) pdw + sizeof(DWORD) + (int) *pdw; - break; - - case srRelocMapToken: - // Emitted bytes: contents of reloc source unchanged. - // reloc src contains token value - pfixup->wType = IMAGE_REL_I386_TOKEN; - break; - -#elif defined(HOST_AMD64) - /* - // - // X86-64 relocations - // - IMAGE_REL_AMD64_ABSOLUTE 0x0000 // Reference is absolute, no relocation is necessary - IMAGE_REL_AMD64_ADDR64 0x0001 // 64-bit address (VA). - IMAGE_REL_AMD64_ADDR32 0x0002 // 32-bit address (VA). - IMAGE_REL_AMD64_ADDR32NB 0x0003 // 32-bit address w/o image base (RVA). - IMAGE_REL_AMD64_REL32 0x0004 // 32-bit relative address from byte following reloc - IMAGE_REL_AMD64_REL32_1 0x0005 // 32-bit relative address from byte distance 1 from reloc - IMAGE_REL_AMD64_REL32_2 0x0006 // 32-bit relative address from byte distance 2 from reloc - IMAGE_REL_AMD64_REL32_3 0x0007 // 32-bit relative address from byte distance 3 from reloc - IMAGE_REL_AMD64_REL32_4 0x0008 // 32-bit relative address from byte distance 4 from reloc - IMAGE_REL_AMD64_REL32_5 0x0009 // 32-bit relative address from byte distance 5 from reloc - IMAGE_REL_AMD64_SECTION 0x000A // Section index - IMAGE_REL_AMD64_SECREL 0x000B // 32 bit offset from base of section containing target - IMAGE_REL_AMD64_SECREL7 0x000C // 7 bit unsigned offset from base of section containing target - IMAGE_REL_AMD64_TOKEN 0x000D // 32 bit metadata token - IMAGE_REL_AMD64_SREL32 0x000E // 32 bit signed span-dependent value emitted into object - IMAGE_REL_AMD64_PAIR 0x000F - IMAGE_REL_AMD64_SSPAN32 0x0010 // 32 bit signed span-dependent value applied at link time - */ - case srRelocAbsolute: - // Emitted bytes: RVA, offset relative to image base - pfixup->wType = IMAGE_REL_AMD64_ADDR32NB; - break; - - case srRelocAbsolutePtr: - // Emitted bytes: RVA, offset relative to image base - // reloc src contains target pointer - pfixup->wType = IMAGE_REL_AMD64_ADDR32NB; - break; - - case srRelocDir64Ptr: - // Emitted bytes: full address of target - // reloc src contains target pointer - pfixup->wType = IMAGE_REL_IA64_DIR64; - break; - - case srRelocMapToken: - // Emitted bytes: contents of reloc source unchanged. - // reloc src contains token value - pfixup->wType = IMAGE_REL_AMD64_TOKEN; - break; -#endif - case srRelocFilePos: - // Emitted bytes: offset relative to start of file, differs from RVA. - pfixup->wType = IMAGE_REL_I386_FILEPOS; - break; - - case srRelocAbsoluteTagged: - pfixup->wType = IMAGE_REL_I386_DIR30NB; - pfixup->rvaTarget = (*pdw & ~0x80000001) >> 1; - break; - - case srRelocHighAdj: - // Emitted bytes: 2 part relocation, with high part adjusted by constant. - pfixup->wType = IMAGE_REL_BASED_HIGHADJ; - break; - - default: - _ASSERTE(!"Unknown relocation type"); - return(E_UNEXPECTED); - break; - } - } - - return(S_OK); - -} // HRESULT CeeFileGenWriter::UpdateFixups() - - -HRESULT CeeFileGenWriter::setEmitFixups() -{ - m_fEmitFixups = true; - return(S_OK); - -} // HRESULT CeeFileGenWriter::setEmitFixups() - -#ifdef TEST_EMIT_FIXUPS - -HRESULT CeeFileGenWriter::TestEmitFixups() -{ - HRESULT hr; - // Test fixups - - CeeSection * testSection; - hr = getSectionCreate(".test", - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, - &testSection); - if (SUCCEEDED(hr)) - { - struct FixupEntry - { - char sz[18]; - DWORD wTargets[8]; - }; - - struct FixupTypes - { - char * pszType; - CeeSectionRelocType relocType; - }; - - FixupTypes rgTypes[] = - { - { "srRelocAbsolute ", srRelocAbsolute }, - { "srRelocAbsolutePtr", srRelocAbsolutePtr }, - { "srRelocHighLow ", srRelocHighLow }, - { "srRelocHighLowPtr ", srRelocHighLowPtr }, - // { "srRelocRelative ", srRelocRelative }, - // { "srRelocRelativePtr", srRelocRelativePtr }, - { "srRelocMapToken ", srRelocMapToken }, - // { "srRelocFilePos ", srRelocFilePos }, - // { "srRelocHighAdj ", srRelocHighAdj }, - }; - - const size_t cFixups = sizeof(rgTypes) / sizeof(rgTypes[0]); - - DWORD * pdwTargets[20]; - - // Target Blocks: - - for (size_t idx = 0; idx < cFixups; idx++) - { - hr = GetSectionBlock(testSection, sizeof(DWORD), 1, (void **) &pdwTargets[idx]); - _ASSERTE(SUCCEEDED(hr)); - - DWORD * pdw = pdwTargets[idx]; - *pdw = idx; - } - - for (size_t idxType = 0; idxType < cFixups; idxType++) - { - // Fixup Entries - FixupEntry * pEntry; - hr = GetSectionBlock(testSection, sizeof(FixupEntry), 1, (void **) &pEntry); - _ASSERTE(SUCCEEDED(hr)); - - memset(pEntry, 0, sizeof(FixupEntry)); - strcpy_s(pEntry->sz, sizeof(pEntry->sz), rgTypes[idxType].pszType); - - size_t ibBlock = testSection->computeOffset((char *) pEntry); - - for (size_t idx = 0; idx < cFixups; idx++) - { - size_t ibFixup = ((size_t) &pEntry->wTargets[idx]) - (size_t) pEntry; - - switch (rgTypes[idxType].relocType) - { - case srRelocAbsolute: - pEntry->wTargets[idx] = idx * sizeof(DWORD); - break; - - case srRelocAbsolutePtr: - pEntry->wTargets[idx] = (DWORD) pdwTargets[idx]; - break; - - case srRelocHighLow: - pEntry->wTargets[idx] = idx * sizeof(DWORD); - break; - - case srRelocHighLowPtr: - pEntry->wTargets[idx] = (DWORD) pdwTargets[idx]; - break; - - case srRelocRelative: - pEntry->wTargets[idx] = idx; - break; - - case srRelocRelativePtr: - { - size_t ibTgt = (size_t) pdwTargets[idx]; - size_t ibSrc = ((size_t) &pEntry->wTargets[idx]) + sizeof(DWORD); - pEntry->wTargets[idx] = (DWORD)( ibTgt - ibSrc ); - ibFixup += sizeof(DWORD); // offset needs to point at end of DWORD - break; - } - - case srRelocHighAdj: - pEntry->wTargets[idx] = idx * sizeof(DWORD); - break; - - case srRelocMapToken: - pEntry->wTargets[idx] = idx * sizeof(DWORD); - break; - - case srRelocFilePos: - pEntry->wTargets[idx] = idx * sizeof(DWORD); - break; - } - - addFixup(*testSection, ibBlock + ibFixup, rgTypes[idxType].relocType, testSection); - testSection->addSectReloc(ibBlock + ibFixup, *testSection, rgTypes[idxType].relocType); - } - } - } - - return(S_OK); -} -#endif // TEST_EMIT_FIXUPS -#endif // EMIT_FIXUPS diff --git a/src/coreclr/dlls/mscorpe/ceefilegenwritertokens.cpp b/src/coreclr/dlls/mscorpe/ceefilegenwritertokens.cpp index 31b4d20411914..beb0ee71d514d 100644 --- a/src/coreclr/dlls/mscorpe/ceefilegenwritertokens.cpp +++ b/src/coreclr/dlls/mscorpe/ceefilegenwritertokens.cpp @@ -126,11 +126,6 @@ HRESULT CeeFileGenWriter::MapTokensForMethod( COR_ILMETHOD_DECODER method((COR_ILMETHOD*) pCode); - // If compressed IL is being emitted, this routine will have no idea how to walk the tokens, - // so don't do it - if (m_dwMacroDefinitionSize != 0) - return S_OK; - pCode = const_cast(method.Code); PC = 0; diff --git a/src/coreclr/dlls/mscorpe/iceefilegen.cpp b/src/coreclr/dlls/mscorpe/iceefilegen.cpp index 44c939497d8dc..6714d355d0bfc 100644 --- a/src/coreclr/dlls/mscorpe/iceefilegen.cpp +++ b/src/coreclr/dlls/mscorpe/iceefilegen.cpp @@ -7,75 +7,6 @@ #include "iceefilegen.h" #include "ceefilegenwriter.h" -// Deprecated -//**************************************************************************** - HRESULT ICeeFileGen::EmitMethod () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::EmitSignature () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::SetEntryClassToken () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::GetEntryClassToken () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::SetEntryPointDescr () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::GetEntryPointDescr () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::SetEntryPointFlags () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::GetEntryPointFlags () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::CreateSig () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::AddSigArg () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::SetSigReturnType () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::SetSigCallingConvention () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } - HRESULT ICeeFileGen::DeleteSig () - { - _ASSERTE("Deprecated" && 0); - return (E_FAIL); - } -//**************************************************************************** - EXTERN_C HRESULT __stdcall CreateICeeFileGen(ICeeFileGen** pCeeFileGen) { if (!pCeeFileGen) @@ -125,7 +56,7 @@ HRESULT ICeeFileGen::CreateCeeFileEx2 (HCEEFILE *ceeFile, DWORD createFlags, LPC return S_OK; } -HRESULT ICeeFileGen::CreateCeeFileFromICeeGen(ICeeGen *pICeeGen, HCEEFILE *ceeFile, DWORD createFlags) +HRESULT ICeeFileGen::CreateCeeFileFromICeeGen(ICeeGenInternal *pICeeGen, HCEEFILE *ceeFile, DWORD createFlags) { if (!ceeFile) return E_POINTER; @@ -235,12 +166,6 @@ HRESULT ICeeFileGen::GetSectionBlock (HCEESECTION section, ULONG len, return S_OK; } -HRESULT ICeeFileGen::TruncateSection (HCEESECTION section, ULONG len) -{ - _ASSERTE(!"This is an obsolete function!"); - return E_NOTIMPL; -} - HRESULT ICeeFileGen::AddSectionReloc (HCEESECTION section, ULONG offset, HCEESECTION relativeTo, CeeSectionRelocType relocType) { TESTANDRETURNPOINTER(section); @@ -250,39 +175,14 @@ HRESULT ICeeFileGen::AddSectionReloc (HCEESECTION section, ULONG offset, HCEESEC if (relSec) { -#ifdef EMIT_FIXUPS - CeeFileGenWriter * gen = reinterpret_cast(&sec->ceeFile()); - HRESULT hr = gen->addFixup(*sec, offset, relocType, relSec); - if (FAILED(hr)) - { - return(hr); - } -#endif return(sec->addSectReloc(offset, *relSec, relocType)); } else { -#ifdef EMIT_FIXUPS - CeeFileGenWriter * gen = reinterpret_cast(&sec->ceeFile()); - HRESULT hr = gen->addFixup(*sec, offset, relocType); - if (FAILED(hr)) - { - return(hr); - } -#endif return(sec->addBaseReloc(offset, relocType)); } } -HRESULT ICeeFileGen::SetSectionDirectoryEntry(HCEESECTION section, ULONG num) -{ - TESTANDRETURNPOINTER(section); - - printf("Warning: deprecated method. Use SetDirectoryEntry instead\n"); - CeeSection *sec = reinterpret_cast(section); - return(sec->directoryEntry(num)); -} - HRESULT ICeeFileGen::SetOutputFileName (HCEEFILE ceeFile, __in LPWSTR outputFileName) { TESTANDRETURNPOINTER(ceeFile); @@ -368,20 +268,6 @@ HRESULT ICeeFileGen::GetIMapTokenIface(HCEEFILE ceeFile, IMetaDataEmit *emitter, return E_NOTIMPL; } -HRESULT ICeeFileGen::EmitMetaData (HCEEFILE ceeFile, IMetaDataEmit *emitter, - mdScope scopeE) -{ - _ASSERTE(!"This is an obsolete function!"); - return E_NOTIMPL; -} - -HRESULT ICeeFileGen::EmitLibraryName (HCEEFILE ceeFile, IMetaDataEmit *emitter, - mdScope scopeE) -{ - _ASSERTE(!"This is an obsolete function!"); - return E_NOTIMPL; -} - HRESULT ICeeFileGen::GetMethodRVA(HCEEFILE ceeFile, ULONG codeOffset, ULONG *codeRVA) { TESTANDRETURNARG(ceeFile != 0); @@ -407,14 +293,6 @@ HRESULT ICeeFileGen::LinkCeeFile (HCEEFILE ceeFile) return gen->link(); } -HRESULT ICeeFileGen::FixupCeeFile (HCEEFILE ceeFile) -{ - TESTANDRETURNPOINTER(ceeFile); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return gen->fixup(); -} - HRESULT ICeeFileGen::GetHeaderInfo (HCEEFILE ceeFile, PIMAGE_NT_HEADERS *ppNtHeaders, PIMAGE_SECTION_HEADER *ppSections, ULONG *pNumSections) { TESTANDRETURNPOINTER(ceeFile); @@ -432,17 +310,6 @@ HRESULT ICeeFileGen::GenerateCeeFile (HCEEFILE ceeFile) return gen->generateImage(NULL); // NULL means don't write in-memory buffer, uses outputFileName } -// GenerateCeeMemoryImage - returns in ppImage an in-memory PE image allocated by CoTaskMemAlloc() -// the caller is responsible for calling CoTaskMemFree on this memory image -HRESULT ICeeFileGen::GenerateCeeMemoryImage (HCEEFILE ceeFile, void **ppImage) -{ - TESTANDRETURNPOINTER(ceeFile); - TESTANDRETURNPOINTER(ppImage); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return gen->generateImage(ppImage); -} - HRESULT ICeeFileGen::SetEntryPoint(HCEEFILE ceeFile, mdMethodDef method) { TESTANDRETURNPOINTER(ceeFile); @@ -507,55 +374,6 @@ HRESULT ICeeFileGen::GetDllSwitch (HCEEFILE ceeFile, BOOL *dllSwitch) return S_OK; } -HRESULT ICeeFileGen::SetObjSwitch (HCEEFILE ceeFile, BOOL objSwitch) -{ - TESTANDRETURNPOINTER(ceeFile); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return(gen->setObjSwitch(objSwitch==TRUE)); -} - -HRESULT ICeeFileGen::GetObjSwitch (HCEEFILE ceeFile, BOOL *objSwitch) -{ - TESTANDRETURNPOINTER(ceeFile); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - TESTANDRETURNPOINTER(objSwitch); - *objSwitch = gen->getObjSwitch(); - return S_OK; -} - - -HRESULT ICeeFileGen::SetLibraryName (HCEEFILE ceeFile, __in LPWSTR LibraryName) -{ - TESTANDRETURNPOINTER(ceeFile); - TESTANDRETURNPOINTER(LibraryName); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return(gen->setLibraryName(LibraryName)); -} - -HRESULT ICeeFileGen::SetLibraryGuid (HCEEFILE ceeFile, __in LPWSTR LibraryGuid) -{ - TESTANDRETURNPOINTER(ceeFile); - TESTANDRETURNPOINTER(LibraryGuid); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return(gen->setLibraryGuid(LibraryGuid)); -} - -__success(return == S_OK) HRESULT ICeeFileGen::GetLibraryName (HCEEFILE ceeFile, __out LPWSTR *LibraryName) -{ - TESTANDRETURNPOINTER(ceeFile); - TESTANDRETURNPOINTER(LibraryName); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - *LibraryName = gen->getLibraryName(); - return S_OK; -} - - - HRESULT ICeeFileGen::EmitMetaDataEx (HCEEFILE ceeFile, IMetaDataEmit *emitter) { TESTANDRETURNPOINTER(ceeFile); @@ -576,15 +394,6 @@ HRESULT ICeeFileGen::EmitMetaDataAt (HCEEFILE ceeFile, IMetaDataEmit *emitter, H return(gen->emitMetaData(emitter, sec, offset, buffer, buffLen)); } -HRESULT ICeeFileGen::EmitLibraryNameEx (HCEEFILE ceeFile, IMetaDataEmit *emitter) -{ - TESTANDRETURNPOINTER(ceeFile); - TESTANDRETURNPOINTER(emitter); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return(gen->emitLibraryName(emitter)); -} - HRESULT ICeeFileGen::GetIMapTokenIfaceEx(HCEEFILE ceeFile, IMetaDataEmit *emitter, IUnknown **pIMapToken) { TESTANDRETURNPOINTER(ceeFile); @@ -604,14 +413,6 @@ HRESULT ICeeFileGen::AddNotificationHandler(HCEEFILE ceeFile, return gen->addNotificationHandler(pHandler); } -HRESULT ICeeFileGen::EmitMacroDefinitions(HCEEFILE ceeFile, void *pData, DWORD cData) -{ - TESTANDRETURNPOINTER(ceeFile); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return gen->EmitMacroDefinitions(pData, cData); -} - HRESULT ICeeFileGen::SetManifestEntry(HCEEFILE ceeFile, ULONG size, ULONG offset) { TESTANDRETURNPOINTER(ceeFile); @@ -670,14 +471,6 @@ HRESULT ICeeFileGen::ComputeOffset(HCEEFILE ceeFile, __in char *ptr, return hr; } -HRESULT ICeeFileGen::SetEnCRVABase(HCEEFILE ceeFile, ULONG dataBase, ULONG rdataBase) -{ - TESTANDRETURNPOINTER(ceeFile); - - CeeFileGenWriter *gen = reinterpret_cast(ceeFile); - return gen->setEnCRvaBase(dataBase, rdataBase); -} - HRESULT ICeeFileGen::GetCorHeader(HCEEFILE ceeFile, IMAGE_COR20_HEADER **header) { diff --git a/src/coreclr/dlls/mscorpe/pewriter.cpp b/src/coreclr/dlls/mscorpe/pewriter.cpp index cc70bd8875289..f1efec649c459 100644 --- a/src/coreclr/dlls/mscorpe/pewriter.cpp +++ b/src/coreclr/dlls/mscorpe/pewriter.cpp @@ -44,10 +44,6 @@ inline static unsigned padLen(unsigned len, unsigned align) { return(roundUp(len, align) - len); } -inline static bool isExeOrDll(IMAGE_NT_HEADERS* ntHeaders) { - return ((ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_EXECUTABLE_IMAGE)) != 0); -} - #ifndef IMAGE_DLLCHARACTERISTICS_NO_SEH #define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x400 #endif @@ -877,7 +873,6 @@ HRESULT PEWriter::Init(PESectionMan *pFrom, DWORD createFlags, LPCWSTR seedFileN m_dataRvaBase = 0; m_rdataRvaBase = 0; m_codeRvaBase = 0; - m_encMode = FALSE; virtualPos = 0; filePos = 0; @@ -1089,13 +1084,6 @@ HRESULT PEWriter::setDirectoryEntry(PEWriterSection *section, ULONG entry, ULONG return S_OK; } -void PEWriter::setEnCRvaBase(ULONG dataBase, ULONG rdataBase) -{ - m_dataRvaBase = dataBase; - m_rdataRvaBase = rdataBase; - m_encMode = TRUE; -} - //----------------------------------------------------------------------------- // These 2 write functions must be implemented here so that they're in the same // .obj file as whoever creates the FILE struct. We can't pass a FILE struct @@ -1276,8 +1264,6 @@ HRESULT PEWriter::linkSortSections(entry * entries, // - empty sections receive no PE section // - bool ExeOrDll = isExeOrDll(m_ntHeaders); - entry *e = entries; for (PEWriterSection **cur = getSectStart(); cur < getSectCur(); cur++) { @@ -1294,13 +1280,6 @@ HRESULT PEWriter::linkSortSections(entry * entries, if ((*cur)->dataLen() == 0) continue; - // - // Special case: omit "text0" from obj's - // - - if (!ExeOrDll && strcmp((*cur)->m_name, ".text0") == 0) - continue; - e->name = (*cur)->m_name; // @@ -1571,8 +1550,6 @@ HRESULT PEWriter::link() { // Assign base addresses to all sections, and layout & merge PE sections // - bool ExeOrDll = isExeOrDll(m_ntHeaders); - // // Collate by name & sort by index // @@ -1605,17 +1582,7 @@ HRESULT PEWriter::link() { // Figure out what file alignment to use. // - unsigned RoundUpVal; - - if (ExeOrDll) - { - RoundUpVal = 0x0200; - } - else - { - // Don't bother padding for objs - RoundUpVal = 4; - } + unsigned RoundUpVal = 0x0200; m_ntHeaders->OptionalHeader.FileAlignment = VAL32(RoundUpVal); } @@ -1624,15 +1591,8 @@ HRESULT PEWriter::link() { // Now, assign a section header & location to each section // - if (ExeOrDll) - { - iUniqueSections++; // One more for .reloc - filePos = sizeof(IMAGE_DOS_HEADER)+sizeof(x86StubPgm) + m_ntHeadersSize; - } - else - { - filePos = sizeof(IMAGE_FILE_HEADER); - } + iUniqueSections++; // One more for .reloc + filePos = sizeof(IMAGE_DOS_HEADER)+sizeof(x86StubPgm) + m_ntHeadersSize; m_ntHeaders->FileHeader.NumberOfSections = VAL16(iUniqueSections); @@ -1677,412 +1637,184 @@ class SectionRVASorter : public CQuickSort } }; -#ifdef _PREFAST_ -#pragma warning(push) -#pragma warning(disable:21000) // Suppress PREFast warning about overly large function -#endif HRESULT PEWriter::fixup(CeeGenTokenMapper *pMapper) { HRESULT hr; - bool ExeOrDll = isExeOrDll(m_ntHeaders); const unsigned RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment); - if(ExeOrDll) - { - // - // Apply manual relocation for entry point field - // - - PESection *textSection; - IfFailRet(getSectionCreate(".text", 0, &textSection)); + // + // Apply manual relocation for entry point field + // - if (m_ntHeaders->OptionalHeader.AddressOfEntryPoint != VAL32(0)) - m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(VAL32(m_ntHeaders->OptionalHeader.AddressOfEntryPoint) + textSection->m_baseRVA); + PESection *textSection; + IfFailRet(getSectionCreate(".text", 0, &textSection)); - // - // Apply normal relocs - // + if (m_ntHeaders->OptionalHeader.AddressOfEntryPoint != VAL32(0)) + m_ntHeaders->OptionalHeader.AddressOfEntryPoint = VAL32(VAL32(m_ntHeaders->OptionalHeader.AddressOfEntryPoint) + textSection->m_baseRVA); - IfFailRet(getSectionCreate(".reloc", sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, - (PESection **) &reloc)); - reloc->m_baseRVA = virtualPos; - reloc->m_filePos = filePos; - reloc->m_header = headersEnd++; - strcpy_s((char *)reloc->m_header->Name, sizeof(reloc->m_header->Name), - ".reloc"); - reloc->m_header->Characteristics = VAL32(reloc->m_flags); - reloc->m_header->VirtualAddress = VAL32(virtualPos); - reloc->m_header->PointerToRawData = VAL32(filePos); + // + // Apply normal relocs + // -#ifdef _DEBUG - if (m_encMode) - printf("Applying relocs for .rdata section with RVA %x\n", m_rdataRvaBase); -#endif + IfFailRet(getSectionCreate(".reloc", sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, + (PESection **) &reloc)); + reloc->m_baseRVA = virtualPos; + reloc->m_filePos = filePos; + reloc->m_header = headersEnd++; + strcpy_s((char *)reloc->m_header->Name, sizeof(reloc->m_header->Name), + ".reloc"); + reloc->m_header->Characteristics = VAL32(reloc->m_flags); + reloc->m_header->VirtualAddress = VAL32(virtualPos); + reloc->m_header->PointerToRawData = VAL32(filePos); - // - // Sort the sections by RVA - // + // + // Sort the sections by RVA + // - CQuickArray sections; + CQuickArray sections; - SIZE_T count = getSectCur() - getSectStart(); - IfFailRet(sections.ReSizeNoThrow(count)); - UINT i = 0; - PEWriterSection **cur; - for(cur = getSectStart(); cur < getSectCur(); cur++, i++) - sections[i] = *cur; + SIZE_T count = getSectCur() - getSectStart(); + IfFailRet(sections.ReSizeNoThrow(count)); + UINT i = 0; + PEWriterSection **cur; + for(cur = getSectStart(); cur < getSectCur(); cur++, i++) + sections[i] = *cur; - SectionRVASorter sorter(sections.Ptr(), sections.Size()); + SectionRVASorter sorter(sections.Ptr(), sections.Size()); - sorter.Sort(); + sorter.Sort(); - PERelocSection relocSection(reloc); + PERelocSection relocSection(reloc); - cur = sections.Ptr(); - PEWriterSection **curEnd = cur + sections.Size(); - while (cur < curEnd) - { - IfFailRet((*cur)->applyRelocs(m_ntHeaders, - &relocSection, - pMapper, - m_dataRvaBase, - m_rdataRvaBase, - m_codeRvaBase)); - cur++; - } + cur = sections.Ptr(); + PEWriterSection **curEnd = cur + sections.Size(); + while (cur < curEnd) + { + IfFailRet((*cur)->applyRelocs(m_ntHeaders, + &relocSection, + pMapper, + m_dataRvaBase, + m_rdataRvaBase, + m_codeRvaBase)); + cur++; + } - relocSection.Finish(isPE32()); - reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen()); + relocSection.Finish(isPE32()); + reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen()); - // Strip the reloc section if the flag is set - if (m_ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) - { - reloc->m_header->Misc.VirtualSize = VAL32(0); - } + // Strip the reloc section if the flag is set + if (m_ntHeaders->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) + { + reloc->m_header->Misc.VirtualSize = VAL32(0); + } - reloc->m_header->SizeOfRawData = VAL32(roundUp(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal)); - reloc->m_filePad = padLen(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal); - filePos += VAL32(reloc->m_header->SizeOfRawData); - virtualPos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize), - VAL32(m_ntHeaders->OptionalHeader.SectionAlignment)); + reloc->m_header->SizeOfRawData = VAL32(roundUp(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal)); + reloc->m_filePad = padLen(VAL32(reloc->m_header->Misc.VirtualSize), RoundUpVal); + filePos += VAL32(reloc->m_header->SizeOfRawData); + virtualPos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize), + VAL32(m_ntHeaders->OptionalHeader.SectionAlignment)); - if (reloc->m_header->Misc.VirtualSize == VAL32(0)) + if (reloc->m_header->Misc.VirtualSize == VAL32(0)) + { + // + // Omit reloc section from section list. (It will + // still be there but the loader won't see it - this + // only works because we've allocated it as the last + // section.) + // + m_ntHeaders->FileHeader.NumberOfSections = VAL16(VAL16(m_ntHeaders->FileHeader.NumberOfSections) - 1); + } + else + { + IMAGE_DATA_DIRECTORY * pRelocDataDirectory; + // + // Put reloc address in header + // + if (isPE32()) { - // - // Omit reloc section from section list. (It will - // still be there but the loader won't see it - this - // only works because we've allocated it as the last - // section.) - // - m_ntHeaders->FileHeader.NumberOfSections = VAL16(VAL16(m_ntHeaders->FileHeader.NumberOfSections) - 1); + pRelocDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); } else { - IMAGE_DATA_DIRECTORY * pRelocDataDirectory; - // - // Put reloc address in header - // + pRelocDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); + } + + pRelocDataDirectory->VirtualAddress = reloc->m_header->VirtualAddress; + pRelocDataDirectory->Size = reloc->m_header->Misc.VirtualSize; + } + + // compute ntHeader fields that depend on the sizes of other things + for(IMAGE_SECTION_HEADER *h = headersEnd-1; h >= headers; h--) { // go backwards, so first entry takes precedence + if (h->Characteristics & VAL32(IMAGE_SCN_CNT_CODE)) { + m_ntHeaders->OptionalHeader.BaseOfCode = h->VirtualAddress; + m_ntHeaders->OptionalHeader.SizeOfCode = + VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfCode) + VAL32(h->SizeOfRawData)); + } + if (h->Characteristics & VAL32(IMAGE_SCN_CNT_INITIALIZED_DATA)) { if (isPE32()) { - pRelocDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); + ntHeaders32()->OptionalHeader.BaseOfData = h->VirtualAddress; } - else - { - pRelocDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]); - } - - pRelocDataDirectory->VirtualAddress = reloc->m_header->VirtualAddress; - pRelocDataDirectory->Size = reloc->m_header->Misc.VirtualSize; + m_ntHeaders->OptionalHeader.SizeOfInitializedData = + VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfInitializedData) + VAL32(h->SizeOfRawData)); } - - // compute ntHeader fields that depend on the sizes of other things - for(IMAGE_SECTION_HEADER *h = headersEnd-1; h >= headers; h--) { // go backwards, so first entry takes precedence - if (h->Characteristics & VAL32(IMAGE_SCN_CNT_CODE)) { - m_ntHeaders->OptionalHeader.BaseOfCode = h->VirtualAddress; - m_ntHeaders->OptionalHeader.SizeOfCode = - VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfCode) + VAL32(h->SizeOfRawData)); - } - if (h->Characteristics & VAL32(IMAGE_SCN_CNT_INITIALIZED_DATA)) { - if (isPE32()) - { - ntHeaders32()->OptionalHeader.BaseOfData = h->VirtualAddress; - } - m_ntHeaders->OptionalHeader.SizeOfInitializedData = - VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfInitializedData) + VAL32(h->SizeOfRawData)); - } - if (h->Characteristics & VAL32(IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { - m_ntHeaders->OptionalHeader.SizeOfUninitializedData = - VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfUninitializedData) + VAL32(h->SizeOfRawData)); - } + if (h->Characteristics & VAL32(IMAGE_SCN_CNT_UNINITIALIZED_DATA)) { + m_ntHeaders->OptionalHeader.SizeOfUninitializedData = + VAL32(VAL32(m_ntHeaders->OptionalHeader.SizeOfUninitializedData) + VAL32(h->SizeOfRawData)); } + } - int index; - IMAGE_DATA_DIRECTORY * pCurDataDirectory; + int index; + IMAGE_DATA_DIRECTORY * pCurDataDirectory; - // go backwards, so first entry takes precedence - for(cur = getSectCur()-1; getSectStart() <= cur; --cur) - { - index = (*cur)->getDirEntry(); + // go backwards, so first entry takes precedence + for(cur = getSectCur()-1; getSectStart() <= cur; --cur) + { + index = (*cur)->getDirEntry(); - // Is this a valid directory entry - if (index > 0) + // Is this a valid directory entry + if (index > 0) + { + if (isPE32()) { - if (isPE32()) - { - _ASSERTE((unsigned)(index) < VAL32(ntHeaders32()->OptionalHeader.NumberOfRvaAndSizes)); + _ASSERTE((unsigned)(index) < VAL32(ntHeaders32()->OptionalHeader.NumberOfRvaAndSizes)); - pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]); - } - else - { - _ASSERTE((unsigned)(index) < VAL32(ntHeaders64()->OptionalHeader.NumberOfRvaAndSizes)); - - pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]); - } - - pCurDataDirectory->VirtualAddress = VAL32((*cur)->m_baseRVA); - pCurDataDirectory->Size = VAL32((*cur)->dataLen()); + pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]); } - } - - // handle the directory entries specified using the file. - for (index=0; index < cEntries; index++) - { - if (pEntries[index].section) + else { - PEWriterSection *section = pEntries[index].section; - _ASSERTE(pEntries[index].offset < section->dataLen()); - - if (isPE32()) - pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]); - else - pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]); + _ASSERTE((unsigned)(index) < VAL32(ntHeaders64()->OptionalHeader.NumberOfRvaAndSizes)); - pCurDataDirectory->VirtualAddress = VAL32(section->m_baseRVA + pEntries[index].offset); - pCurDataDirectory->Size = VAL32(pEntries[index].size); + pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]); } + + pCurDataDirectory->VirtualAddress = VAL32((*cur)->m_baseRVA); + pCurDataDirectory->Size = VAL32((*cur)->dataLen()); } + } - m_ntHeaders->OptionalHeader.SizeOfImage = VAL32(virtualPos); - } // end if(ExeOrDll) - else //i.e., if OBJ + // handle the directory entries specified using the file. + for (index=0; index < cEntries; index++) { - // - // Clean up note: - // I've cleaned up the executable linking path, but the .obj linking - // is still a bit strange, what with a "extra" reloc & strtab sections - // which are created after the linking step and get treated specially. - // - reloc = new (nothrow) PEWriterSection(".reloc", - sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, 0x4000, 0); - if(reloc == NULL) return E_OUTOFMEMORY; - strtab = new (nothrow) PEWriterSection(".strtab", - sdReadOnly | IMAGE_SCN_MEM_DISCARDABLE, 0x4000, 0); //string table (if any) - if(strtab == NULL) return E_OUTOFMEMORY; - - DWORD* TokInSymbolTable = new (nothrow) DWORD[16386]; - if (TokInSymbolTable == NULL) return E_OUTOFMEMORY; - - m_ntHeaders->FileHeader.SizeOfOptionalHeader = 0; - //For each section set VirtualAddress to 0 - PEWriterSection **cur; - for(cur = getSectStart(); cur < getSectCur(); cur++) + if (pEntries[index].section) { - IMAGE_SECTION_HEADER* header = (*cur)->m_header; - header->VirtualAddress = VAL32(0); - } - // Go over section relocations and build the Symbol Table, use .reloc section as buffer: - DWORD tk=0, rva=0, NumberOfSymbols=0; - BOOL ToRelocTable = FALSE; - IMAGE_SYMBOL is; - IMAGE_RELOCATION ir; - ULONG StrTableLen = 4; //size itself only - char* szSymbolName = NULL; - char* pch; - - PESection *textSection; - getSectionCreate(".text", 0, &textSection); - - for(PESectionReloc* rcur = textSection->m_relocStart; rcur < textSection->m_relocCur; rcur++) - { - switch((int)rcur->type) - { - case 0x7FFA: // Ptr to symbol name -#ifdef HOST_64BIT - _ASSERTE(!"this is probably broken!!"); -#endif // HOST_64BIT - szSymbolName = (char*)(UINT_PTR)(rcur->offset); - break; + PEWriterSection *section = pEntries[index].section; + _ASSERTE(pEntries[index].offset < section->dataLen()); - case 0x7FFC: // Ptr to file name - TokInSymbolTable[NumberOfSymbols++] = 0; - memset(&is,0,sizeof(IMAGE_SYMBOL)); - memcpy(is.N.ShortName,".file\0\0\0",8); - is.Value = 0; - is.SectionNumber = VAL16(IMAGE_SYM_DEBUG); - is.Type = VAL16(IMAGE_SYM_DTYPE_NULL); - is.StorageClass = IMAGE_SYM_CLASS_FILE; - is.NumberOfAuxSymbols = 1; - if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) - memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); - else return E_OUTOFMEMORY; - TokInSymbolTable[NumberOfSymbols++] = 0; - memset(&is,0,sizeof(IMAGE_SYMBOL)); -#ifdef HOST_64BIT - _ASSERTE(!"this is probably broken!!"); -#endif // HOST_64BIT - strcpy_s((char*)&is,sizeof(is),(char*)(UINT_PTR)(rcur->offset)); - if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) - memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); - else return E_OUTOFMEMORY; -#ifdef HOST_64BIT - _ASSERTE(!"this is probably broken!!"); -#endif // HOST_64BIT - delete (char*)(UINT_PTR)(rcur->offset); - ToRelocTable = FALSE; - tk = 0; - szSymbolName = NULL; - break; - - case 0x7FFB: // compid value - TokInSymbolTable[NumberOfSymbols++] = 0; - memset(&is,0,sizeof(IMAGE_SYMBOL)); - memcpy(is.N.ShortName,"@comp.id",8); - is.Value = VAL32(rcur->offset); - is.SectionNumber = VAL16(IMAGE_SYM_ABSOLUTE); - is.Type = VAL16(IMAGE_SYM_DTYPE_NULL); - is.StorageClass = IMAGE_SYM_CLASS_STATIC; - is.NumberOfAuxSymbols = 0; - if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) - memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); - else return E_OUTOFMEMORY; - ToRelocTable = FALSE; - tk = 0; - szSymbolName = NULL; - break; - - case 0x7FFF: // Token value, def - tk = rcur->offset; - ToRelocTable = FALSE; - break; - - case 0x7FFE: //Token value, ref - tk = rcur->offset; - ToRelocTable = TRUE; - break; - - case 0x7FFD: //RVA value - rva = rcur->offset; - if(tk) - { - // Add to SymbolTable - DWORD i; - for(i = 0; (i < NumberOfSymbols)&&(tk != TokInSymbolTable[i]); i++); - if(i == NumberOfSymbols) - { - if(szSymbolName && *szSymbolName) // Add "extern" symbol and string table entry - { - TokInSymbolTable[NumberOfSymbols++] = 0; - memset(&is,0,sizeof(IMAGE_SYMBOL)); - i++; // so reloc record (if generated) points to COM token symbol - is.N.Name.Long = VAL32(StrTableLen); - is.SectionNumber = VAL16(1); //textSection is the first one - is.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; - is.NumberOfAuxSymbols = 0; - is.Value = VAL32(rva); - if(TypeFromToken(tk) == mdtMethodDef) - { - is.Type = VAL16(0x20); //IMAGE_SYM_DTYPE_FUNCTION; - } - if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) - memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); - else return E_OUTOFMEMORY; - DWORD l = (DWORD)(strlen(szSymbolName)+1); // don't forget zero terminator! - if((pch = reloc->getBlock(1))) - memcpy(pch,szSymbolName,1); - else return E_OUTOFMEMORY; - delete szSymbolName; - StrTableLen += l; - } - TokInSymbolTable[NumberOfSymbols++] = tk; - memset(&is,0,sizeof(IMAGE_SYMBOL)); - sprintf_s((char*)is.N.ShortName,sizeof(is.N.ShortName),"%08X",tk); - is.SectionNumber = VAL16(1); //textSection is the first one - is.StorageClass = 0x6B; //IMAGE_SYM_CLASS_COM_TOKEN; - is.Value = VAL32(rva); - if(TypeFromToken(tk) == mdtMethodDef) - { - is.Type = VAL16(0x20); //IMAGE_SYM_DTYPE_FUNCTION; - //is.NumberOfAuxSymbols = 1; - } - if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) - memcpy(pch,&is,sizeof(IMAGE_SYMBOL)); - else return E_OUTOFMEMORY; - if(is.NumberOfAuxSymbols == 1) - { - BYTE dummy[sizeof(IMAGE_SYMBOL)]; - memset(dummy,0,sizeof(IMAGE_SYMBOL)); - dummy[0] = dummy[2] = 1; - if((pch = reloc->getBlock(sizeof(IMAGE_SYMBOL)))) - memcpy(pch,dummy,sizeof(IMAGE_SYMBOL)); - else return E_OUTOFMEMORY; - TokInSymbolTable[NumberOfSymbols++] = 0; - } - } - if(ToRelocTable) - { - IMAGE_SECTION_HEADER* phdr = textSection->m_header; - // Add to reloc table - ir.VirtualAddress = VAL32(rva); - ir.SymbolTableIndex = VAL32(i); - ir.Type = VAL16(IMAGE_REL_I386_SECREL); - if(phdr->PointerToRelocations == 0) - phdr->PointerToRelocations = VAL32(VAL32(phdr->PointerToRawData) + VAL32(phdr->SizeOfRawData)); - phdr->NumberOfRelocations = VAL32(VAL32(phdr->NumberOfRelocations) + 1); - if((pch = reloc->getBlock(sizeof(IMAGE_RELOCATION)))) - memcpy(pch,&is,sizeof(IMAGE_RELOCATION)); - else return E_OUTOFMEMORY; - } - } - ToRelocTable = FALSE; - tk = 0; - szSymbolName = NULL; - break; - - default: - break; - } //end switch(cur->type) - } // end for all relocs - // Add string table counter: - if((pch = reloc->getBlock(sizeof(ULONG)))) - memcpy(pch,&StrTableLen,sizeof(ULONG)); - else return E_OUTOFMEMORY; - reloc->m_header->Misc.VirtualSize = VAL32(reloc->dataLen()); - if(NumberOfSymbols) - { - // recompute the actual sizes and positions of all the sections - filePos = roundUp(VAL16(m_ntHeaders->FileHeader.NumberOfSections) * sizeof(IMAGE_SECTION_HEADER)+ - sizeof(IMAGE_FILE_HEADER), RoundUpVal); - for(cur = getSectStart(); cur < getSectCur(); cur++) - { - IMAGE_SECTION_HEADER* header = (*cur)->m_header; - header->Misc.VirtualSize = VAL32((*cur)->dataLen()); - header->VirtualAddress = VAL32(0); - header->SizeOfRawData = VAL32(roundUp(VAL32(header->Misc.VirtualSize), RoundUpVal)); - header->PointerToRawData = VAL32(filePos); + if (isPE32()) + pCurDataDirectory = &(ntHeaders32()->OptionalHeader.DataDirectory[index]); + else + pCurDataDirectory = &(ntHeaders64()->OptionalHeader.DataDirectory[index]); - filePos += VAL32(header->SizeOfRawData); - } - m_ntHeaders->FileHeader.Machine = VAL16(0xC0EE); //COM+ EE - m_ntHeaders->FileHeader.PointerToSymbolTable = VAL32(filePos); - m_ntHeaders->FileHeader.NumberOfSymbols = VAL32(NumberOfSymbols); - filePos += roundUp(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(),RoundUpVal); + pCurDataDirectory->VirtualAddress = VAL32(section->m_baseRVA + pEntries[index].offset); + pCurDataDirectory->Size = VAL32(pEntries[index].size); } - delete[] TokInSymbolTable; - } //end if OBJ + } + + m_ntHeaders->OptionalHeader.SizeOfImage = VAL32(virtualPos); - const unsigned headerOffset = (unsigned) (ExeOrDll ? sizeof(IMAGE_DOS_HEADER) + sizeof(x86StubPgm) : 0); + const unsigned headerOffset = (unsigned)sizeof(IMAGE_DOS_HEADER) + sizeof(x86StubPgm); memset(&m_dosHeader, 0, sizeof(IMAGE_DOS_HEADER)); m_dosHeader.e_magic = VAL16(IMAGE_DOS_SIGNATURE); @@ -2096,9 +1828,6 @@ HRESULT PEWriter::fixup(CeeGenTokenMapper *pMapper) return(S_OK); // SUCCESS } -#ifdef _PREFAST_ -#pragma warning(pop) -#endif HRESULT PEWriter::Open(__in LPCWSTR fileName) { @@ -2188,25 +1917,15 @@ HRESULT PEWriter::write(__in LPCWSTR fileName) { HRESULT hr; - bool ExeOrDll; unsigned RoundUpVal; - ExeOrDll = isExeOrDll(m_ntHeaders); RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment); IfFailGo(Open(fileName)); - if(ExeOrDll) - { - // write the PE headers - IfFailGo(Write(&m_dosHeader, sizeof(IMAGE_DOS_HEADER))); - IfFailGo(Write(x86StubPgm, sizeof(x86StubPgm))); - IfFailGo(Write(m_ntHeaders, m_ntHeadersSize)); - } - else - { - // write the object file header - IfFailGo(Write(&(m_ntHeaders->FileHeader),sizeof(IMAGE_FILE_HEADER))); - } + // write the PE headers + IfFailGo(Write(&m_dosHeader, sizeof(IMAGE_DOS_HEADER))); + IfFailGo(Write(x86StubPgm, sizeof(x86StubPgm))); + IfFailGo(Write(m_ntHeaders, m_ntHeadersSize)); IfFailGo(Write(headers, (int)(sizeof(IMAGE_SECTION_HEADER)*(headersEnd-headers)))); @@ -2221,18 +1940,6 @@ HRESULT PEWriter::write(__in LPCWSTR fileName) { } } - // writes for an object file - if (!ExeOrDll) - { - // write the relocs section (Does nothing if relocs section is empty) - IfFailGo(reloc->write(m_file)); - //write string table (obj only, empty for exe or dll) - IfFailGo(strtab->write(m_file)); - int lena = padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), RoundUpVal); - if (lena > 0) - IfFailGo(Write(NULL, lena)); - } - return Close(); ErrExit: @@ -2243,19 +1950,11 @@ HRESULT PEWriter::write(__in LPCWSTR fileName) { HRESULT PEWriter::write(void ** ppImage) { - bool ExeOrDll = isExeOrDll(m_ntHeaders); const unsigned RoundUpVal = VAL32(m_ntHeaders->OptionalHeader.FileAlignment); char *pad = (char *) _alloca(RoundUpVal); memset(pad, 0, RoundUpVal); size_t lSize = filePos; - if (!ExeOrDll) - { - lSize += reloc->dataLen(); - lSize += strtab->dataLen(); - lSize += padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), - RoundUpVal); - } // allocate the block we are handing back to the caller void * pImage = (void *) ::CoTaskMemAlloc(lSize); @@ -2269,17 +1968,10 @@ HRESULT PEWriter::write(void ** ppImage) char *pCur = (char *)pImage; - if(ExeOrDll) - { - // PE Header - COPY_AND_ADVANCE(pCur, &m_dosHeader, sizeof(IMAGE_DOS_HEADER)); - COPY_AND_ADVANCE(pCur, x86StubPgm, sizeof(x86StubPgm)); - COPY_AND_ADVANCE(pCur, m_ntHeaders, m_ntHeadersSize); - } - else - { - COPY_AND_ADVANCE(pCur, &(m_ntHeaders->FileHeader), sizeof(IMAGE_FILE_HEADER)); - } + // PE Header + COPY_AND_ADVANCE(pCur, &m_dosHeader, sizeof(IMAGE_DOS_HEADER)); + COPY_AND_ADVANCE(pCur, x86StubPgm, sizeof(x86StubPgm)); + COPY_AND_ADVANCE(pCur, m_ntHeaders, m_ntHeadersSize); COPY_AND_ADVANCE(pCur, headers, sizeof(*headers)*(headersEnd - headers)); @@ -2295,26 +1987,6 @@ HRESULT PEWriter::write(void ** ppImage) } } - // !!! Need to jump to the right place... - - if (!ExeOrDll) - { - // now the relocs (exe, dll) or symbol table (obj) (if any) - // write the relocs section (Does nothing if relocs section is empty) - reloc->writeMem((void **)&pCur); - - //write string table (obj only, empty for exe or dll) - strtab->writeMem((void **)&pCur); - - // final pad - size_t len = padLen(VAL32(reloc->m_header->Misc.VirtualSize)+strtab->dataLen(), RoundUpVal); - if (len > 0) - { - // WARNING: macro - must enclose in curly braces - COPY_AND_ADVANCE(pCur, pad, len); - } - } - // make sure we wrote the exact numbmer of bytes expected _ASSERTE(lSize == (size_t) (pCur - (char *)pImage)); diff --git a/src/coreclr/dlls/mscorpe/pewriter.h b/src/coreclr/dlls/mscorpe/pewriter.h index ee56061bedec1..21817b1d5efc4 100644 --- a/src/coreclr/dlls/mscorpe/pewriter.h +++ b/src/coreclr/dlls/mscorpe/pewriter.h @@ -85,8 +85,6 @@ class PEWriter : public PESectionMan size_t getImageBase(); - void setEnCRvaBase(ULONG dataBase, ULONG rdataBase); - HRESULT getFileTimeStamp(DWORD *pTimeStamp); IMAGE_NT_HEADERS32* ntHeaders32() { return (IMAGE_NT_HEADERS32*) m_ntHeaders; } @@ -113,7 +111,6 @@ class PEWriter : public PESectionMan private: DWORD m_ilRVA; - BOOL m_encMode; ULONG m_dataRvaBase; ULONG m_rdataRvaBase; ULONG m_codeRvaBase; diff --git a/src/coreclr/dlls/mscorrc/CMakeLists.txt b/src/coreclr/dlls/mscorrc/CMakeLists.txt index 040b12a240a2a..c6618f0b77b23 100644 --- a/src/coreclr/dlls/mscorrc/CMakeLists.txt +++ b/src/coreclr/dlls/mscorrc/CMakeLists.txt @@ -15,7 +15,7 @@ if(CLR_CMAKE_HOST_WIN32) include.rc ) - install_clr(TARGETS mscorrc ADDITIONAL_DESTINATIONS sharedFramework) + install_clr(TARGETS mscorrc DESTINATIONS . sharedFramework COMPONENT runtime) else() build_resources(${CMAKE_CURRENT_SOURCE_DIR}/include.rc mscorrc TARGET_CPP_FILE) diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index e51040030dd15..a2a7b0ebefe1d 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -413,7 +413,7 @@ BEGIN IDS_INVALID_REDIM "Illegal attempt to replace or redimension a fixed or locked SafeArray." - IDS_INVALID_PINVOKE_CALLCONV "Invalid unmanaged calling convention: must be one of stdcall, cdecl, or thiscall." + IDS_INVALID_PINVOKE_CALLCONV "Unsupported unmanaged calling convention." IDS_CLASSLOAD_NSTRUCT_EXPLICIT_OFFSET "Could not load type '%1' from assembly '%2' because field '%3' was not given an explicit offset." IDS_WRONGSIZEARRAY_IN_NSTRUCT "Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout." diff --git a/src/coreclr/gc/CMakeLists.txt b/src/coreclr/gc/CMakeLists.txt index 3ba1e888eb401..a6c4073d0b148 100644 --- a/src/coreclr/gc/CMakeLists.txt +++ b/src/coreclr/gc/CMakeLists.txt @@ -110,7 +110,7 @@ convert_to_absolute_path(GC_SOURCES ${GC_SOURCES}) add_library_clr(clrgc SHARED ${GC_SOURCES}) add_dependencies(clrgc eventing_headers) target_link_libraries(clrgc ${GC_LINK_LIBRARIES}) -install_clr(TARGETS clrgc) +install_clr(TARGETS clrgc DESTINATIONS . COMPONENT runtime) if(CLR_CMAKE_HOST_UNIX) # dprintf causes many warnings (https://github.com/dotnet/runtime/issues/8737) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 5e1b2099a3865..8df0a78585992 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -3493,6 +3493,7 @@ void region_allocator::make_busy_block (uint32_t* index_start, uint32_t num_unit #ifdef _DEBUG dprintf (REGIONS_LOG, ("MBB[B: %Id] %d->%d", (size_t)num_units, (int)(index_start - region_map_start), (int)(index_start - region_map_start + num_units))); #endif //_DEBUG + ASSERT_HOLDING_SPIN_LOCK (®ion_allocator_lock); *index_start = num_units; } @@ -3501,6 +3502,7 @@ void region_allocator::make_free_block (uint32_t* index_start, uint32_t num_unit #ifdef _DEBUG dprintf (REGIONS_LOG, ("MFB[F: %Id] %d->%d", (size_t)num_units, (int)(index_start - region_map_start), (int)(index_start - region_map_start + num_units))); #endif //_DEBUG + ASSERT_HOLDING_SPIN_LOCK (®ion_allocator_lock); *index_start = region_alloc_free_bit | num_units; } @@ -3519,6 +3521,7 @@ void region_allocator::adjust_map (uint32_t* current_free_index_start, void region_allocator::print_map (const char* msg) { + ASSERT_HOLDING_SPIN_LOCK (®ion_allocator_lock); #ifdef _DEBUG const char* heap_type = "UH"; dprintf (REGIONS_LOG, ("[%s]-----printing----%s", heap_type, msg)); @@ -3550,6 +3553,8 @@ uint8_t* region_allocator::allocate_end (uint32_t num_units) { uint8_t* alloc = NULL; + ASSERT_HOLDING_SPIN_LOCK (®ion_allocator_lock); + if (global_region_used < global_region_end) { size_t end_remaining = global_region_end - global_region_used; @@ -3566,8 +3571,35 @@ uint8_t* region_allocator::allocate_end (uint32_t num_units) return alloc; } +void region_allocator::enter_spin_lock() +{ + while (true) + { + if (Interlocked::CompareExchange(®ion_allocator_lock.lock, 0, -1) < 0) + break; + + while (region_allocator_lock.lock >= 0) + { + YieldProcessor(); // indicate to the processor that we are spinning + } + } +#ifdef _DEBUG + region_allocator_lock.holding_thread = GCToEEInterface::GetThread(); +#endif //_DEBUG +} + +void region_allocator::leave_spin_lock() +{ + region_allocator_lock.lock = -1; +#ifdef _DEBUG + region_allocator_lock.holding_thread = (Thread*)-1; +#endif //_DEBUG +} + uint8_t* region_allocator::allocate (uint32_t num_units) { + enter_spin_lock(); + uint32_t* current_index = region_map_start; uint32_t* end_index = region_map_end; @@ -3607,6 +3639,9 @@ uint8_t* region_allocator::allocate (uint32_t num_units) total_free_units -= num_units; print_map ("alloc: found in free"); + + leave_spin_lock(); + return region_address_of (current_free_index_start); } } @@ -3647,6 +3682,8 @@ uint8_t* region_allocator::allocate (uint32_t num_units) dprintf (REGIONS_LOG, ("couldn't find memory at the end! only %Id bytes left", (global_region_end - global_region_used))); } + leave_spin_lock(); + return alloc; } @@ -3685,6 +3722,8 @@ bool region_allocator::allocate_large_region (uint8_t** start, uint8_t** end) void region_allocator::delete_region (uint8_t* start) { + enter_spin_lock(); + assert (is_region_aligned (start)); print_map ("before delete"); @@ -3710,6 +3749,8 @@ void region_allocator::delete_region (uint8_t* start) total_free_units += current_val; print_map ("after delete"); + + leave_spin_lock(); } #endif //USE_REGIONS @@ -4140,7 +4181,7 @@ class CObjectHeader : public Object _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment())); #endif // FEATURE_STRUCTALIGN -#ifdef FEATURE_64BIT_ALIGNMENT +#if defined(FEATURE_64BIT_ALIGNMENT) && !defined(FEATURE_REDHAWK) if (pMT->RequiresAlign8()) { _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U)); @@ -7620,11 +7661,11 @@ BOOL gc_heap::card_bundles_enabled () } #endif // CARD_BUNDLE -#if defined (TARGET_AMD64) +#if defined (HOST_64BIT) #define brick_size ((size_t)4096) #else #define brick_size ((size_t)2048) -#endif //TARGET_AMD64 +#endif //HOST_64BIT inline size_t gc_heap::brick_of (uint8_t* add) @@ -22994,18 +23035,19 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) if ((num_gen0_regions % pinning_seg_interval) == 0) { int align_const = get_alignment_constant (TRUE); - // Pinning the first object in the region. - uint8_t* obj_to_pin = heap_segment_mem (gen0_region); - pin_by_gc (obj_to_pin); - - obj_to_pin += Align (size (obj_to_pin), align_const); - // Pinning the middle object in the region. + // Pinning the first and the middle object in the region. + uint8_t* boundary = heap_segment_mem (gen0_region); + uint8_t* obj_to_pin = boundary; + int num_pinned_objs = 0; while (obj_to_pin < heap_segment_allocated (gen0_region)) { - if (obj_to_pin > region_mid) + if (obj_to_pin >= boundary && !((CObjectHeader*)obj_to_pin)->IsFree()) { pin_by_gc (obj_to_pin); - break; + num_pinned_objs++; + if (num_pinned_objs >= 2) + break; + boundary += (gen0_region_size / 2) + 1; } obj_to_pin += Align (size (obj_to_pin), align_const); } @@ -33532,6 +33574,9 @@ bool card_marking_enumerator::move_next(heap_segment* seg, uint8_t*& low, uint8_ low = (chunk_index_within_seg == 0) ? start : (aligned_start + (size_t)chunk_index_within_seg * CARD_MARKING_STEALING_GRANULARITY); high = (chunk_index_within_seg + 1 == chunk_count_within_seg) ? end : (aligned_start + (size_t)(chunk_index_within_seg + 1) * CARD_MARKING_STEALING_GRANULARITY); chunk_high = high; + + dprintf (3, ("cme:mn ci: %u, low: %Ix, high: %Ix", chunk_index, low, high)); + return true; } else @@ -33548,16 +33593,24 @@ bool card_marking_enumerator::move_next(heap_segment* seg, uint8_t*& low, uint8_ // keep the chunk index for later old_chunk_index = chunk_index; + + dprintf (3, ("cme:mn oci: %u, seg mismatch seg: %Ix, segment: %Ix", old_chunk_index, heap_segment_mem (segment), heap_segment_mem (seg))); + return false; } } segment = heap_segment_next_in_range(segment); + segment_start_chunk_index += chunk_count_within_seg; if (segment == nullptr) { + // keep the chunk index for later + old_chunk_index = chunk_index; + + dprintf (3, ("cme:mn oci: %u no more segments", old_chunk_index)); + return false; } - segment_start_chunk_index += chunk_count_within_seg; } } @@ -33739,7 +33792,10 @@ void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating CARD_ { // Switch to regions for this generation. seg = generation_start_segment (generation_of (curr_gen_number)); - dprintf (REGIONS_LOG, ("h%d switching to gen%d start seg %Ix", +#ifdef FEATURE_CARD_MARKING_STEALING + card_mark_enumerator.switch_to_segment(seg); +#endif // FEATURE_CARD_MARKING_STEALING + dprintf (REGIONS_LOG, ("h%d switching to gen%d start seg %Ix", heap_number, curr_gen_number, (size_t)seg)); } } diff --git a/src/coreclr/gc/gcdesc.h b/src/coreclr/gc/gcdesc.h index 8e58e2f7ce748..9b461a701a686 100644 --- a/src/coreclr/gc/gcdesc.h +++ b/src/coreclr/gc/gcdesc.h @@ -161,7 +161,7 @@ class CGCDesc // If it doesn't contain pointers, there isn't a GCDesc PTR_MethodTable mt(pMT); - _ASSERTE(mt->ContainsPointersOrCollectible()); + _ASSERTE(mt->ContainsPointers()); return PTR_CGCDesc(mt); } @@ -186,35 +186,49 @@ class CGCDesc return PTR_CGCDescSeries(PTR_size_t(PTR_CGCDesc(this))-1)-1; } - // Returns number of immediate pointers this object has. + // Returns number of immediate pointers this object has. It should match the number of + // pointers enumerated by go_through_object_cl macro. The implementation shape has intentional + // similarity with the go_through_object family of macros. // size is only used if you have an array of value types. #ifndef DACCESS_COMPILE static size_t GetNumPointers (MethodTable* pMT, size_t ObjectSize, size_t NumComponents) { size_t NumOfPointers = 0; - CGCDesc* map = GetCGCDescFromMT(pMT); - CGCDescSeries* cur = map->GetHighestSeries(); - ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries(); - if (cnt > 0) + if (pMT->ContainsPointers()) { - CGCDescSeries* last = map->GetLowestSeries(); - while (cur >= last) + CGCDesc* map = GetCGCDescFromMT(pMT); + CGCDescSeries* cur = map->GetHighestSeries(); + ptrdiff_t cnt = (ptrdiff_t)map->GetNumSeries(); + + if (cnt >= 0) { - NumOfPointers += (cur->GetSeriesSize() + ObjectSize) / sizeof(JSlot); - cur--; + CGCDescSeries* last = map->GetLowestSeries(); + do + { + NumOfPointers += (cur->GetSeriesSize() + ObjectSize) / sizeof(JSlot); + cur--; + } + while (cur >= last); } - } - else - { - /* Handle the repeating case - array of valuetypes */ - for (ptrdiff_t __i = 0; __i > cnt; __i--) + else { - NumOfPointers += cur->val_serie[__i].nptrs; + /* Handle the repeating case - array of valuetypes */ + for (ptrdiff_t __i = 0; __i > cnt; __i--) + { + NumOfPointers += cur->val_serie[__i].nptrs; + } + + NumOfPointers *= NumComponents; } + } - NumOfPointers *= NumComponents; +#ifndef FEATURE_REDHAWK + if (pMT->Collectible()) + { + NumOfPointers += 1; } +#endif return NumOfPointers; } diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index 0e9f0a18895fa..f8455ac323cd0 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -342,12 +342,12 @@ class recursive_gc_sync; #ifdef MULTIPLE_HEAPS // This feature hasn't been enabled for regions yet. -#ifndef USE_REGIONS +//#ifndef USE_REGIONS // card marking stealing only makes sense in server GC // but it works and is easier to debug for workstation GC // so turn it on for server GC, turn on for workstation GC if necessary #define FEATURE_CARD_MARKING_STEALING -#endif //!USE_REGIONS +//#endif //!USE_REGIONS #endif //MULTIPLE_HEAPS #ifdef FEATURE_CARD_MARKING_STEALING @@ -5329,6 +5329,11 @@ class region_allocator size_t region_alignment; size_t large_region_alignment; + GCSpinLock region_allocator_lock; + + void enter_spin_lock(); + void leave_spin_lock(); + uint32_t* region_map_start; uint32_t* region_map_end; @@ -5664,6 +5669,14 @@ class card_marking_enumerator ; } +#ifdef USE_REGIONS + void switch_to_segment(heap_segment* seg) + { + assert(segment == nullptr); + segment = seg; + } +#endif + uint8_t* get_chunk_high() { return chunk_high; diff --git a/src/coreclr/gc/sample/CMakeLists.txt b/src/coreclr/gc/sample/CMakeLists.txt index 8dbdca3dc5779..cf8c1694961e8 100644 --- a/src/coreclr/gc/sample/CMakeLists.txt +++ b/src/coreclr/gc/sample/CMakeLists.txt @@ -58,7 +58,7 @@ else() ../gcenv.unix.cpp) endif() -_add_executable(gcsample +add_executable_clr(gcsample ${SOURCES} ) diff --git a/src/coreclr/gc/unix/gcenv.unix.cpp b/src/coreclr/gc/unix/gcenv.unix.cpp index ecef15208124c..ba50e349badf2 100644 --- a/src/coreclr/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/gc/unix/gcenv.unix.cpp @@ -194,6 +194,24 @@ enum membarrier_cmd MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE = (1 << 6) }; +bool CanFlushUsingMembarrier() +{ + // Starting with Linux kernel 4.14, process memory barriers can be generated + // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. + + int mask = membarrier(MEMBARRIER_CMD_QUERY, 0); + + if (mask >= 0 && + mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED && + // Register intent to use the private expedited command. + membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0) == 0) + { + return true; + } + + return false; +} + // // Tracks if the OS supports FlushProcessWriteBuffers using membarrier // @@ -354,13 +372,7 @@ bool GCToOSInterface::Initialize() assert(s_flushUsingMemBarrier == 0); - // Starting with Linux kernel 4.14, process memory barriers can be generated - // using MEMBARRIER_CMD_PRIVATE_EXPEDITED. - int mask = membarrier(MEMBARRIER_CMD_QUERY, 0); - if (mask >= 0 && - mask & MEMBARRIER_CMD_PRIVATE_EXPEDITED && - // Register intent to use the private expedited command. - membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0) == 0) + if (CanFlushUsingMembarrier()) { s_flushUsingMemBarrier = TRUE; } diff --git a/src/coreclr/gc/windows/gcenv.windows.cpp b/src/coreclr/gc/windows/gcenv.windows.cpp index 53b868df6820a..ebaef4fee4a4f 100644 --- a/src/coreclr/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/gc/windows/gcenv.windows.cpp @@ -677,7 +677,6 @@ void GCToOSInterface::YieldThread(uint32_t switchCount) // Reserve virtual memory range. // Parameters: -// address - starting virtual address, it can be NULL to let the function choose the starting address // size - size of the virtual memory range // alignment - requested memory alignment, 0 means no specific alignment requested // flags - flags to control special settings like write watching diff --git a/src/coreclr/gcinfo/CMakeLists.txt b/src/coreclr/gcinfo/CMakeLists.txt index abdcd53fae3b7..70b0f7396d89b 100644 --- a/src/coreclr/gcinfo/CMakeLists.txt +++ b/src/coreclr/gcinfo/CMakeLists.txt @@ -39,7 +39,7 @@ add_library_clr(gcinfo_crossgen set_target_properties(gcinfo_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE) -_install (FILES gcinfoencoder.cpp +install (FILES gcinfoencoder.cpp DESTINATION gcinfo) function(create_gcinfo_lib) @@ -71,23 +71,14 @@ else() set(TARGET_OS_NAME win) endif() -if (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) - if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) - create_gcinfo_lib(TARGET gcinfo_unix_arm64 OS unix ARCH arm64) - create_gcinfo_lib(TARGET gcinfo_unix_x64 OS unix ARCH x64) - create_gcinfo_lib(TARGET gcinfo_win_arm64 OS win ARCH arm64) - create_gcinfo_lib(TARGET gcinfo_win_x64 OS win ARCH x64) - endif (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) - - create_gcinfo_lib(TARGET gcinfo_unix_armel OS unix ARCH armel) - create_gcinfo_lib(TARGET gcinfo_unix_arm OS unix ARCH arm) - create_gcinfo_lib(TARGET gcinfo_win_arm OS win ARCH arm) - create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) -else() - create_gcinfo_lib(TARGET gcinfo_${TARGET_OS_NAME}_${ARCH_TARGET_NAME} OS ${TARGET_OS_NAME} ARCH ${ARCH_TARGET_NAME}) - - if (CLR_CMAKE_HOST_ARCH_I386 AND NOT ((TARGET_OS_NAME STREQUAL unix) AND (ARCH_TARGET_NAME STREQUAL "armel"))) - # On x86, build gcinfo for RyuJIT/ARM32 cross-compiling altjit for ARM_SOFTFP (armel). - create_gcinfo_lib(TARGET gcinfo_unix_armel OS unix ARCH armel) - endif() -endif (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) +if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) + create_gcinfo_lib(TARGET gcinfo_unix_arm64 OS unix ARCH arm64) + create_gcinfo_lib(TARGET gcinfo_unix_x64 OS unix ARCH x64) + create_gcinfo_lib(TARGET gcinfo_win_arm64 OS win ARCH arm64) + create_gcinfo_lib(TARGET gcinfo_win_x64 OS win ARCH x64) +endif (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) + +create_gcinfo_lib(TARGET gcinfo_unix_armel OS unix ARCH armel) +create_gcinfo_lib(TARGET gcinfo_unix_arm OS unix ARCH arm) +create_gcinfo_lib(TARGET gcinfo_win_arm OS win ARCH arm) +create_gcinfo_lib(TARGET gcinfo_win_x86 OS win ARCH x86) diff --git a/src/coreclr/hosts/CMakeLists.txt b/src/coreclr/hosts/CMakeLists.txt index e7afb6db1de45..f3a3b0c8fbd0e 100644 --- a/src/coreclr/hosts/CMakeLists.txt +++ b/src/coreclr/hosts/CMakeLists.txt @@ -1,9 +1,9 @@ include_directories(inc) if(CLR_CMAKE_HOST_WIN32) - add_subdirectory(corerun) add_subdirectory(coreshim) else(CLR_CMAKE_HOST_WIN32) add_definitions(-D_FILE_OFFSET_BITS=64) - add_subdirectory(unixcorerun) endif(CLR_CMAKE_HOST_WIN32) + +add_subdirectory(corerun) diff --git a/src/coreclr/hosts/applydefines.pl b/src/coreclr/hosts/applydefines.pl deleted file mode 100644 index d8d4c318cb6fe..0000000000000 --- a/src/coreclr/hosts/applydefines.pl +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env perl - -use strict; - -my $sourceFile; -my $outputFile=""; -my $definesFile=""; - -#parse arguments - -if (@ARGV == 0) -{ - Usage(); -} - -my %Defines; - -# parse args - -while (@ARGV) -{ - my $nextArg=shift; - if($nextArg eq '-s') - { - NeedNextArg($nextArg, 'file name'); - $sourceFile=shift; - } - elsif ($nextArg eq '-o') - { - NeedNextArg($nextArg, 'file name'); - $outputFile=shift; - } - elsif ($nextArg eq '-f') - { - NeedNextArg($nextArg, 'file name'); - $definesFile=shift; - } - elsif ($nextArg eq '-d') - { - NeedNextArg($nextArg, 'value'); - my $customDefine=shift; - if ( $customDefine=~m/^\"?(\S+)=(\S*)\"?$/ ) - { - $Defines{$1}=$2; - } - else - { - print "-d expects name=value\n"; - Usage(); - } - } - elsif ($nextArg eq '-h') - { - Usage(); - } - else - { - print "Unknown argument '$nextArg'\n"; - Usage(); - } -} - -# check if we have what we need - -if ($sourceFile eq "" || $outputFile eq "" || $definesFile eq "") -{ - Usage(); -} - -open (SOURCEFILE,$sourceFile) or die "Cannot open $sourceFile for reading\n"; -open (DEFINESFILE,$definesFile) or die "Cannot open $definesFile for reading\n"; -open (OUTPUTFILE,"> $outputFile") or die "Cannot open $outputFile for writing\n"; - -#load defines - -while () -{ - chomp; - if (/^\s*#define\s+(\S+)\s+(\S*)\s*$/) - { - if (defined $2) - { - $Defines{$1}=$2; - } - else - { - $Defines{$1}=""; - } - } -} - -while () -{ - my $string=$_; - my $processed=""; - while ($string=~m/\$\(([^)]+)\)/) - { - if (! defined $Defines{$1}) - { - die "'$1' is not defined.\n"; - } - $string=~s/\$\(([^)]+)\)/$Defines{$1}/; - } - print OUTPUTFILE $string ; -} - - -# functions -sub Usage() -{ - print "Usage: applydefines [options]\n"; - print "\t-s \t: the source file to process\n"; - print "\t-f \t: the file containing #define settings\n"; - print "\t-o \t: the output file\n"; - print "\t-d =\t: additional define\n"; - - exit 1; -} - -sub NeedNextArg() -{ - if (@ARGV == 0) - { - print "'@_[0]' requires @_[1]\n"; - Usage(); - } -} - diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index f149bb9059e65..da545877ca318 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -1,21 +1,35 @@ -project(CoreRun) +project(corerun) set(CMAKE_INCLUDE_CURRENT_DIR ON) -add_definitions(-DFX_VER_INTERNALNAME_STR=CoreRun.exe) -_add_executable(CoreRun - corerun.cpp logger.cpp +if(CLR_CMAKE_HOST_WIN32) + add_definitions(-DFX_VER_INTERNALNAME_STR=corerun.exe) +else(CLR_CMAKE_HOST_WIN32) + include_directories("${CLR_SRC_NATIVE_DIR}/common") + include(configure.cmake) +endif(CLR_CMAKE_HOST_WIN32) + +add_executable_clr(corerun + corerun.cpp native.rc ) -target_link_libraries(CoreRun - utilcodestaticnohost - advapi32.lib - oleaut32.lib - uuid.lib - user32.lib - ${STATIC_MT_CRT_LIB} - ${STATIC_MT_VCRT_LIB} -) +if(CLR_CMAKE_HOST_WIN32) + target_link_libraries(corerun + advapi32.lib + oleaut32.lib + uuid.lib + user32.lib + ${STATIC_MT_CRT_LIB} + ${STATIC_MT_VCRT_LIB} + ) +else(CLR_CMAKE_HOST_WIN32) + target_link_libraries(corerun ${CMAKE_DL_LIBS}) + + # Android implements pthread natively + if(NOT CLR_CMAKE_TARGET_ANDROID) + target_link_libraries(corerun pthread) + endif() +endif(CLR_CMAKE_HOST_WIN32) -install_clr(TARGETS CoreRun) +install_clr(TARGETS corerun DESTINATIONS . COMPONENT runtime) diff --git a/src/coreclr/hosts/unixcorerun/config.h.in b/src/coreclr/hosts/corerun/config.h.in similarity index 100% rename from src/coreclr/hosts/unixcorerun/config.h.in rename to src/coreclr/hosts/corerun/config.h.in diff --git a/src/coreclr/hosts/unixcorerun/configure.cmake b/src/coreclr/hosts/corerun/configure.cmake similarity index 100% rename from src/coreclr/hosts/unixcorerun/configure.cmake rename to src/coreclr/hosts/corerun/configure.cmake diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 2db9d3606765c..63390bde8d510 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -1,544 +1,294 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// Runtime headers +#include -// -// .A simple CoreCLR host that runs on CoreSystem. -// - -#include "windows.h" -#include -#include "mscoree.h" -#include "coreclrhost.h" -#include -#include "palclr.h" -#include "sstring.h" - -// Utility macro for testing whether or not a flag is set. -#define HAS_FLAG(value, flag) (((value) & (flag)) == (flag)) - -// Environment variable for setting whether or not to use Server GC. -// Off by default. -static const wchar_t *serverGcVar = W("COMPlus_gcServer"); +#include "corerun.hpp" -// Environment variable for setting whether or not to use Concurrent GC. -// On by default. -static const wchar_t *concurrentGcVar = W("COMPlus_gcConcurrent"); +using char_t = pal::char_t; +using string_t = pal::string_t; -// The name of the CoreCLR native runtime DLL. -static const wchar_t *coreCLRDll = W("CoreCLR.dll"); - -// The location where CoreCLR is expected to be installed. If CoreCLR.dll isn't -// found in the same directory as the host, it will be looked for here. -static const wchar_t *coreCLRInstallDirectory = W("%windir%\\system32\\"); - -// Encapsulates the environment that CoreCLR will run in, including the TPALIST -class HostEnvironment +struct configuration { - // The path to this module - PathString m_hostPath; - - // The path to the directory containing this module - PathString m_hostDirectoryPath; - - // The name of this module, without the path - SString m_hostExeName; - - // The list of paths to the assemblies that will be trusted by CoreCLR - SString m_tpaList; - - coreclr_initialize_ptr m_CLRRuntimeHostInitialize; - - coreclr_execute_assembly_ptr m_CLRRuntimeHostExecute; - - coreclr_shutdown_2_ptr m_CLRRuntimeHostShutdown; - - HMODULE m_coreCLRModule; - - Logger *m_log; - - // Attempts to load CoreCLR.dll from the given directory. - // On success pins the dll, sets m_coreCLRDirectoryPath and returns the HMODULE. - // On failure returns nullptr. - HMODULE TryLoadCoreCLR(const wchar_t* directoryPath) { - - StackSString coreCLRPath(directoryPath); - coreCLRPath.Append(coreCLRDll); + configuration() = default; + configuration(const configuration&) = delete; + configuration(configuration&&) = delete; + configuration& operator=(const configuration&) = delete; + configuration& operator=(configuration&&) = delete; - *m_log << W("Attempting to load: ") << coreCLRPath.GetUnicode() << Logger::endl; - - HMODULE result = WszLoadLibraryEx(coreCLRPath, NULL, 0); - if (!result) { - *m_log << W("Failed to load: ") << coreCLRPath.GetUnicode() << Logger::endl; - *m_log << W("Error code: ") << GetLastError() << Logger::endl; - return nullptr; - } - - // Pin the module - CoreCLR.dll does not support being unloaded. - HMODULE dummy_coreCLRModule; - if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, coreCLRPath, &dummy_coreCLRModule)) { - *m_log << W("Failed to pin: ") << coreCLRPath.GetUnicode() << Logger::endl; - return nullptr; + ~configuration() + { + for (int i = 0; i < entry_assembly_argc; ++i) + { + ::free((void*)entry_assembly_argv[i]); } - - StackSString coreCLRLoadedPath; - WszGetModuleFileName(result, coreCLRLoadedPath); - - *m_log << W("Loaded: ") << coreCLRLoadedPath.GetUnicode() << Logger::endl; - - return result; + ::free(entry_assembly_argv); } -public: - // The path to the directory that CoreCLR is in - PathString m_coreCLRDirectoryPath; - - HostEnvironment(Logger *logger) - : m_CLRRuntimeHostInitialize(nullptr) - , m_CLRRuntimeHostExecute(nullptr) - , m_CLRRuntimeHostShutdown(nullptr) - , m_log(logger) { - - // Discover the path to this exe's module. All other files are expected to be in the same directory. - WszGetModuleFileName(::GetModuleHandleW(nullptr), m_hostPath); - - // Search for the last backslash in the host path. - SString::CIterator lastBackslash = m_hostPath.End(); - m_hostPath.FindBack(lastBackslash, W('\\')); - - // Copy the directory path - m_hostDirectoryPath.Set(m_hostPath, m_hostPath.Begin(), lastBackslash + 1); - - // Save the exe name - m_hostExeName = m_hostPath.GetUnicode(lastBackslash + 1); - - *m_log << W("Host directory: ") << m_hostDirectoryPath.GetUnicode() << Logger::endl; - - // Check for %CORE_ROOT% and try to load CoreCLR.dll from it if it is set - StackSString coreRoot; - m_coreCLRModule = NULL; // Initialize this here since we don't call TryLoadCoreCLR if CORE_ROOT is unset. - if (WszGetEnvironmentVariable(W("CORE_ROOT"), coreRoot) > 0 && coreRoot.GetCount() > 0) - { - coreRoot.Append(W('\\')); - m_coreCLRModule = TryLoadCoreCLR(coreRoot); - } - else - { - *m_log << W("CORE_ROOT not set; skipping") << Logger::endl; - *m_log << W("You can set the environment variable CORE_ROOT to point to the path") << Logger::endl; - *m_log << W("where CoreCLR.dll lives to help CoreRun.exe find it.") << Logger::endl; - } - - // Try to load CoreCLR from the directory that coreRun is in - if (!m_coreCLRModule) - { - m_coreCLRModule = TryLoadCoreCLR(m_hostDirectoryPath); - } + // + // Settings + // - if (!m_coreCLRModule) - { + // CLR path - user supplied location of coreclr binary and managed assemblies. + string_t clr_path; - // Failed to load. Try to load from the well-known location. - wchar_t coreCLRInstallPath[MAX_LONGPATH]; - ::ExpandEnvironmentStringsW(coreCLRInstallDirectory, coreCLRInstallPath, MAX_LONGPATH); - m_coreCLRModule = TryLoadCoreCLR(coreCLRInstallPath); + // The full path to the Supplied managed entry assembly. + string_t entry_assembly_fullpath; - } + // Arguments to pass to managed entry assembly. + int entry_assembly_argc; + const char_t** entry_assembly_argv; - if (m_coreCLRModule) - { + // Wait for debugger to be attached. + bool wait_to_debug; - // Save the directory that CoreCLR was found in - DWORD modulePathLength = WszGetModuleFileName(m_coreCLRModule, m_coreCLRDirectoryPath); + // Perform self test. + bool self_test; +}; - // Search for the last backslash and terminate it there to keep just the directory path with trailing slash - SString::Iterator lastBackslash = m_coreCLRDirectoryPath.End(); - m_coreCLRDirectoryPath.FindBack(lastBackslash, W('\\')); - m_coreCLRDirectoryPath.Truncate(lastBackslash + 1); +namespace envvar +{ + // Points to a path containing the CoreCLR binary. + const char_t* coreRoot = W("CORE_ROOT"); - m_CLRRuntimeHostInitialize = (coreclr_initialize_ptr)GetProcAddress(m_coreCLRModule, "coreclr_initialize"); - if (!m_CLRRuntimeHostInitialize) - { - *m_log << W("Failed to find function coreclr_initialize in ") << coreCLRDll << Logger::endl; - } + // Points to a path containing additional platform assemblies. + const char_t* coreLibraries = W("CORE_LIBRARIES"); - m_CLRRuntimeHostExecute = (coreclr_execute_assembly_ptr)GetProcAddress(m_coreCLRModule, "coreclr_execute_assembly"); - if (!m_CLRRuntimeHostExecute) - { - *m_log << W("Failed to find function coreclr_execute_assembly in ") << coreCLRDll << Logger::endl; - } + // Variable used to preload a mock hostpolicy for testing. + const char_t* mockHostPolicy = W("MOCK_HOSTPOLICY"); +} - m_CLRRuntimeHostShutdown = (coreclr_shutdown_2_ptr)GetProcAddress(m_coreCLRModule, "coreclr_shutdown_2"); - if (!m_CLRRuntimeHostShutdown) - { - *m_log << W("Failed to find function coreclr_shutdown_2 in ") << coreCLRDll << Logger::endl; - } - } - else - { - *m_log << W("Unable to load ") << coreCLRDll << Logger::endl; - } +static void wait_for_debugger() +{ + pal::debugger_state_t state = pal::is_debugger_attached(); + if (state == pal::debugger_state_t::na) + { + pal::fprintf(stdout, W("Debugger attach is not available on this platform\n")); + return; } - - bool TPAListContainsFile(_In_z_ wchar_t* fileNameWithoutExtension, _In_reads_(countExtensions) const wchar_t** rgTPAExtensions, int countExtensions) + else if (state == pal::debugger_state_t::not_attached) { - if (m_tpaList.IsEmpty()) return false; - - for (int iExtension = 0; iExtension < countExtensions; iExtension++) - { - StackSString fileName; - fileName.Append(W("\\")); // So that we don't match other files that end with the current file name - fileName.Append(fileNameWithoutExtension); - fileName.Append(rgTPAExtensions[iExtension] + 1); - fileName.Append(W(";")); // So that we don't match other files that begin with the current file name - - if (m_tpaList.Find(m_tpaList.Begin(), fileName)) - { - return true; - } - } - return false; + pal::fprintf(stdout, W("Waiting for the debugger to attach. Press any key to continue ...\n")); + (void)getchar(); + state = pal::is_debugger_attached(); } - void RemoveExtensionAndNi(_In_z_ wchar_t* fileName) + if (state == pal::debugger_state_t::attached) { - // Remove extension, if it exists - wchar_t* extension = wcsrchr(fileName, W('.')); - if (extension != NULL) - { - extension[0] = W('\0'); - - // Check for .ni - size_t len = wcslen(fileName); - if (len > 3 && - fileName[len - 1] == W('i') && - fileName[len - 2] == W('n') && - fileName[len - 3] == W('.') ) - { - fileName[len - 3] = W('\0'); - } - } + pal::fprintf(stdout, W("Debugger is attached.\n")); } - - void AddFilesFromDirectoryToTPAList(_In_z_ const wchar_t* targetPath, _In_reads_(countExtensions) const wchar_t** rgTPAExtensions, int countExtensions) + else { - *m_log << W("Adding assemblies from ") << targetPath << W(" to the TPA list") << Logger::endl; - StackSString assemblyPath; - const size_t dirLength = wcslen(targetPath); - - for (int iExtension = 0; iExtension < countExtensions; iExtension++) - { - assemblyPath.Set(targetPath, (DWORD)dirLength); - assemblyPath.Append(rgTPAExtensions[iExtension]); - WIN32_FIND_DATA data; - HANDLE findHandle = WszFindFirstFile(assemblyPath, &data); - - if (findHandle != INVALID_HANDLE_VALUE) { - do { - if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - // It seems that CoreCLR doesn't always use the first instance of an assembly on the TPA list (ni's may be preferred - // over il, even if they appear later). So, only include the first instance of a simple assembly name to allow - // users the opportunity to override Framework assemblies by placing dlls in %CORE_LIBRARIES% - - // ToLower for case-insensitive comparisons - wchar_t* fileNameChar = data.cFileName; - while (*fileNameChar) - { - *fileNameChar = towlower(*fileNameChar); - fileNameChar++; - } - - // Remove extension - wchar_t fileNameWithoutExtension[MAX_PATH_FNAME]; - wcscpy_s(fileNameWithoutExtension, MAX_PATH_FNAME, data.cFileName); - - RemoveExtensionAndNi(fileNameWithoutExtension); - - // Add to the list if not already on it - if (!TPAListContainsFile(fileNameWithoutExtension, rgTPAExtensions, countExtensions)) - { - assemblyPath.Truncate(assemblyPath.Begin() + (DWORD)dirLength); - assemblyPath.Append(data.cFileName); - m_tpaList.Append(assemblyPath); - m_tpaList.Append(W(';')); - } - else - { - *m_log << W("Not adding ") << targetPath << data.cFileName << W(" to the TPA list because another file with the same name is already present on the list") << Logger::endl; - } - } - } while (0 != WszFindNextFile(findHandle, &data)); - - FindClose(findHandle); - } - } + pal::fprintf(stdout, W("Debugger failed to attach.\n")); } +} - // Returns the semicolon-separated list of paths to runtime dlls that are considered trusted. - // On first call, scans the coreclr directory for dlls and adds them all to the list. - const SString& GetTpaList() { - if (m_tpaList.IsEmpty()) { - const wchar_t *rgTPAExtensions[] = { - W("*.ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir - W("*.dll"), - W("*.ni.exe"), - W("*.exe") - }; - - // Add files from %CORE_LIBRARIES% if specified - StackSString coreLibraries; - if (WszGetEnvironmentVariable(W("CORE_LIBRARIES"), coreLibraries) > 0 && coreLibraries.GetCount() > 0) - { - coreLibraries.Append(W('\\')); - AddFilesFromDirectoryToTPAList(coreLibraries, rgTPAExtensions, _countof(rgTPAExtensions)); - } - else - { - *m_log << W("CORE_LIBRARIES not set; skipping") << Logger::endl; - *m_log << W("You can set the environment variable CORE_LIBRARIES to point to a") << Logger::endl; - *m_log << W("path containing additional platform assemblies,") << Logger::endl; - } - - AddFilesFromDirectoryToTPAList(m_coreCLRDirectoryPath, rgTPAExtensions, _countof(rgTPAExtensions)); - } +// N.B. It seems that CoreCLR doesn't always use the first instance of an assembly on the TPA list +// (for example, ni's may be preferred over il, even if they appear later). Therefore, when building +// the TPA only include the first instance of a simple assembly name to allow users the opportunity to +// override Framework assemblies by placing dlls in %CORE_LIBRARIES%. +static string_t build_tpa(const string_t& core_root, const string_t& core_libraries) +{ + static const char_t* const tpa_extensions[] = + { + W(".ni.dll"), // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir + W(".dll"), + W(".ni.exe"), + W(".exe"), + nullptr + }; - return m_tpaList; - } + std::set name_set; + pal::stringstream_t tpa_list; - // Returns the path to the host module - const SString& GetHostPath() { - return m_hostPath; - } + // Iterate over all extensions. + for (const char_t* const* curr_ext = tpa_extensions; *curr_ext != nullptr; ++curr_ext) + { + const char_t* ext = *curr_ext; + const size_t ext_len = pal::strlen(ext); - // Returns the path to the host module - const SString& GetHostExeName() { - return m_hostExeName; - } + // Iterate over all supplied directories. + for (const string_t& dir : { core_libraries, core_root }) + { + if (dir.empty()) + continue; - bool IsHostLoaded() { - return m_coreCLRModule && m_CLRRuntimeHostInitialize && m_CLRRuntimeHostExecute && m_CLRRuntimeHostShutdown; - } + assert(dir.back() == pal::dir_delim); + string_t tmp = pal::build_file_list(dir, ext, [&](const char_t* file) + { + string_t file_local{ file }; - HRESULT InitializeHost( - const char *exePath, - const char *appDomainFriendlyName, - int propertyCount, - const char **propertyKeys, - const char **propertyValues, - void **hostHandle, - unsigned int *domainId) - { - if (m_CLRRuntimeHostInitialize) - { - return m_CLRRuntimeHostInitialize(exePath, appDomainFriendlyName, propertyCount, propertyKeys, propertyValues, hostHandle, domainId); - } - else - { - return E_FAIL; - } - } + // Strip the extension. + if (pal::string_ends_with(file_local, ext_len, ext)) + file_local = file_local.substr(0, file_local.length() - ext_len); - HRESULT ExecuteAssembly( - void *hostHandle, - unsigned int domainId, - int argc, - const char **argv, - const char *managedAssemblyPath, - unsigned int *exitCode) - { - if (m_CLRRuntimeHostExecute) - { - return m_CLRRuntimeHostExecute(hostHandle, domainId, argc, argv, managedAssemblyPath, exitCode); - } - else - { - return E_FAIL; - } - } + // Return true if the file is new. + return name_set.insert(file_local).second; + }); - HRESULT ShutdownHost( - void *hostHandle, - unsigned int domainId, - int *latchedExitCode) - { - if (m_CLRRuntimeHostShutdown) - { - return m_CLRRuntimeHostShutdown(hostHandle, domainId, latchedExitCode); - } - else - { - return E_FAIL; + // Add to the TPA. + tpa_list << tmp; } } -}; -// Creates the startup flags for the runtime, starting with the default startup -// flags and adding or removing from them based on environment variables. Only -// two environment variables are respected right now: serverGcVar, controlling -// Server GC, and concurrentGcVar, controlling Concurrent GC. -STARTUP_FLAGS CreateStartupFlags() { - auto initialFlags = - static_cast( - STARTUP_FLAGS::STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | - STARTUP_FLAGS::STARTUP_SINGLE_APPDOMAIN | - STARTUP_FLAGS::STARTUP_CONCURRENT_GC); - - // server GC is off by default, concurrent GC is on by default. - auto checkVariable = [&](STARTUP_FLAGS flag, const wchar_t *var) { - wchar_t result[25]; - size_t outsize; - if (_wgetenv_s(&outsize, result, 25, var) == 0 && outsize > 0) { - // set the flag if the var is present and set to 1, - // clear the flag if the var isp resent and set to 0. - // Otherwise, ignore it. - if (_wcsicmp(result, W("1")) == 0) { - initialFlags = static_cast(initialFlags | flag); - } else if (_wcsicmp(result, W("0")) == 0) { - initialFlags = static_cast(initialFlags & ~flag); - } - } - }; + return tpa_list.str(); +} - checkVariable(STARTUP_FLAGS::STARTUP_SERVER_GC, serverGcVar); - checkVariable(STARTUP_FLAGS::STARTUP_CONCURRENT_GC, concurrentGcVar); +static bool try_get_export(pal::mod_t mod, const char* symbol, void** fptr) +{ + assert(mod != nullptr && symbol != nullptr && fptr != nullptr); + *fptr = pal::get_module_symbol(mod, symbol); + if (*fptr != nullptr) + return true; - return initialFlags; + pal::fprintf(stderr, W("Export '%s' not found.\n"), symbol); + return false; } -// Class used to manage activation context. -// See: https://docs.microsoft.com/en-us/windows/desktop/SbsCs/using-the-activation-context-api -class ActivationContext +class logger_t final { + const char* _exePath; + int _propertyCount; + const char** _propertyKeys; + const char** _propertyValues; + const char* _managedAssembly; + int _argc; + const char** _argv; public: - // logger - Logger to record errors - // assemblyPath - Assembly containing activation context manifest - ActivationContext(Logger &logger, _In_z_ const WCHAR *assemblyPath) - : _logger{ logger } - , _actCxt{ INVALID_HANDLE_VALUE } - , _actCookie{} + logger_t( + const char* exePath, + int propertyCount, const char** propertyKeys, const char** propertyValues, + const char* managedAssembly, int argc, const char** argv) + : _exePath{ exePath } + , _propertyCount{ propertyCount } + , _propertyKeys{ propertyKeys } + , _propertyValues{ propertyValues } + , _managedAssembly{ managedAssembly } + , _argc{ argc } + , _argv{ argv } + { } + + void dump_details(FILE* fd = stdout) { - ACTCTX cxt{}; - cxt.cbSize = sizeof(cxt); - cxt.dwFlags = (ACTCTX_FLAG_APPLICATION_NAME_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID); - cxt.lpSource = assemblyPath; - cxt.lpResourceName = MAKEINTRESOURCEW(1); // The CreateProcess manifest which contains the context details - - _actCxt = ::CreateActCtxW(&cxt); - if (_actCxt == INVALID_HANDLE_VALUE) - { - DWORD err = ::GetLastError(); - if (err == ERROR_RESOURCE_TYPE_NOT_FOUND) - { - _logger << W("Assembly does not contain a manifest for activation") << Logger::endl; - } - else - { - _logger << W("Activation Context creation failed. Error Code: ") << Logger::hresult << err << Logger::endl; - } - } - else + // Using std::fprintf since values have been converted to UTF-8. + std::fprintf(fd, "Exe path: %s\n", _exePath); + std::fprintf(fd, "Properties:\n"); + for (int i = 0; i < _propertyCount; ++i) { - BOOL res = ::ActivateActCtx(_actCxt, &_actCookie); - if (res == FALSE) - _logger << W("Failed to activate Activation Context. Error Code: ") << Logger::hresult << ::GetLastError() << Logger::endl; + std::fprintf(fd, " %s = %s\n", _propertyKeys[i], _propertyValues[i]); } - } - ~ActivationContext() - { - if (_actCookie != ULONG_PTR{}) + std::fprintf(fd, "Managed assembly: %s\n", _managedAssembly); + std::fprintf(fd, "Arguments (%d): ", _argc); + for (int i = 0; i < _argc; ++i) { - BOOL res = ::DeactivateActCtx(0, _actCookie); - if (res == FALSE) - _logger << W("Failed to de-activate Activation Context. Error Code: ") << Logger::hresult << ::GetLastError() << Logger::endl; + std::fprintf(fd, "%s ", _argv[i]); } - - if (_actCxt != INVALID_HANDLE_VALUE) - ::ReleaseActCtx(_actCxt); + std::fprintf(fd, "\n"); } - -private: - Logger &_logger; - HANDLE _actCxt; - ULONG_PTR _actCookie; }; -class ClrInstanceDetails +// The current CoreCLR instance details. +static void* CurrentClrInstance; +static unsigned int CurrentAppDomainId; + +static int run(const configuration& config) { - static void * _currentClrInstance; - static unsigned int _currentAppDomainId; + platform_specific_actions actions; -public: // static - static HRESULT GetDetails(void **clrInstance, unsigned int *appDomainId) - { - *clrInstance = _currentClrInstance; - *appDomainId = _currentAppDomainId; - return S_OK; - } + // Check if debugger attach scenario was requested. + if (config.wait_to_debug) + wait_for_debugger(); -public: - ClrInstanceDetails(void *clrInstance, unsigned int appDomainId) + string_t exe_path = pal::get_exe_path(); + + // Determine the managed application's path. + string_t app_path; { - _currentClrInstance = clrInstance; - _currentAppDomainId = appDomainId; + string_t file; + pal::split_path_to_dir_filename(config.entry_assembly_fullpath, app_path, file); + pal::ensure_trailing_delimiter(app_path); } - ~ClrInstanceDetails() + // Define the NI app_path. + string_t app_path_ni = app_path + W("NI"); + pal::ensure_trailing_delimiter(app_path_ni); + app_path_ni.append(1, pal::env_path_delim); + app_path_ni.append(app_path); + + // Accumulate path for native search path. + pal::stringstream_t native_search_dirs; + native_search_dirs << app_path << pal::env_path_delim; + + // CORE_LIBRARIES + string_t core_libs = pal::getenv(envvar::coreLibraries); + if (!core_libs.empty() && core_libs != app_path) { - _currentClrInstance = nullptr; - _currentAppDomainId = 0; + pal::ensure_trailing_delimiter(core_libs); + native_search_dirs << core_libs << pal::env_path_delim; } -}; -void * ClrInstanceDetails::_currentClrInstance; -unsigned int ClrInstanceDetails::_currentAppDomainId; - -extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void **clrInstance, unsigned int *appDomainId) -{ - return ClrInstanceDetails::GetDetails(clrInstance, appDomainId); -} + // Determine CORE_ROOT. + // Check if the path is user supplied and if not try + // the CORE_ROOT environment variable. + string_t core_root = !config.clr_path.empty() + ? config.clr_path + : pal::getenv(envvar::coreRoot); -bool TryLoadHostPolicy(StackSString& hostPolicyPath) -{ - const WCHAR *hostpolicyName = W("hostpolicy.dll"); - HMODULE hMod = ::GetModuleHandleW(hostpolicyName); - if (hMod != nullptr) + // If CORE_ROOT wasn't supplied use the exe binary path, otherwise + // ensure path is valid and add to native search path. + if (core_root.empty()) { - return true; + string_t file; + pal::split_path_to_dir_filename(exe_path, core_root, file); + pal::ensure_trailing_delimiter(core_root); } - // Check if a hostpolicy exists and if it does, load it. - if (INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW(hostPolicyPath.GetUnicode())) + else { - hMod = ::LoadLibraryExW(hostPolicyPath.GetUnicode(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + pal::ensure_trailing_delimiter(core_root); + native_search_dirs << core_root << pal::env_path_delim; } - return hMod != nullptr; -} + string_t tpa_list = build_tpa(core_root, core_libs); -HRESULT InitializeHost( - Logger& log, - HostEnvironment& host, - const SString& appPath, - const SString& appNiPath, - const SString& nativeDllSearchDirs, - void **hostHandle, - unsigned int *domainId) -{ - StackScratchBuffer hostExeNameUTF8; - StackScratchBuffer tpaListUTF8; - StackScratchBuffer appPathUTF8; - StackScratchBuffer appNiPathUTF8; - StackScratchBuffer nativeDllSearchDirsUTF8; + { + // Load hostpolicy if requested. + string_t mock_hostpolicy = pal::getenv(envvar::mockHostPolicy); + if (!mock_hostpolicy.empty() + && !pal::try_load_hostpolicy(mock_hostpolicy)) + { + return -1; + } + } + + actions.before_coreclr_load(); - STARTUP_FLAGS flags = CreateStartupFlags(); + // Attempt to load CoreCLR. + pal::mod_t coreclr_mod; + if (!pal::try_load_coreclr(core_root, coreclr_mod)) + { + return -1; + } - //------------------------------------------------------------- + // Get CoreCLR exports + coreclr_initialize_ptr coreclr_init_func = nullptr; + coreclr_execute_assembly_ptr coreclr_execute_func = nullptr; + coreclr_shutdown_2_ptr coreclr_shutdown2_func = nullptr; + if (!try_get_export(coreclr_mod, "coreclr_initialize", (void**)&coreclr_init_func) + || !try_get_export(coreclr_mod, "coreclr_execute_assembly", (void**)&coreclr_execute_func) + || !try_get_export(coreclr_mod, "coreclr_shutdown_2", (void**)&coreclr_shutdown2_func)) + { + return -1; + } - // Initialize host. + // Construct CoreCLR properties. + pal::string_utf8_t tpa_list_utf8 = pal::convert_to_utf8(std::move(tpa_list)); + pal::string_utf8_t app_path_utf8 = pal::convert_to_utf8(std::move(app_path)); + pal::string_utf8_t app_path_ni_utf8 = pal::convert_to_utf8(std::move(app_path_ni)); + pal::string_utf8_t native_search_dirs_utf8 = pal::convert_to_utf8(native_search_dirs.str()); // Allowed property names: - // APPBASE - // - The base path of the application from which the exe and other assemblies will be loaded // // TRUSTED_PLATFORM_ASSEMBLIES // - The list of complete paths to each of the fully trusted assemblies @@ -551,308 +301,289 @@ HRESULT InitializeHost( // // NATIVE_DLL_SEARCH_DIRECTORIES // - The list of paths that will be probed for native DLLs called by PInvoke - // - const char* property_keys[] = { + const char* propertyKeys[] = + { "TRUSTED_PLATFORM_ASSEMBLIES", "APP_PATHS", "APP_NI_PATHS", "NATIVE_DLL_SEARCH_DIRECTORIES", - "System.GC.Server", - "System.GC.Concurrent" }; - const char* property_values[] = { + const char* propertyValues[] = + { // TRUSTED_PLATFORM_ASSEMBLIES - host.GetTpaList().GetUTF8(tpaListUTF8), + tpa_list_utf8.c_str(), // APP_PATHS - appPath.GetUTF8(appPathUTF8), + app_path_utf8.c_str(), // APP_NI_PATHS - appNiPath.GetUTF8(appNiPathUTF8), + app_path_ni_utf8.c_str(), // NATIVE_DLL_SEARCH_DIRECTORIES - nativeDllSearchDirs.GetUTF8(nativeDllSearchDirsUTF8), - // System.GC.Server - flags & STARTUP_SERVER_GC ? "true" : "false", - // System.GC.Concurrent - flags & STARTUP_CONCURRENT_GC ? "true" : "false" + native_search_dirs_utf8.c_str(), }; - log << W("Initialize host") << Logger::endl; - for (int idx = 0; idx < ARRAYSIZE(property_keys); idx++) - { - log << property_keys[idx] << W("=") << property_values[idx] << Logger::endl; - } - - HRESULT hr = host.InitializeHost( - NULL, - host.GetHostExeName().GetUTF8(hostExeNameUTF8), - ARRAYSIZE(property_keys), - property_keys, - property_values, - hostHandle, - domainId); - - if (FAILED(hr)) + int propertyCount = (int)(sizeof(propertyKeys) / sizeof(propertyKeys[0])); + + // Construct arguments + pal::string_utf8_t exe_path_utf8 = pal::convert_to_utf8(std::move(exe_path)); + std::vector argv_lifetime; + pal::malloc_ptr argv_utf8{ pal::convert_argv_to_utf8(config.entry_assembly_argc, config.entry_assembly_argv, argv_lifetime) }; + pal::string_utf8_t entry_assembly_utf8 = pal::convert_to_utf8(config.entry_assembly_fullpath.c_str()); + + logger_t logger{ + exe_path_utf8.c_str(), + propertyCount, propertyKeys, propertyValues, + entry_assembly_utf8.c_str(), config.entry_assembly_argc, argv_utf8.get() }; + + int result; + result = coreclr_init_func( + exe_path_utf8.c_str(), + "corerun", + propertyCount, + propertyKeys, + propertyValues, + &CurrentClrInstance, + &CurrentAppDomainId); + if (FAILED(result)) { - log << W("Failed to initialize host. ERRORCODE: ") << Logger::hresult << hr << Logger::endl; + pal::fprintf(stderr, W("BEGIN: coreclr_initialize failed - Error: 0x%08x\n"), result); + logger.dump_details(); + pal::fprintf(stderr, W("END: coreclr_initialize failed - Error: 0x%08x\n"), result); + return -1; } - return hr; -} - -HRESULT ExecuteAssembly( - Logger& log, - HostEnvironment& host, - const SString& managedAssemblyFullName, - void *hostHandle, - unsigned int domainId, - int argc, - const wchar_t *argv[], - unsigned int *exitCode) -{ - NewArrayHolder argvUTF8Holder; - NewArrayHolder argvUTF8Values; - if (argc && argv) + int exit_code; { - argvUTF8Holder = new (nothrow) const char*[argc]; - argvUTF8Values = new (nothrow) SString[argc]; - if (argvUTF8Holder.GetValue() && argvUTF8Values.GetValue()) + actions.before_execute_assembly(config.entry_assembly_fullpath); + + result = coreclr_execute_func( + CurrentClrInstance, + CurrentAppDomainId, + config.entry_assembly_argc, + argv_utf8.get(), + entry_assembly_utf8.c_str(), + (uint32_t*)&exit_code); + if (FAILED(result)) { - StackSString conversionBuffer; - for (int i = 0; i < argc; i++) - { - conversionBuffer.Set(argv[i]); - conversionBuffer.ConvertToUTF8(argvUTF8Values[i]); - argvUTF8Holder[i] = argvUTF8Values[i].GetUTF8NoConvert(); - } + pal::fprintf(stderr, W("BEGIN: coreclr_execute_assembly failed - Error: 0x%08x\n"), result); + logger.dump_details(); + pal::fprintf(stderr, W("END: coreclr_execute_assembly failed - Error: 0x%08x\n"), result); + return -1; } - } - ActivationContext cxt{ log, managedAssemblyFullName.GetUnicode() }; - ClrInstanceDetails current{ hostHandle, domainId }; - - log << W("Executing assembly: ") << managedAssemblyFullName << Logger::endl; + actions.after_execute_assembly(); + } - StackScratchBuffer managedAssemblyFullNameUTF8; - HRESULT hr = host.ExecuteAssembly(hostHandle, domainId, argc, argvUTF8Holder.GetValue(), managedAssemblyFullName.GetUTF8(managedAssemblyFullNameUTF8), exitCode); - if (FAILED(hr)) + int latched_exit_code = 0; + result = coreclr_shutdown2_func(CurrentClrInstance, CurrentAppDomainId, &latched_exit_code); + if (FAILED(result)) { - log << W("Failed call to ExecuteAssembly. ERRORCODE: ") << Logger::hresult << hr << Logger::endl; + pal::fprintf(stderr, W("coreclr_shutdown_2 failed - Error: 0x%08x\n"), result); + exit_code = -1; } - return hr; + if (exit_code != -1) + exit_code = latched_exit_code; + + return exit_code; } -HRESULT ShutdownHost( - Logger& log, - HostEnvironment& host, - void *hostHandle, - unsigned int domainId, - unsigned int *exitCode) +// Display the command line options +static void display_usage() { - log << W("Shutting down host") << Logger::endl; - - HRESULT hr = host.ShutdownHost(hostHandle, domainId, (int *)exitCode); - if (FAILED(hr)) - { - log << W("Failed to shutdown host. ERRORCODE: ") << Logger::hresult << hr << Logger::endl; - } - - return hr; + pal::fprintf( + stderr, + W("USAGE: corerun [OPTIONS] assembly [ARGUMENTS]\n") + W("\n") + W("Execute the managed assembly with the passed in arguments\n") + W("\n") + W("Options:\n") + W(" -c, --clr-path - path to CoreCLR binary and managed CLR assemblies\n") + W(" -d, --debug - causes corerun to wait for a debugger to attach before executing\n") + W(" -?, -h, --help - show this help\n") + W("\n") + W("CoreCLR is searched for in %%CORE_ROOT%%, then in the directory\n") + W("the corerun binary is located.\n")); } -bool TryRun(const int argc, const wchar_t* argv[], Logger &log, const bool verbose, const bool waitForDebugger, DWORD &exitCode) +// Parse the command line arguments +static bool parse_args( + const int argc, + const char_t* argv[], + configuration& config) { - // Assume failure - exitCode = -1; - - HostEnvironment hostEnvironment(&log); - - //------------------------------------------------------------- - - // Find the specified exe. This is done using LoadLibrary so that - // the OS library search semantics are used to find it. - - const wchar_t* exeName = argc > 0 ? argv[0] : nullptr; - if(exeName == nullptr) + // The command line must contain at least the current exe name and the managed assembly path. + if (argc < 2) { - log << W("No exename specified.") << Logger::endl; + display_usage(); return false; } - StackSString appPath; - StackSString appNiPath; - StackSString managedAssemblyFullName; - - wchar_t* filePart = NULL; - - COUNT_T size = MAX_LONGPATH; - wchar_t* appPathPtr = appPath.OpenUnicodeBuffer(size - 1); - DWORD length = WszGetFullPathName(exeName, size, appPathPtr, &filePart); - if (length >= size) - { - appPath.CloseBuffer(); - size = length; - appPathPtr = appPath.OpenUnicodeBuffer(size - 1); - length = WszGetFullPathName(exeName, size, appPathPtr, &filePart); - } - if (length == 0 || length >= size || filePart == NULL) + for (int i = 1; i < argc; i++) { - log << W("Failed to get full path: ") << exeName << Logger::endl; - log << W("Error code: ") << GetLastError() << Logger::endl; - return false; - } - - managedAssemblyFullName.Set(appPathPtr); + bool is_option = pal::is_cli_option(argv[i][0]); - *(filePart) = W('\0'); - appPath.CloseBuffer(DWORD(filePart - appPathPtr)); - - log << W("Loading: ") << managedAssemblyFullName.GetUnicode() << Logger::endl; + // First argument that is not an option is the managed assembly to execute. + if (!is_option) + { + config.entry_assembly_fullpath = pal::get_absolute_path(argv[i]); + i++; // Move to next argument. - appNiPath.Set(appPath); - appNiPath.Append(W("NI")); - appNiPath.Append(W(";")); - appNiPath.Append(appPath); + config.entry_assembly_argc = argc - i; + config.entry_assembly_argv = (const char_t**)::malloc(config.entry_assembly_argc * sizeof(const char_t*)); + assert(config.entry_assembly_argv != nullptr); + for (int c = 0; c < config.entry_assembly_argc; ++c) + { + config.entry_assembly_argv[c] = pal::strdup(argv[i + c]); + } - // Construct native search directory paths - StackSString nativeDllSearchDirs(appPath); - StackSString coreLibraries; - if (WszGetEnvironmentVariable(W("CORE_LIBRARIES"), coreLibraries) > 0 && coreLibraries.GetCount() > 0) - { - nativeDllSearchDirs.Append(W(";")); - nativeDllSearchDirs.Append(coreLibraries); - } - nativeDllSearchDirs.Append(W(";")); - nativeDllSearchDirs.Append(hostEnvironment.m_coreCLRDirectoryPath); + // Successfully parsed arguments. + return true; + } - // Preload mock hostpolicy if requested. - StackSString hostpolicyPath; - if (WszGetEnvironmentVariable(W("MOCK_HOSTPOLICY"), hostpolicyPath) > 0 && hostpolicyPath.GetCount() > 0) - { - if (!TryLoadHostPolicy(hostpolicyPath)) + const char_t* arg = argv[i]; + size_t arg_len = pal::strlen(arg); + if (arg_len == 1) { - log << W("Unable to load requested mock hostpolicy."); - return false; + pal::fprintf(stderr, W("Option %s: invalid form\n"), arg); + break; // Invalid option } - } - - if (!hostEnvironment.IsHostLoaded()) - { - return false; - } - HRESULT hr = E_FAIL; - void* hostHandle = nullptr; - DWORD domainId = 0; - - hr = InitializeHost(log, hostEnvironment, appPath, appNiPath, nativeDllSearchDirs, &hostHandle, (unsigned int*)&domainId); - if (FAILED(hr)) - { - exitCode = hr; - return false; - } + const char_t* option = arg + 1; + if (option[0] == W('-')) // Handle double '--' + option++; - if(waitForDebugger) - { - if(!IsDebuggerPresent()) + // Path to core_root + if (pal::strcmp(option, W("c")) == 0 || (pal::strcmp(option, W("clr-path")) == 0)) { - log << W("Waiting for the debugger to attach. Press any key to continue ...") << Logger::endl; - getchar(); - if (IsDebuggerPresent()) + i++; + if (i < argc) { - log << "Debugger is attached." << Logger::endl; + config.clr_path = argv[i]; } else { - log << "Debugger failed to attach." << Logger::endl; + pal::fprintf(stderr, W("Option %s: missing path\n"), arg); + break; } } + else if ((pal::strcmp(option, W("d")) == 0 || (pal::strcmp(option, W("debug")) == 0))) + { + config.wait_to_debug = true; + } + else if (pal::strcmp(option, W("st")) == 0) + { + config.self_test = true; + return true; + } + else if ((pal::strcmp(option, W("?")) == 0 || (pal::strcmp(option, W("h")) == 0 || (pal::strcmp(option, W("help")) == 0)))) + { + display_usage(); + break; + } + else + { + pal::fprintf(stderr, W("Unknown option %s\n"), arg); + break; + } } - hr = ExecuteAssembly(log, hostEnvironment, managedAssemblyFullName, hostHandle, domainId, argc - 1, (argc - 1) ? &(argv[1]) : NULL, (unsigned int*)&exitCode); - if (FAILED(hr)) - { - return false; - } + return false; +} - log << W("App exit value = ") << exitCode << Logger::endl; +// Forward declaration for self testing method. +static int self_test(); - hr = ShutdownHost(log, hostEnvironment, hostHandle, domainId, (unsigned int*)&exitCode); - if (FAILED(hr)) - { - return false; - } +// +// Entry points +// - return true; -} +int MAIN(const int argc, const char_t* argv[]) +{ + configuration config{}; + if (!parse_args(argc, argv, config)) + return EXIT_FAILURE; -void showHelp() { - ::wprintf( - W("Runs executables on CoreCLR\r\n") - W("\r\n") - W("USAGE: coreRun [/d] [/v] Managed.exe\r\n") - W("\r\n") - W(" where Managed.exe is a managed executable built for CoreCLR\r\n") - W(" /v causes verbose output to be written to the console\r\n") - W(" /d causes coreRun to wait for a debugger to attach before\r\n") - W(" launching Managed.exe\r\n") - W("\r\n") - W(" CoreCLR is searched for in %%core_root%%, then in the directory\r\n") - W(" that coreRun.exe is in, then finally in %s.\r\n"), - coreCLRInstallDirectory - ); + if (config.self_test) + return self_test(); + + int exit_code = run(config); + return exit_code; } -int __cdecl wmain(const int argc, const wchar_t* argv[]) +#ifdef TARGET_WINDOWS +// Used by CoreShim to determine running CoreCLR details. +extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId) { - // Parse the options from the command line - - bool verbose = false; - bool waitForDebugger = false; - bool helpRequested = false; - int newArgc = argc - 1; - const wchar_t **newArgv = argv + 1; + assert(clrInstance != nullptr && appDomainId != nullptr); + *clrInstance = CurrentClrInstance; + *appDomainId = CurrentAppDomainId; + return S_OK; +} +#endif // TARGET_WINDOWS - auto stringsEqual = [](const wchar_t * const a, const wchar_t * const b) -> bool { - return ::_wcsicmp(a, b) == 0; - }; +// +// Self testing for corerun. +// - auto tryParseOption = [&](const wchar_t* arg) -> bool { - if ( stringsEqual(arg, W("/v")) || stringsEqual(arg, W("-v")) ) { - verbose = true; - return true; - } else if ( stringsEqual(arg, W("/d")) || stringsEqual(arg, W("-d")) ) { - waitForDebugger = true; - return true; - } else if ( stringsEqual(arg, W("/?")) || stringsEqual(arg, W("-?")) || stringsEqual(arg, W("-h")) || stringsEqual(arg, W("--help")) ) { - helpRequested = true; - return true; - } else { - return false; +#define THROW_IF_FALSE(stmt) if (!(stmt)) throw W(#stmt); +static int self_test() +{ + try + { + { + configuration config{}; + const char_t* args[] = { W(""), W("-d"), W("foo") }; + THROW_IF_FALSE(parse_args(3, args, config)); + THROW_IF_FALSE(config.wait_to_debug); + THROW_IF_FALSE(config.clr_path.empty()); + THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); + THROW_IF_FALSE(config.entry_assembly_argc == 0); } - }; - - while (newArgc > 0 && tryParseOption(newArgv[0])) { - newArgc--; - newArgv++; - } - - if (argc < 2 || helpRequested || newArgc==0) { - showHelp(); - return -1; - } else { - Logger log; - if (verbose) { - log.Enable(); - } else { - log.Disable(); + { + configuration config{}; + const char_t* args[] = { W(""), W("-d"), W("foo"), W("1"), W("2"), W("3") }; + THROW_IF_FALSE(parse_args(6, args, config)); + THROW_IF_FALSE(config.wait_to_debug); + THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); + THROW_IF_FALSE(config.entry_assembly_argc == 3); } - - DWORD exitCode; - auto success = TryRun(newArgc, newArgv, log, verbose, waitForDebugger, exitCode); - - log << W("Execution ") << (success ? W("succeeded") : W("failed")) << Logger::endl; - - return exitCode; + { + configuration config{}; + const char_t* args[] = { W(""), W("--clr-path"), W("path"), W("foo"), W("1") }; + THROW_IF_FALSE(parse_args(5, args, config)); + THROW_IF_FALSE(!config.wait_to_debug); + THROW_IF_FALSE(config.clr_path == W("path")); + THROW_IF_FALSE(!config.entry_assembly_fullpath.empty()); + THROW_IF_FALSE(config.entry_assembly_argc == 1); + } + { + string_t path; + path = W("path"); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim); + path = W(""); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim); + path = W("\\"); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim || path.length() == 1); + path = W("/"); + pal::ensure_trailing_delimiter(path); + THROW_IF_FALSE(path.back() == pal::dir_delim || path.length() == 1); + } + { + THROW_IF_FALSE(!pal::string_ends_with(W(""), W(".cd"))); + THROW_IF_FALSE(pal::string_ends_with(W("ab.cd"), W(".cd"))); + THROW_IF_FALSE(!pal::string_ends_with(W("ab.cd"), W(".cde"))); + THROW_IF_FALSE(!pal::string_ends_with(W("ab.cd"), W("ab.cde"))); + } + } + catch (const char_t msg[]) + { + pal::fprintf(stderr, W("Fail: %s\n"), msg); + return EXIT_FAILURE; } + + pal::fprintf(stdout, W("Self-test passed.\n")); + return EXIT_SUCCESS; } diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp new file mode 100644 index 0000000000000..af9cc1e80fb58 --- /dev/null +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -0,0 +1,670 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef __CORERUN_HPP__ +#define __CORERUN_HPP__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// Class used to perform specific actions. +class platform_specific_actions; + +// +// Platform abstraction layer +// + +namespace pal +{ + // Handle to a loaded module + using mod_t = void*; + + struct free_delete + { + void operator()(void* x) { ::free(x); } + }; + + template + using malloc_ptr = std::unique_ptr; + + enum class debugger_state_t + { + na, + attached, + not_attached, + }; +} + +#ifdef TARGET_WINDOWS +#include + +#define MAIN __cdecl wmain +#define W(str) L ## str + +namespace pal +{ + using char_t = wchar_t; + using string_t = std::basic_string; + using stringstream_t = std::basic_stringstream; + using string_utf8_t = std::basic_string; + + const char_t dir_delim = W('\\'); + const char_t env_path_delim = W(';'); + const char_t nativelib_ext[] = W(".dll"); + const char_t coreclr_lib[] = W("coreclr"); + + int strcmp(const char_t* str1, const char_t* str2) { return wcscmp(str1, str2); } + size_t strlen(const char_t* str) { return wcslen(str); } + char_t* strdup(const char_t* str) { return ::_wcsdup(str); } + int fprintf(FILE* fd, const char_t* const fmt, ...) + { + va_list args; + va_start(args, fmt); + int ret = ::vfwprintf(fd, fmt, args); + va_end(args); + return ret; + } + bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-') || option_maybe == W('/'); } + string_t getenv(const char_t* var) + { + DWORD needed = ::GetEnvironmentVariableW(var, nullptr, 0); + if (needed == 0) + return {}; + + malloc_ptr buffer{ (char_t*)::malloc(needed * sizeof(char_t)) }; + assert(buffer != nullptr); + DWORD wrote = ::GetEnvironmentVariableW(var, buffer.get(), needed); + assert(wrote < needed); + return { buffer.get() }; + } + string_t get_exe_path() + { + char_t file_name[1024]; + DWORD count = ::GetModuleFileNameW(nullptr, file_name, ARRAYSIZE(file_name)); + assert(::GetLastError() != ERROR_INSUFFICIENT_BUFFER); + + return { file_name }; + } + string_t get_absolute_path(const char_t* path) + { + DWORD needed = ::GetFullPathNameW(path, 0, nullptr, nullptr); + malloc_ptr buffer{ (char_t*)::malloc(needed * sizeof(char_t)) }; + assert(buffer != nullptr); + + DWORD wrote = ::GetFullPathNameW(path, needed, buffer.get(), nullptr); + assert(wrote < needed); + return { buffer.get() }; + } + + debugger_state_t is_debugger_attached() + { + return (::IsDebuggerPresent() == TRUE) ? debugger_state_t::attached : debugger_state_t::not_attached; + } + + bool does_file_exist(const string_t& file_path) + { + return INVALID_FILE_ATTRIBUTES != ::GetFileAttributesW(file_path.c_str()); + } + + // Forward declaration + void ensure_trailing_delimiter(pal::string_t& dir); + + string_t build_file_list( + const string_t& dir, + const char_t* ext, + std::function should_add) + { + assert(ext != nullptr); + + string_t dir_local = dir; + dir_local.append(W("*")); + dir_local.append(ext); + + WIN32_FIND_DATA data; + HANDLE findHandle = ::FindFirstFileW(dir_local.data(), &data); + if (findHandle == INVALID_HANDLE_VALUE) + return {}; + + stringstream_t file_list; + do + { + if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + // ToLower for case-insensitive comparisons + char_t* fileNameChar = data.cFileName; + while (*fileNameChar) + { + *fileNameChar = towlower(*fileNameChar); + fileNameChar++; + } + + if (should_add(data.cFileName)) + file_list << dir << data.cFileName << env_path_delim; + } + } while (FALSE != ::FindNextFileW(findHandle, &data)); + + ::FindClose(findHandle); + + return file_list.str(); + } + + void* get_module_symbol(mod_t m, const char* sym) + { + assert(m != nullptr && sym != nullptr); + return ::GetProcAddress((HMODULE)m, sym); + } + + string_utf8_t convert_to_utf8(const char_t* str) + { + // Compute the needed buffer + int bytes_req = ::WideCharToMultiByte( + CP_UTF8, 0, // Conversion args + str, -1, // Input string + nullptr, 0, // Null to request side + nullptr, nullptr); + + malloc_ptr buffer{ (char*)::malloc(bytes_req) }; + assert(buffer != nullptr); + + int written = ::WideCharToMultiByte( + CP_UTF8, 0, // Conversion args + str, -1, // Input string + buffer.get(), bytes_req, // Output buffer + nullptr, nullptr); + assert(bytes_req == written); + + return { buffer.get() }; + } + + string_utf8_t convert_to_utf8(string_t&& str) + { + return convert_to_utf8(str.c_str()); + } + + bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value) + { + const char_t* hostpolicyName = W("hostpolicy.dll"); + pal::mod_t hMod = (pal::mod_t)::GetModuleHandleW(hostpolicyName); + if (hMod != nullptr) + return true; + + // Check if a hostpolicy exists and if it does, load it. + if (pal::does_file_exist(mock_hostpolicy_value)) + hMod = (pal::mod_t)::LoadLibraryExW(mock_hostpolicy_value.c_str(), nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + + if (hMod == nullptr) + pal::fprintf(stderr, W("Failed to load mock hostpolicy at path '%s'. Error: 0x%08x\n"), mock_hostpolicy_value.c_str(), ::GetLastError()); + + return hMod != nullptr; + } + + bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod) + { + pal::string_t coreclr_path = core_root; + pal::ensure_trailing_delimiter(coreclr_path); + coreclr_path.append(pal::coreclr_lib); + coreclr_path.append(pal::nativelib_ext); + + hMod = (pal::mod_t)::LoadLibraryExW(coreclr_path.c_str(), nullptr, 0); + if (hMod == nullptr) + { + pal::fprintf(stderr, W("Failed to load: '%s'. Error: 0x%08x\n"), coreclr_path.c_str(), ::GetLastError()); + return false; + } + + // Pin the module - CoreCLR.dll does not support being unloaded. + HMODULE unused; + if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, coreclr_path.c_str(), &unused)) + { + pal::fprintf(stderr, W("Failed to pin: '%s'. Error: 0x%08x\n"), coreclr_path.c_str(), ::GetLastError()); + return false; + } + + return true; + } +} + +class platform_specific_actions final +{ + HANDLE _actCxt; + ULONG_PTR _actCookie; + +public: + platform_specific_actions() = default; + + void before_coreclr_load() { } + + void before_execute_assembly(const pal::string_t& assembly) + { + ACTCTX cxt{}; + cxt.cbSize = sizeof(cxt); + cxt.dwFlags = (ACTCTX_FLAG_APPLICATION_NAME_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID); + cxt.lpSource = assembly.c_str(); + cxt.lpResourceName = MAKEINTRESOURCEW(1); // The CreateProcess manifest which contains the context details + + _actCxt = ::CreateActCtxW(&cxt); + if (_actCxt == INVALID_HANDLE_VALUE) + { + _actCookie = ULONG_PTR{}; + DWORD err = ::GetLastError(); + if (err != ERROR_RESOURCE_TYPE_NOT_FOUND && err != ERROR_RESOURCE_DATA_NOT_FOUND) + pal::fprintf(stderr, W("Activation Context creation failed. Error: 0x%08x\n"), err); + } + else + { + BOOL res = ::ActivateActCtx(_actCxt, &_actCookie); + if (res == FALSE) + pal::fprintf(stderr, W("Failed to activate Activation Context. Error: 0x%08x\n"), ::GetLastError()); + } + } + + void after_execute_assembly() + { + if (_actCookie != ULONG_PTR{}) + { + BOOL res = ::DeactivateActCtx(0, _actCookie); + if (res == FALSE) + pal::fprintf(stderr, W("Failed to de-activate Activation Context. Error: 0x%08x\n"), ::GetLastError()); + } + + if (_actCxt != INVALID_HANDLE_VALUE) + ::ReleaseActCtx(_actCxt); + } +}; + +#else // !TARGET_WINDOWS +#include +#include +#include +#include +#include + +// Needed for detecting the debugger attach scenario +#if defined(__APPLE__) +#include +#include +#else // !__APPLE__ +#include +#include +#endif // !__APPLE__ + +// CMake generated +#include +#include + +#define MAIN main +#define W(str) str +#define FAILED(result) (result < 0) + +#if !HAVE_DIRENT_D_TYPE +#define DT_UNKNOWN 0 +#define DT_DIR 4 +#define DT_REG 8 +#define DT_LNK 10 +#endif + +namespace pal +{ + using char_t = char; + using string_t = std::basic_string; + using stringstream_t = std::basic_stringstream; + using string_utf8_t = std::basic_string; + + const char_t dir_delim = W('/'); + const char_t env_path_delim = W(':'); + +#if defined(__APPLE__) + const char_t nativelib_ext[] = W(".dylib"); +#else // Various Linux-related OS-es + const char_t nativelib_ext[] = W(".so"); +#endif + const char_t coreclr_lib[] = W("libcoreclr"); + + int strcmp(const char_t* str1, const char_t* str2) { return ::strcmp(str1, str2); } + size_t strlen(const char_t* str) { return ::strlen(str); } + char_t* strdup(const char_t* str) { return ::strdup(str); } + int fprintf(FILE* fd, const char_t* const fmt, ...) + { + va_list args; + va_start(args, fmt); + int ret = ::vfprintf(fd, fmt, args); + va_end(args); + return ret; + } + bool is_cli_option(const char_t option_maybe) { return option_maybe == W('-'); } + string_t getenv(const char_t* var) + { + const char_t* val = ::getenv(var); + if (val == nullptr) + return {}; + return { val }; + } + + string_t get_exe_path() { return { getexepath() }; } + + string_t get_absolute_path(const string_t& path) + { + string_t abs_path = path; + + char_t realPath[PATH_MAX]; + if (realpath(path.c_str(), realPath) != nullptr && realPath[0] != W('\0')) + { + abs_path.assign(realPath); + // realpath should return canonicalized path without the trailing slash + assert(abs_path.back() != W('/')); + } + + return abs_path; + } + + debugger_state_t is_debugger_attached() + { +#if defined(__APPLE__) + // Taken from https://developer.apple.com/library/archive/qa/qa1361/_index.html + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(junk == 0); + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ) ? debugger_state_t::attached : debugger_state_t::not_attached; + +#else // !__APPLE__ + // Use procfs to detect if there is a tracer process. + // See https://www.kernel.org/doc/html/latest/filesystems/proc.html + char status[2048] = { 0 }; + int fd = ::open("/proc/self/status", O_RDONLY); + if (fd == -1) + { + // If the file can't be opened assume we are on a not supported platform. + return debugger_state_t::na; + } + + // Attempt to read + ssize_t bytes_read = ::read(fd, status, sizeof(status) - 1); + if (bytes_read > 0) + { + // We have data. At this point we can likely make a strong decision. + const char tracer_pid_name[] = "TracerPid:"; + const char* tracer_pid_ptr = ::strstr(status, tracer_pid_name); + if (tracer_pid_ptr == nullptr) + return debugger_state_t::not_attached; + + // The number after the name is the process ID of the + // tracer application or 0 if none exists. + const char* curr = tracer_pid_ptr + (sizeof(tracer_pid_name) - 1); + const char* end = status + bytes_read; + for (;curr < end; ++curr) + { + if (::isspace(*curr)) + continue; + + // Check the first non-space if it is 0. If so, we have + // a non-zero process ID and a tracer is attached. + return (::isdigit(*curr) && *curr != '0') ? debugger_state_t::attached : debugger_state_t::not_attached; + } + } + + // The read in data is either incomplete (i.e. small buffer) or + // the returned content is not expected. Let's fallback to not available. + return debugger_state_t::na; + +#endif // !__APPLE__ + } + + bool does_file_exist(const char_t* file_path) + { + // Check if the specified path exists + struct stat sb; + if (stat(file_path, &sb) == -1) + { + perror(W("Path not found")); + return false; + } + + // Verify that the path points to a file + if (!S_ISREG(sb.st_mode)) + { + pal::fprintf(stderr, W("The specified managed assembly is not a file: %s\n"), file_path); + return false; + } + + return true; + } + + // Forward declaration + template + bool string_ends_with(const string_t& str, const char_t(&suffix)[LEN]); + bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix); + void ensure_trailing_delimiter(pal::string_t& dir); + + string_t build_file_list( + const string_t& directory, + const char_t* ext, + std::function should_add) + { + assert(ext != nullptr); + const size_t ext_len = pal::strlen(ext); + + DIR* dir = opendir(directory.c_str()); + if (dir == nullptr) + return {}; + + stringstream_t file_list; + + // For all entries in the directory + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) + { +#if HAVE_DIRENT_D_TYPE + int dirEntryType = entry->d_type; +#else + int dirEntryType = DT_UNKNOWN; +#endif + + // We are interested in files only + switch (dirEntryType) + { + case DT_REG: + break; + + // Handle symlinks and file systems that do not support d_type + case DT_LNK: + case DT_UNKNOWN: + { + string_t full_filename; + full_filename.append(directory); + full_filename.append(1, pal::dir_delim); + full_filename.append(entry->d_name); + if (!does_file_exist(full_filename.c_str())) + continue; + } + break; + + default: + continue; + } + + // Check if the extension matches the one we are looking for + if (!string_ends_with(entry->d_name, ext_len, ext)) + continue; + + // Make sure if we have an assembly with multiple extensions present, + // we insert only one version of it. + if (should_add(entry->d_name)) + file_list << directory << dir_delim << entry->d_name << env_path_delim; + } + + closedir(dir); + return file_list.str(); + } + + void* get_module_symbol(mod_t m, const char* sym) + { + assert(m != nullptr && sym != nullptr); + return dlsym(m, sym); + } + + string_utf8_t convert_to_utf8(const char_t* str) + { + return { str }; + } + + string_utf8_t convert_to_utf8(string_t&& str) + { + return std::move(str); + } + + bool try_load_hostpolicy(pal::string_t mock_hostpolicy_value) + { + if (!string_ends_with(mock_hostpolicy_value, pal::nativelib_ext)) + mock_hostpolicy_value.append(pal::nativelib_ext); + + pal::mod_t hMod = (pal::mod_t)dlopen(mock_hostpolicy_value.c_str(), RTLD_LAZY); + if (hMod == nullptr) + pal::fprintf(stderr, W("Failed to load mock hostpolicy at path '%s'. Error: %s\n"), mock_hostpolicy_value.c_str(), dlerror()); + + return hMod != nullptr; + } + + bool try_load_coreclr(const pal::string_t& core_root, pal::mod_t& hMod) + { + pal::string_t coreclr_path = core_root; + pal::ensure_trailing_delimiter(coreclr_path); + coreclr_path.append(pal::coreclr_lib); + coreclr_path.append(pal::nativelib_ext); + + hMod = (pal::mod_t)dlopen(coreclr_path.c_str(), RTLD_NOW | RTLD_LOCAL); + if (hMod == nullptr) + { + pal::fprintf(stderr, W("Failed to load: '%s'. Error: %s\n"), coreclr_path.c_str(), dlerror()); + return false; + } + + return true; + } +} + +class platform_specific_actions final +{ +public: + platform_specific_actions() = default; + + void before_coreclr_load() + { +#ifdef HOST_ARM + // libunwind library is used to unwind stack frame, but libunwind for ARM + // does not support ARM vfpv3/NEON registers in DWARF format correctly. + // Therefore let's disable stack unwinding using DWARF information + // See https://github.com/dotnet/runtime/issues/6479 + // + // libunwind use following methods to unwind stack frame. + // UNW_ARM_METHOD_ALL 0xFF + // UNW_ARM_METHOD_DWARF 0x01 + // UNW_ARM_METHOD_FRAME 0x02 + // UNW_ARM_METHOD_EXIDX 0x04 + putenv(const_cast("UNW_ARM_UNWIND_METHOD=6")); +#endif // HOST_ARM + } + + void before_execute_assembly(const pal::string_t& assembly) { } + + void after_execute_assembly() { } +}; + +#endif // !TARGET_WINDOWS + +namespace pal +{ + void split_path_to_dir_filename(const pal::string_t& path, pal::string_t& dir, pal::string_t& filename) + { + size_t pos = path.find_last_of(dir_delim); + if (pos == pal::string_t::npos) + { + dir = {}; + filename = path; + return; + } + + dir = path.substr(0, pos); + filename = path.substr(pos + 1); + } + + bool string_ends_with(const string_t& str, size_t suffix_len, const char_t* suffix) + { + assert(suffix != nullptr); + + size_t str_len = str.length(); + if (str_len < suffix_len) + return false; + + const char_t* suffix_maybe = str.data() + (str_len - suffix_len); + return ::memcmp(suffix_maybe, suffix, suffix_len * sizeof(char_t)) == 0; + } + + template + bool string_ends_with(const string_t& str, const char_t(&suffix)[LEN]) + { + return string_ends_with(str, LEN - 1, suffix); + } + + void ensure_trailing_delimiter(pal::string_t& dir) + { + if (dir.empty()) + { + dir = pal::dir_delim; + } + else if (dir.back() != pal::dir_delim) + { + dir.push_back(pal::dir_delim); + } + } + + const char** convert_argv_to_utf8(int argc, const char_t** argv, std::vector& lifetime) + { + malloc_ptr ret{ (const char**)::malloc(sizeof(char*) * argc) }; + assert(ret != nullptr); + + lifetime.resize(argc); + for (int i = 0; i < argc; ++i) + { + string_utf8_t s = convert_to_utf8(argv[i]); + lifetime[i] = std::move(s); + ret.get()[i] = lifetime[i].c_str(); + } + + return ret.release(); + } +} + +#endif // __CORERUN_HPP__ diff --git a/src/coreclr/hosts/corerun/logger.cpp b/src/coreclr/hosts/corerun/logger.cpp deleted file mode 100644 index f3fd0477f1e67..0000000000000 --- a/src/coreclr/hosts/corerun/logger.cpp +++ /dev/null @@ -1,282 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - - -#include -#include -#include -#include "palclr.h" -#include "sstring.h" - -void Logger::Enable() { - m_isEnabled = true; -} - -void Logger::Disable() { - m_isEnabled = false; -} - -void print(const wchar_t *val) { - // If val is longer than 2048 characters, wprintf will refuse to print it. - // So write it in chunks. - - const size_t chunkSize = 1024; - - wchar_t chunk[chunkSize]; - - auto valLength = ::wcslen(val); - - for (size_t i = 0 ; i < valLength ; i += chunkSize) { - - ::wcsncpy_s(chunk, chunkSize, val + i, _TRUNCATE); - - ::wprintf(W("%s"), chunk); - } -} - -Logger& Logger::operator<< (bool val) { - if (m_isEnabled) { - if (val) { - EnsurePrefixIsPrinted(); - print(W("true")); - } else { - EnsurePrefixIsPrinted(); - print(W("false")); - } - } - return *this; -} -void PrintAsHResult(int val) { - const wchar_t * str = nullptr; - - switch (val) { - case 0x00000000: str = W("S_OK"); break; - case 0x00000001: str = W("S_FALSE"); break; - case 0x8000000B: str = W("E_BOUNDS"); break; - case 0x8000000C: str = W("E_CHANGED_STATE"); break; - case 0x80000013: str = W("RO_E_CLOSED"); break; - case 0x8000211D: str = W("COR_E_AMBIGUOUSMATCH"); break; - case 0x80004001: str = W("E_NOTIMPL"); break; - case 0x80004002: str = W("COR_E_INVALIDCAST"); break; - //case 0x80004002: str = W("E_NOINTERFACE"); break; - case 0x80004003: str = W("COR_E_NULLREFERENCE"); break; - //case 0x80004003: str = W("E_POINTER"); break; - case 0x80004004: str = W("E_ABORT"); break; - case 0x80004005: str = W("E_FAIL"); break; - case 0x8000FFFF: str = W("E_UNEXPECTED"); break; - case 0x8002000a: str = W("DISP_E_OVERFLOW"); break; - case 0x8002000e: str = W("COR_E_TARGETPARAMCOUNT"); break; - case 0x80020012: str = W("COR_E_DIVIDEBYZERO"); break; - case 0x80028ca0: str = W("TYPE_E_TYPEMISMATCH"); break; - case 0x80070005: str = W("COR_E_UNAUTHORIZEDACCESS"); break; - //case 0x80070005: str = W("E_ACCESSDENIED"); break; - case 0x80070006: str = W("E_HANDLE"); break; - case 0x8007000B: str = W("COR_E_BADIMAGEFORMAT"); break; - case 0x8007000E: str = W("COR_E_OUTOFMEMORY"); break; - //case 0x8007000E: str = W("E_OUTOFMEMORY"); break; - case 0x80070057: str = W("COR_E_ARGUMENT"); break; - //case 0x80070057: str = W("E_INVALIDARG"); break; - case 0x80070216: str = W("COR_E_ARITHMETIC"); break; - case 0x800703E9: str = W("COR_E_STACKOVERFLOW"); break; - case 0x80090020: str = W("NTE_FAIL"); break; - case 0x80131013: str = W("COR_E_TYPEUNLOADED"); break; - case 0x80131014: str = W("COR_E_APPDOMAINUNLOADED"); break; - case 0x80131015: str = W("COR_E_CANNOTUNLOADAPPDOMAIN"); break; - case 0x80131040: str = W("FUSION_E_REF_DEF_MISMATCH"); break; - case 0x80131047: str = W("FUSION_E_INVALID_NAME"); break; - case 0x80131416: str = W("CORSEC_E_POLICY_EXCEPTION"); break; - case 0x80131417: str = W("CORSEC_E_MIN_GRANT_FAIL"); break; - case 0x80131418: str = W("CORSEC_E_NO_EXEC_PERM"); break; - //case 0x80131419: str = W("CORSEC_E_XMLSYNTAX"); break; - case 0x80131430: str = W("CORSEC_E_CRYPTO"); break; - case 0x80131431: str = W("CORSEC_E_CRYPTO_UNEX_OPER"); break; - case 0x80131500: str = W("COR_E_EXCEPTION"); break; - case 0x80131501: str = W("COR_E_SYSTEM"); break; - case 0x80131502: str = W("COR_E_ARGUMENTOUTOFRANGE"); break; - case 0x80131503: str = W("COR_E_ARRAYTYPEMISMATCH"); break; - case 0x80131504: str = W("COR_E_CONTEXTMARSHAL"); break; - case 0x80131505: str = W("COR_E_TIMEOUT"); break; - case 0x80131506: str = W("COR_E_EXECUTIONENGINE"); break; - case 0x80131507: str = W("COR_E_FIELDACCESS"); break; - case 0x80131508: str = W("COR_E_INDEXOUTOFRANGE"); break; - case 0x80131509: str = W("COR_E_INVALIDOPERATION"); break; - case 0x8013150A: str = W("COR_E_SECURITY"); break; - case 0x8013150C: str = W("COR_E_SERIALIZATION"); break; - case 0x8013150D: str = W("COR_E_VERIFICATION"); break; - case 0x80131510: str = W("COR_E_METHODACCESS"); break; - case 0x80131511: str = W("COR_E_MISSINGFIELD"); break; - case 0x80131512: str = W("COR_E_MISSINGMEMBER"); break; - case 0x80131513: str = W("COR_E_MISSINGMETHOD"); break; - case 0x80131514: str = W("COR_E_MULTICASTNOTSUPPORTED"); break; - case 0x80131515: str = W("COR_E_NOTSUPPORTED"); break; - case 0x80131516: str = W("COR_E_OVERFLOW"); break; - case 0x80131517: str = W("COR_E_RANK"); break; - case 0x80131518: str = W("COR_E_SYNCHRONIZATIONLOCK"); break; - case 0x80131519: str = W("COR_E_THREADINTERRUPTED"); break; - case 0x8013151A: str = W("COR_E_MEMBERACCESS"); break; - case 0x80131520: str = W("COR_E_THREADSTATE"); break; - case 0x80131521: str = W("COR_E_THREADSTOP"); break; - case 0x80131522: str = W("COR_E_TYPELOAD"); break; - case 0x80131523: str = W("COR_E_ENTRYPOINTNOTFOUND"); break; - //case 0x80131523: str = W("COR_E_UNSUPPORTEDFORMAT"); break; - case 0x80131524: str = W("COR_E_DLLNOTFOUND"); break; - case 0x80131525: str = W("COR_E_THREADSTART"); break; - case 0x80131527: str = W("COR_E_INVALIDCOMOBJECT"); break; - case 0x80131528: str = W("COR_E_NOTFINITENUMBER"); break; - case 0x80131529: str = W("COR_E_DUPLICATEWAITOBJECT"); break; - case 0x8013152B: str = W("COR_E_SEMAPHOREFULL"); break; - case 0x8013152C: str = W("COR_E_WAITHANDLECANNOTBEOPENED"); break; - case 0x8013152D: str = W("COR_E_ABANDONEDMUTEX"); break; - case 0x80131530: str = W("COR_E_THREADABORTED"); break; - case 0x80131531: str = W("COR_E_INVALIDOLEVARIANTTYPE"); break; - case 0x80131532: str = W("COR_E_MISSINGMANIFESTRESOURCE"); break; - case 0x80131533: str = W("COR_E_SAFEARRAYTYPEMISMATCH"); break; - case 0x80131534: str = W("COR_E_TYPEINITIALIZATION"); break; - case 0x80131535: str = W("COR_E_COMEMULATE"); break; - //case 0x80131535: str = W("COR_E_MARSHALDIRECTIVE"); break; - case 0x80131536: str = W("COR_E_MISSINGSATELLITEASSEMBLY"); break; - case 0x80131537: str = W("COR_E_FORMAT"); break; - case 0x80131538: str = W("COR_E_SAFEARRAYRANKMISMATCH"); break; - case 0x80131539: str = W("COR_E_PLATFORMNOTSUPPORTED"); break; - case 0x8013153A: str = W("COR_E_INVALIDPROGRAM"); break; - case 0x8013153B: str = W("COR_E_OPERATIONCANCELED"); break; - case 0x8013153D: str = W("COR_E_INSUFFICIENTMEMORY"); break; - case 0x8013153E: str = W("COR_E_RUNTIMEWRAPPED"); break; - case 0x80131541: str = W("COR_E_DATAMISALIGNED"); break; - case 0x80131543: str = W("COR_E_TYPEACCESS"); break; - case 0x80131577: str = W("COR_E_KEYNOTFOUND"); break; - case 0x80131578: str = W("COR_E_INSUFFICIENTEXECUTIONSTACK"); break; - case 0x80131600: str = W("COR_E_APPLICATION"); break; - case 0x80131601: str = W("COR_E_INVALIDFILTERCRITERIA"); break; - case 0x80131602: str = W("COR_E_REFLECTIONTYPELOAD "); break; - case 0x80131603: str = W("COR_E_TARGET"); break; - case 0x80131604: str = W("COR_E_TARGETINVOCATION"); break; - case 0x80131605: str = W("COR_E_CUSTOMATTRIBUTEFORMAT"); break; - case 0x80131622: str = W("COR_E_OBJECTDISPOSED"); break; - case 0x80131623: str = W("COR_E_SAFEHANDLEMISSINGATTRIBUTE"); break; - case 0x80131640: str = W("COR_E_HOSTPROTECTION"); break; - } - - ::wprintf(W("0x%x"), val); - - if (str != nullptr) { - ::wprintf(W("/%0s"), str); - } - -} - -Logger& Logger::operator<< (int val) { - - if (m_isEnabled) { - - EnsurePrefixIsPrinted(); - - if (m_formatHRESULT) { - - PrintAsHResult(val); - m_formatHRESULT = false; - - } else { - - ::wprintf(W("%d"), val); - - } - } - - return *this; -} - -#ifdef _MSC_VER -Logger& Logger::operator<< (long val) { - if (m_isEnabled) { - EnsurePrefixIsPrinted(); - - if (m_formatHRESULT) { - - PrintAsHResult(val); - m_formatHRESULT = false; - - } else { - - ::wprintf(W("%d"), val); - - } - } - return *this; -} - -Logger& Logger::operator<< (unsigned long val) { - if (m_isEnabled) { - EnsurePrefixIsPrinted(); - - if (m_formatHRESULT) { - - PrintAsHResult(val); - m_formatHRESULT = false; - - } else { - - ::wprintf(W("%d"), val); - - } - } - return *this; -} -#endif - -Logger& Logger::operator<< (const wchar_t *val) { - if (m_isEnabled) { - EnsurePrefixIsPrinted(); - print(val); - } - return *this; -} - -Logger& Logger::operator<< (const char *val) { - if (m_isEnabled) { - EnsurePrefixIsPrinted(); - - SString valUTF8(SString::Utf8Literal, val); - SmallStackSString valUnicode; - valUTF8.ConvertToUnicode(valUnicode); - - print(valUnicode); - } - return *this; -} - -Logger& Logger::operator<< (Logger& ( *pf )(Logger&)) { - if (m_isEnabled) { - return pf(*this); - } else { - return *this; - } -} - -void Logger::EnsurePrefixIsPrinted() { - if (this->m_isEnabled && this->m_prefixRequired) { - print(W(" HOSTLOG: ")); - m_prefixRequired = false; - } -} - -// Manipulators - -// Newline -Logger& Logger::endl (Logger& log) { - if (log.m_isEnabled) { - log.EnsurePrefixIsPrinted(); - print(W("\r\n")); - log.m_prefixRequired = true; - log.m_formatHRESULT = false; - } - return log; -} - -// Format the next integer value as an HResult -Logger& Logger::hresult (Logger& log) { - log.m_formatHRESULT = true; - return log; -} - diff --git a/src/coreclr/hosts/corerun/logger.h b/src/coreclr/hosts/corerun/logger.h deleted file mode 100644 index fe954f806303f..0000000000000 --- a/src/coreclr/hosts/corerun/logger.h +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - -// -// Logger for the CoreCLR host ccrun. -// Relies on the SYSCRT and therefore cannot use C++ libraries. -// - - -class Logger { - bool m_isEnabled; - bool m_prefixRequired; - bool m_formatHRESULT; - -public: - Logger() : - m_isEnabled(true), - m_prefixRequired(true), - m_formatHRESULT(false) { } - - ~Logger() { } - - // Enables output from the logger - void Enable(); - - // Disables output from the logger - void Disable(); - - - Logger& operator<< (bool val); - Logger& operator<< (short val); - Logger& operator<< (unsigned short val); - Logger& operator<< (int val); - Logger& operator<< (unsigned int val); -#ifdef _MSC_VER - Logger& operator<< (long val); - Logger& operator<< (unsigned long val); -#endif - Logger& operator<< (float val); - Logger& operator<< (double val); - Logger& operator<< (long double val); - Logger& operator<< (const wchar_t* val); - Logger& operator<< (const char* val); - Logger& operator<< (Logger& ( *pf )(Logger&)); - static Logger& endl ( Logger& log ); - static Logger& hresult ( Logger& log); - -private: - void EnsurePrefixIsPrinted(); -}; - - - - - diff --git a/src/coreclr/hosts/corerun/test.txt b/src/coreclr/hosts/corerun/test.txt deleted file mode 100644 index 037873ba557eb..0000000000000 --- a/src/coreclr/hosts/corerun/test.txt +++ /dev/null @@ -1 +0,0 @@ -time 2 diff --git a/src/coreclr/hosts/coreshim/CMakeLists.txt b/src/coreclr/hosts/coreshim/CMakeLists.txt index 51061e11d1dd3..5c6a2fd28899d 100644 --- a/src/coreclr/hosts/coreshim/CMakeLists.txt +++ b/src/coreclr/hosts/coreshim/CMakeLists.txt @@ -22,4 +22,4 @@ target_link_libraries(CoreShim ${STATIC_MT_VCRT_LIB} ) -install_clr(TARGETS CoreShim) +install_clr(TARGETS CoreShim DESTINATIONS . COMPONENT runtime) diff --git a/src/coreclr/hosts/unixcorerun/CMakeLists.txt b/src/coreclr/hosts/unixcorerun/CMakeLists.txt deleted file mode 100644 index 9e768f97707d3..0000000000000 --- a/src/coreclr/hosts/unixcorerun/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -project(unixcorerun) - -include_directories("${CLR_SRC_NATIVE_DIR}/common") -include(configure.cmake) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -_add_executable(corerun corerun.cpp) - -target_link_libraries(corerun ${CMAKE_DL_LIBS}) - -# Android implements pthread natively -if(NOT CLR_CMAKE_TARGET_ANDROID) - target_link_libraries(corerun pthread) -endif() - -install_clr(TARGETS corerun) diff --git a/src/coreclr/hosts/unixcorerun/corerun.cpp b/src/coreclr/hosts/unixcorerun/corerun.cpp deleted file mode 100644 index 8e00aeac409fb..0000000000000 --- a/src/coreclr/hosts/unixcorerun/corerun.cpp +++ /dev/null @@ -1,581 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "coreclrhost.h" -#include -#ifndef SUCCEEDED -#define SUCCEEDED(Status) ((Status) >= 0) -#endif // !SUCCEEDED - -#include -#include - -#if !HAVE_DIRENT_D_TYPE -#define DT_UNKNOWN 0 -#define DT_DIR 4 -#define DT_REG 8 -#define DT_LNK 10 -#endif - -// Name of the environment variable controlling server GC. -// If set to 1, server GC is enabled on startup. If 0, server GC is -// disabled. Server GC is off by default. -static const char* serverGcVar = "COMPlus_gcServer"; - -// Name of environment variable to control "System.Globalization.Invariant" -// Set to 1 for Globalization Invariant mode to be true. Default is false. -static const char* globalizationInvariantVar = "CORECLR_GLOBAL_INVARIANT"; - -bool GetAbsolutePath(const char* path, std::string& absolutePath) -{ - bool result = false; - - char realPath[PATH_MAX]; - if (realpath(path, realPath) != nullptr && realPath[0] != '\0') - { - absolutePath.assign(realPath); - // realpath should return canonicalized path without the trailing slash - assert(absolutePath.back() != '/'); - - result = true; - } - - return result; -} - -bool GetDirectory(const char* absolutePath, std::string& directory) -{ - directory.assign(absolutePath); - size_t lastSlash = directory.rfind('/'); - if (lastSlash != std::string::npos) - { - directory.erase(lastSlash); - return true; - } - - return false; -} - -bool GetClrFilesAbsolutePath(const char* currentExePath, const char* clrFilesPath, std::string& clrFilesAbsolutePath) -{ - std::string clrFilesRelativePath; - const char* clrFilesPathLocal = clrFilesPath; - if (clrFilesPathLocal == nullptr) - { - // There was no CLR files path specified, use the folder of the corerun/coreconsole - if (!GetDirectory(currentExePath, clrFilesRelativePath)) - { - perror("Failed to get directory from argv[0]"); - return false; - } - - clrFilesPathLocal = clrFilesRelativePath.c_str(); - - // TODO: consider using an env variable (if defined) as a fall-back. - // The windows version of the corerun uses core_root env variable - } - - if (!GetAbsolutePath(clrFilesPathLocal, clrFilesAbsolutePath)) - { - perror("Failed to convert CLR files path to absolute path"); - return false; - } - - return true; -} - -void AddFilesFromDirectoryToTpaList(const char* directory, std::string& tpaList) -{ - const char * const tpaExtensions[] = { - ".ni.dll", // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir - ".dll", - ".ni.exe", - ".exe", - }; - - DIR* dir = opendir(directory); - if (dir == nullptr) - { - return; - } - - std::set addedAssemblies; - - // Walk the directory for each extension separately so that we first get files with .ni.dll extension, - // then files with .dll extension, etc. - for (size_t extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++) - { - const char* ext = tpaExtensions[extIndex]; - int extLength = strlen(ext); - - struct dirent* entry; - - // For all entries in the directory - while ((entry = readdir(dir)) != nullptr) - { -#if HAVE_DIRENT_D_TYPE - int dirEntryType = entry->d_type; -#else - int dirEntryType = DT_UNKNOWN; -#endif - - // We are interested in files only - switch (dirEntryType) - { - case DT_REG: - break; - - // Handle symlinks and file systems that do not support d_type - case DT_LNK: - case DT_UNKNOWN: - { - std::string fullFilename; - - fullFilename.append(directory); - fullFilename.append("/"); - fullFilename.append(entry->d_name); - - struct stat sb; - if (stat(fullFilename.c_str(), &sb) == -1) - { - continue; - } - - if (!S_ISREG(sb.st_mode)) - { - continue; - } - } - break; - - default: - continue; - } - - std::string filename(entry->d_name); - - // Check if the extension matches the one we are looking for - int extPos = filename.length() - extLength; - if ((extPos <= 0) || (filename.compare(extPos, extLength, ext) != 0)) - { - continue; - } - - std::string filenameWithoutExt(filename.substr(0, extPos)); - - // Make sure if we have an assembly with multiple extensions present, - // we insert only one version of it. - if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end()) - { - addedAssemblies.insert(filenameWithoutExt); - - tpaList.append(directory); - tpaList.append("/"); - tpaList.append(filename); - tpaList.append(":"); - } - } - - // Rewind the directory stream to be able to iterate over it for the next extension - rewinddir(dir); - } - - closedir(dir); -} - -const char* GetEnvValueBoolean(const char* envVariable) -{ - const char* envValue = std::getenv(envVariable); - if (envValue == nullptr) - { - envValue = "0"; - } - // CoreCLR expects strings "true" and "false" instead of "1" and "0". - return (std::strcmp(envValue, "1") == 0 || strcasecmp(envValue, "true") == 0) ? "true" : "false"; -} - -static void *TryLoadHostPolicy(const char *hostPolicyPath) -{ -#if defined(__APPLE__) - static const char LibrarySuffix[] = ".dylib"; -#else // Various Linux-related OS-es - static const char LibrarySuffix[] = ".so"; -#endif - - std::string hostPolicyCompletePath(hostPolicyPath); - hostPolicyCompletePath.append(LibrarySuffix); - - void *libraryPtr = dlopen(hostPolicyCompletePath.c_str(), RTLD_LAZY); - if (libraryPtr == nullptr) - { - fprintf(stderr, "Failed to load mock hostpolicy at path '%s'. Error: %s", hostPolicyCompletePath.c_str(), dlerror()); - } - - return libraryPtr; -} - -int ExecuteManagedAssembly( - const char* currentExeAbsolutePath, - const char* clrFilesAbsolutePath, - const char* managedAssemblyAbsolutePath, - int managedAssemblyArgc, - const char** managedAssemblyArgv) -{ - // Indicates failure - int exitCode = -1; - -#ifdef HOST_ARM - // libunwind library is used to unwind stack frame, but libunwind for ARM - // does not support ARM vfpv3/NEON registers in DWARF format correctly. - // Therefore let's disable stack unwinding using DWARF information - // See https://github.com/dotnet/runtime/issues/6479 - // - // libunwind use following methods to unwind stack frame. - // UNW_ARM_METHOD_ALL 0xFF - // UNW_ARM_METHOD_DWARF 0x01 - // UNW_ARM_METHOD_FRAME 0x02 - // UNW_ARM_METHOD_EXIDX 0x04 - putenv(const_cast("UNW_ARM_UNWIND_METHOD=6")); -#endif // HOST_ARM - -#if defined(__APPLE__) - static const char* const coreClrDll = "libcoreclr.dylib"; -#else - static const char* const coreClrDll = "libcoreclr.so"; -#endif - - std::string coreClrDllPath(clrFilesAbsolutePath); - coreClrDllPath.append("/"); - coreClrDllPath.append(coreClrDll); - - if (coreClrDllPath.length() >= PATH_MAX) - { - fprintf(stderr, "Absolute path to libcoreclr.so too long\n"); - return -1; - } - - // Get just the path component of the managed assembly path - std::string appPath; - GetDirectory(managedAssemblyAbsolutePath, appPath); - - std::string tpaList; - // Construct native search directory paths - std::string nativeDllSearchDirs(appPath); - char *coreLibraries = getenv("CORE_LIBRARIES"); - if (coreLibraries) - { - nativeDllSearchDirs.append(":"); - nativeDllSearchDirs.append(coreLibraries); - if (std::strcmp(coreLibraries, clrFilesAbsolutePath) != 0) - { - AddFilesFromDirectoryToTpaList(coreLibraries, tpaList); - } - } - nativeDllSearchDirs.append(":"); - nativeDllSearchDirs.append(clrFilesAbsolutePath); - - void* hostpolicyLib = nullptr; - char* mockHostpolicyPath = getenv("MOCK_HOSTPOLICY"); - if (mockHostpolicyPath) - { - hostpolicyLib = TryLoadHostPolicy(mockHostpolicyPath); - if (hostpolicyLib == nullptr) - { - return -1; - } - } - - AddFilesFromDirectoryToTpaList(clrFilesAbsolutePath, tpaList); - - void* coreclrLib = dlopen(coreClrDllPath.c_str(), RTLD_NOW | RTLD_LOCAL); - if (coreclrLib != nullptr) - { - coreclr_initialize_ptr initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize"); - coreclr_execute_assembly_ptr executeAssembly = (coreclr_execute_assembly_ptr)dlsym(coreclrLib, "coreclr_execute_assembly"); - coreclr_shutdown_2_ptr shutdownCoreCLR = (coreclr_shutdown_2_ptr)dlsym(coreclrLib, "coreclr_shutdown_2"); - - if (initializeCoreCLR == nullptr) - { - fprintf(stderr, "Function coreclr_initialize not found in the libcoreclr.so\n"); - } - else if (executeAssembly == nullptr) - { - fprintf(stderr, "Function coreclr_execute_assembly not found in the libcoreclr.so\n"); - } - else if (shutdownCoreCLR == nullptr) - { - fprintf(stderr, "Function coreclr_shutdown_2 not found in the libcoreclr.so\n"); - } - else - { - // Check whether we are enabling server GC (off by default) - const char* useServerGc = GetEnvValueBoolean(serverGcVar); - - // Check Globalization Invariant mode (false by default) - const char* globalizationInvariant = GetEnvValueBoolean(globalizationInvariantVar); - - // Allowed property names: - // APPBASE - // - The base path of the application from which the exe and other assemblies will be loaded - // - // TRUSTED_PLATFORM_ASSEMBLIES - // - The list of complete paths to each of the fully trusted assemblies - // - // APP_PATHS - // - The list of paths which will be probed by the assembly loader - // - // APP_NI_PATHS - // - The list of additional paths that the assembly loader will probe for ngen images - // - // NATIVE_DLL_SEARCH_DIRECTORIES - // - The list of paths that will be probed for native DLLs called by PInvoke - // - const char *propertyKeys[] = { - "TRUSTED_PLATFORM_ASSEMBLIES", - "APP_PATHS", - "APP_NI_PATHS", - "NATIVE_DLL_SEARCH_DIRECTORIES", - "System.GC.Server", - "System.Globalization.Invariant", - }; - const char *propertyValues[] = { - // TRUSTED_PLATFORM_ASSEMBLIES - tpaList.c_str(), - // APP_PATHS - appPath.c_str(), - // APP_NI_PATHS - appPath.c_str(), - // NATIVE_DLL_SEARCH_DIRECTORIES - nativeDllSearchDirs.c_str(), - // System.GC.Server - useServerGc, - // System.Globalization.Invariant - globalizationInvariant, - }; - - void* hostHandle; - unsigned int domainId; - - int st = initializeCoreCLR( - currentExeAbsolutePath, - "unixcorerun", - sizeof(propertyKeys) / sizeof(propertyKeys[0]), - propertyKeys, - propertyValues, - &hostHandle, - &domainId); - - if (!SUCCEEDED(st)) - { - fprintf(stderr, "coreclr_initialize failed - status: 0x%08x\n", st); - exitCode = -1; - } - else - { - st = executeAssembly( - hostHandle, - domainId, - managedAssemblyArgc, - managedAssemblyArgv, - managedAssemblyAbsolutePath, - (unsigned int*)&exitCode); - - if (!SUCCEEDED(st)) - { - fprintf(stderr, "coreclr_execute_assembly failed - status: 0x%08x\n", st); - exitCode = -1; - } - - int latchedExitCode = 0; - st = shutdownCoreCLR(hostHandle, domainId, &latchedExitCode); - if (!SUCCEEDED(st)) - { - fprintf(stderr, "coreclr_shutdown failed - status: 0x%08x\n", st); - exitCode = -1; - } - - if (exitCode != -1) - { - exitCode = latchedExitCode; - } - } - } - } - else - { - const char* error = dlerror(); - fprintf(stderr, "dlopen failed to open the libcoreclr.so with error %s\n", error); - } - - if (hostpolicyLib) - { - if(dlclose(hostpolicyLib) != 0) - { - fprintf(stderr, "Warning - dlclose of mock hostpolicy failed.\n"); - } - } - - return exitCode; -} - -// Display the command line options -void DisplayUsage() -{ - fprintf( - stderr, - "Usage: corerun [OPTIONS] assembly [ARGUMENTS]\n" - "Execute the specified managed assembly with the passed in arguments\n\n" - "Options:\n" - "-c, --clr-path path to the libcoreclr.so and the managed CLR assemblies\n"); -} - -// Parse the command line arguments -bool ParseArguments( - const int argc, - const char* argv[], - const char** clrFilesPath, - const char** managedAssemblyPath, - int* managedAssemblyArgc, - const char*** managedAssemblyArgv) -{ - bool success = false; - - *clrFilesPath = nullptr; - *managedAssemblyPath = nullptr; - *managedAssemblyArgv = nullptr; - *managedAssemblyArgc = 0; - - // The command line must contain at least the current exe name and the managed assembly path - if (argc >= 2) - { - for (int i = 1; i < argc; i++) - { - // Check for an option - if (argv[i][0] == '-') - { - // Path to the libcoreclr.so and the managed CLR assemblies - if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--clr-path") == 0) - { - i++; - if (i < argc) - { - *clrFilesPath = argv[i]; - } - else - { - fprintf(stderr, "Option %s: missing path\n", argv[i - 1]); - break; - } - } - else if (strcmp(argv[i], "-?") == 0 || strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) - { - DisplayUsage(); - break; - } - else - { - fprintf(stderr, "Unknown option %s\n", argv[i]); - break; - } - } - else - { - // First argument that is not an option is the managed assembly to execute - *managedAssemblyPath = argv[i]; - - int managedArgvOffset = (i + 1); - *managedAssemblyArgc = argc - managedArgvOffset; - if (*managedAssemblyArgc != 0) - { - *managedAssemblyArgv = &argv[managedArgvOffset]; - } - success = true; - break; - } - } - } - else - { - DisplayUsage(); - } - - return success; -} - -int main(const int argc, const char* argv[]) -{ - const char* clrFilesPath; - const char* managedAssemblyPath; - const char** managedAssemblyArgv; - int managedAssemblyArgc; - - if (!ParseArguments( - argc, - argv, - &clrFilesPath, - &managedAssemblyPath, - &managedAssemblyArgc, - &managedAssemblyArgv)) - { - // Invalid command line - return -1; - } - - // Check if the specified managed assembly file exists - struct stat sb; - if (stat(managedAssemblyPath, &sb) == -1) - { - perror("Managed assembly not found"); - return -1; - } - - // Verify that the managed assembly path points to a file - if (!S_ISREG(sb.st_mode)) - { - fprintf(stderr, "The specified managed assembly is not a file\n"); - return -1; - } - - // Make sure we have a full path for argv[0]. - char* path = getexepath(); - if (!path) - { - perror("Could not get full path"); - return -1; - } - - std::string argv0AbsolutePath(path); - free(path); - - std::string clrFilesAbsolutePath; - if(!GetClrFilesAbsolutePath(argv0AbsolutePath.c_str(), clrFilesPath, clrFilesAbsolutePath)) - { - return -1; - } - - std::string managedAssemblyAbsolutePath; - if (!GetAbsolutePath(managedAssemblyPath, managedAssemblyAbsolutePath)) - { - perror("Failed to convert managed assembly path to absolute path"); - return -1; - } - - int exitCode = ExecuteManagedAssembly( - argv0AbsolutePath.c_str(), - clrFilesAbsolutePath.c_str(), - managedAssemblyAbsolutePath.c_str(), - managedAssemblyArgc, - managedAssemblyArgv); - - return exitCode; -} diff --git a/src/coreclr/ilasm/CMakeLists.txt b/src/coreclr/ilasm/CMakeLists.txt index 3d385551b9352..c2747a83aec60 100644 --- a/src/coreclr/ilasm/CMakeLists.txt +++ b/src/coreclr/ilasm/CMakeLists.txt @@ -65,7 +65,7 @@ if(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CL set(END_LIBRARY_GROUP -Wl,--end-group) endif(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CLR_CMAKE_HOST_SUNOS) -_add_executable(ilasm +add_executable_clr(ilasm ${ILASM_SOURCES} ${ILASM_RESOURCES} ) @@ -126,4 +126,4 @@ else() ) endif(CLR_CMAKE_HOST_UNIX) -install_clr(TARGETS ilasm) +install_clr(TARGETS ilasm DESTINATIONS . COMPONENT iltools) diff --git a/src/coreclr/ilasm/asmman.cpp b/src/coreclr/ilasm/asmman.cpp index 55fbeb06de760..87e79e51988bf 100644 --- a/src/coreclr/ilasm/asmman.cpp +++ b/src/coreclr/ilasm/asmman.cpp @@ -10,7 +10,6 @@ #include "assembler.h" #include "strongnameinternal.h" #include -#include extern WCHAR* pwzInputFiles[]; @@ -456,7 +455,7 @@ void AsmMan::EndAssembly() // into the public key buffer). if (m_sStrongName.m_cbPublicKey >= sizeof(PublicKeyBlob) && (offsetof(PublicKeyBlob, PublicKey) + - ((PublicKeyBlob*)m_sStrongName.m_pbPublicKey)->cbPublicKey) == m_sStrongName.m_cbPublicKey) + VAL32(((PublicKeyBlob*)m_sStrongName.m_pbPublicKey)->cbPublicKey)) == m_sStrongName.m_cbPublicKey) m_sStrongName.m_fFullSign = FALSE; else m_sStrongName.m_fFullSign = TRUE; diff --git a/src/coreclr/ilasm/assem.cpp b/src/coreclr/ilasm/assem.cpp index 286447b4ec133..aa94f55797262 100644 --- a/src/coreclr/ilasm/assem.cpp +++ b/src/coreclr/ilasm/assem.cpp @@ -253,16 +253,6 @@ void Assembler::SetDLL(BOOL IsDll) m_fDLL = IsDll; } -void Assembler::SetOBJ(BOOL IsObj) -{ - HRESULT OK; - OK = m_pCeeFileGen->SetObjSwitch(m_pCeeFile, IsObj); - _ASSERTE(SUCCEEDED(OK)); - - m_fOBJ = IsObj; -} - - void Assembler::ResetArgNameList() { if(m_firstArgName) delArgNameList(m_firstArgName); diff --git a/src/coreclr/ilasm/assembler.cpp b/src/coreclr/ilasm/assembler.cpp index f28552f00fee1..48f9487656f7c 100644 --- a/src/coreclr/ilasm/assembler.cpp +++ b/src/coreclr/ilasm/assembler.cpp @@ -1799,7 +1799,6 @@ mdToken Assembler::MakeMemberRef(mdToken cr, __in __nullterminated char* pszMemb mr = 0; } } - //if(m_fOBJ) m_pCurMethod->m_TRDList.PUSH(new TokenRelocDescr(m_CurPC,mr)); delete [] pszMemberName; delete sig; } @@ -2068,7 +2067,6 @@ void Assembler::EmitInstrStringLiteral(Instr* instr, BinStr* literal, BOOL Conve else { EmitOpcode(instr); - if(m_fOBJ) m_pCurMethod->m_TRDList.PUSH(new TokenRelocDescr(m_CurPC,tk)); EmitBytes((BYTE *)&tk,sizeof(mdToken)); } @@ -2092,7 +2090,6 @@ void Assembler::EmitInstrSig(Instr* instr, BinStr* sig) else { EmitOpcode(instr); - if(m_fOBJ) m_pCurMethod->m_TRDList.PUSH(new TokenRelocDescr(m_CurPC,MetadataToken)); EmitBytes((BYTE *)&MetadataToken, sizeof(mdSignature)); } delete sig; diff --git a/src/coreclr/ilasm/assembler.h b/src/coreclr/ilasm/assembler.h index 3edc1b40702af..b7ec921f5792d 100644 --- a/src/coreclr/ilasm/assembler.h +++ b/src/coreclr/ilasm/assembler.h @@ -803,7 +803,6 @@ class Assembler { //MethodList m_MethodList; BOOL m_fDLL; - BOOL m_fOBJ; BOOL m_fEntryPointPresent; BOOL m_fHaveFieldsWithRvas; BOOL m_fFoldCode; @@ -870,7 +869,6 @@ class Assembler { state_t CheckLocalTypeConsistancy(int instr, unsigned arg); state_t AddGlobalLabel(__in __nullterminated char *pszName, HCEESECTION section); void SetDLL(BOOL); - void SetOBJ(BOOL); void ResetForNextMethod(); void ResetLineNumbers(); void SetStdMapping(BOOL val = TRUE) { m_fStdMapping = val; }; diff --git a/src/coreclr/ilasm/main.cpp b/src/coreclr/ilasm/main.cpp index 230d0df089af6..17fa665db3d0a 100644 --- a/src/coreclr/ilasm/main.cpp +++ b/src/coreclr/ilasm/main.cpp @@ -105,7 +105,7 @@ WCHAR wzPdbFilename[MAX_FILENAME_LENGTH]; extern "C" int _cdecl wmain(int argc, __in WCHAR **argv) { int i, NumFiles = 0, NumDeltaFiles = 0; - bool IsDLL = false, IsOBJ = false; + bool IsDLL = false; Assembler *pAsm; MappedFileStream *pIn; AsmParse *pParser; @@ -281,13 +281,7 @@ extern "C" int _cdecl wmain(int argc, __in WCHAR **argv) } else if (!_stricmp(szOpt, "DLL")) { - IsDLL = true; IsOBJ = false; - } - else if (!_stricmp(szOpt, "OBJ")) - { - //IsOBJ = true; IsDLL = false; - printf("Option /OBJECT is not supported.\n"); - goto ErrorExit; + IsDLL = true; } else if (!_stricmp(szOpt, "ERR")) { @@ -608,7 +602,7 @@ extern "C" int _cdecl wmain(int argc, __in WCHAR **argv) } } while(j); - wcscat_s(wzOutputFilename, MAX_FILENAME_LENGTH,(IsDLL ? W(".dll") : (IsOBJ ? W(".obj") : W(".exe")))); + wcscat_s(wzOutputFilename, MAX_FILENAME_LENGTH,(IsDLL ? W(".dll") : W(".exe"))); } if (pAsm->m_fGeneratePDB) { @@ -644,7 +638,6 @@ extern "C" int _cdecl wmain(int argc, __in WCHAR **argv) } pAsm->SetDLL(IsDLL); - pAsm->SetOBJ(IsOBJ); wcscpy_s(pAsm->m_wzOutputFileName,MAX_FILENAME_LENGTH,wzOutputFilename); strcpy_s(pAsm->m_szSourceFileName,MAX_FILENAME_LENGTH*3+1,szInputFilename); @@ -665,7 +658,7 @@ extern "C" int _cdecl wmain(int argc, __in WCHAR **argv) { pParser->msg("\nAssembling '%s' ", szInputFilename); if(!pAsm->m_fAutoInheritFromObject) pParser->msg(" NOAUTOINHERIT"); - pParser->msg(IsDLL ? " to DLL" : (IsOBJ? " to OBJ" : " to EXE")); + pParser->msg(IsDLL ? " to DLL" : " to EXE"); //====================================================================== if (pAsm->m_fStdMapping == FALSE) pParser->msg(", with REFERENCE mapping"); @@ -732,7 +725,7 @@ extern "C" int _cdecl wmain(int argc, __in WCHAR **argv) if(exitval == 0) // Write the output file { if(bClock) cw.cFilegenEnd = GetTickCount(); - if(pAsm->m_fReportProgress) pParser->msg("Writing %s file\n", pAsm->m_fOBJ ? "COFF" : "PE"); + if(pAsm->m_fReportProgress) pParser->msg("Writing PE file\n"); // Generate the file if (FAILED(hr = pAsm->m_pCeeFileGen->GenerateCeeFile(pAsm->m_pCeeFile))) { diff --git a/src/coreclr/ildasm/dis.cpp b/src/coreclr/ildasm/dis.cpp index 91f37422d76ce..8ca544ebd1057 100644 --- a/src/coreclr/ildasm/dis.cpp +++ b/src/coreclr/ildasm/dis.cpp @@ -741,8 +741,8 @@ char* DumpUnicodeString(void* GUICookie, if((size_t)pszString & (sizeof(WCHAR)-1)) #endif { - L = (cbString+1)*sizeof(WCHAR); - pszStringCopy = new WCHAR[cbString+1]; + L = cbString*sizeof(WCHAR); + pszStringCopy = new WCHAR[cbString]; memcpy(pszStringCopy, pszString, L); pszString=pszStringCopy; } diff --git a/src/coreclr/ildasm/exe/CMakeLists.txt b/src/coreclr/ildasm/exe/CMakeLists.txt index fe0892f6bd30f..f946f33ffa213 100644 --- a/src/coreclr/ildasm/exe/CMakeLists.txt +++ b/src/coreclr/ildasm/exe/CMakeLists.txt @@ -58,7 +58,7 @@ if(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CL set(END_LIBRARY_GROUP -Wl,--end-group) endif(CLR_CMAKE_HOST_LINUX OR CLR_CMAKE_HOST_FREEBSD OR CLR_CMAKE_HOST_NETBSD OR CLR_CMAKE_HOST_SUNOS) -_add_executable(ildasm +add_executable_clr(ildasm ${ILDASM_SOURCES} ${ILDASM_RESOURCES} ) @@ -115,4 +115,4 @@ else() ) endif(CLR_CMAKE_HOST_UNIX) -install_clr(TARGETS ildasm) +install_clr(TARGETS ildasm DESTINATIONS . COMPONENT iltools) diff --git a/src/coreclr/inc/CMakeLists.txt b/src/coreclr/inc/CMakeLists.txt index 456cdf9096534..535be85af85db 100644 --- a/src/coreclr/inc/CMakeLists.txt +++ b/src/coreclr/inc/CMakeLists.txt @@ -58,13 +58,13 @@ if(FEATURE_JIT_PITCHING) endif(FEATURE_JIT_PITCHING) # Compile *_i.cpp to lib -_add_library(corguids_obj OBJECT ${CORGUIDS_SOURCES}) +add_library_clr(corguids_obj OBJECT ${CORGUIDS_SOURCES}) add_library(corguids INTERFACE) target_sources(corguids INTERFACE $) # Binplace the inc files for packaging later. -_install (FILES cfi.h +install (FILES cfi.h cor.h cordebuginfo.h coredistools.h diff --git a/src/coreclr/inc/CrstTypes.def b/src/coreclr/inc/CrstTypes.def index dbf3363a07cfa..185a590d938e5 100644 --- a/src/coreclr/inc/CrstTypes.def +++ b/src/coreclr/inc/CrstTypes.def @@ -86,7 +86,7 @@ Crst ArgBasedStubCache End Crst AssemblyLoader - AcquiredBefore DeadlockDetection UniqueStack + AcquiredBefore DeadlockDetection UniqueStack DebuggerMutex End Crst AvailableClass diff --git a/src/coreclr/inc/bbsweep.h b/src/coreclr/inc/bbsweep.h index a65e9a94cbedb..03702f816f360 100644 --- a/src/coreclr/inc/bbsweep.h +++ b/src/coreclr/inc/bbsweep.h @@ -22,6 +22,10 @@ #include #endif // !TARGET_UNIX +#ifndef ARRAYSIZE +#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) +#endif // !ARRAYSIZE + // The CLR headers don't allow us to use methods like SetEvent directly (instead // we need to use the host APIs). However, this file is included both in the CLR // and in the BBSweep tool, and the host API is not available in the tool. Moreover, diff --git a/src/coreclr/inc/ceefilegenwriter.h b/src/coreclr/inc/ceefilegenwriter.h index ab55bdc6a9148..94391857c9770 100644 --- a/src/coreclr/inc/ceefilegenwriter.h +++ b/src/coreclr/inc/ceefilegenwriter.h @@ -33,13 +33,9 @@ class CeeFileGenWriter : public CCeeGen LPWSTR m_outputFileName; LPWSTR m_resourceFileName; - LPWSTR m_libraryName; - GUID m_libraryGuid; bool m_dllSwitch; ULONG m_iatOffset; - DWORD m_dwMacroDefinitionRVA; - DWORD m_dwMacroDefinitionSize; DWORD m_dwManifestRVA; DWORD m_dwManifestSize; @@ -70,15 +66,6 @@ class CeeFileGenWriter : public CCeeGen int m_iDataOffsetIAT; char *m_iDataIAT; -#ifdef EMIT_FIXUPS - - CeeSection * m_sectionFixups; - IMAGE_DEBUG_DIRECTORY * m_pDebugDir; - bool m_fFixupsUpdated; - bool m_fEmitFixups; - -#endif - HRESULT allocateIAT(); public: // Create with one of these two methods, not operator new @@ -116,9 +103,6 @@ class CeeFileGenWriter : public CCeeGen HRESULT setResourceFileName(__in LPWSTR resourceFileName); LPWSTR getResourceFileName(); - HRESULT setLibraryName(__in LPWSTR libraryName); - LPWSTR getLibraryName(); - HRESULT setDirectoryEntry(CeeSection §ion, ULONG entry, ULONG size, ULONG offset=0); HRESULT computeSectionOffset(CeeSection §ion, __in char *ptr, unsigned *offset); @@ -128,21 +112,12 @@ class CeeFileGenWriter : public CCeeGen HRESULT getFileTimeStamp(DWORD *pTimeStamp); -//@FUTURE: this entry point is only here so that down level clients of this interface -// can import the method by name in the exports table using the original name. -// These things really ought to be exported through a v-table so there is no -// name mangling issues. It would make the export table much smaller as well. - HRESULT emitLibraryName(IMetaDataEmit *emitter); HRESULT setLibraryGuid(__in LPWSTR libraryGuid); HRESULT setDllSwitch(bool dllSwitch); bool getDllSwitch(); - HRESULT setObjSwitch(bool objSwitch); - bool getObjSwitch(); - HRESULT EmitMacroDefinitions(void *pData, DWORD cData); HRESULT setManifestEntry(ULONG size, ULONG offset); HRESULT setStrongNameEntry(ULONG size, ULONG offset); - HRESULT setEnCRvaBase(ULONG dataBase, ULONG rdataBase); HRESULT setVTableEntry(ULONG size, ULONG offset); HRESULT setVTableEntry64(ULONG size, void* ptr); @@ -157,20 +132,6 @@ class CeeFileGenWriter : public CCeeGen HRESULT MapTokens(CeeGenTokenMapper *pMapper, IMetaDataImport *pImport); HRESULT MapTokensForMethod(CeeGenTokenMapper *pMapper,BYTE *pCode, LPCWSTR szMethodName); - -#ifdef EMIT_FIXUPS - - HRESULT InitFixupSection(); - HRESULT UpdateFixups(); - HRESULT setEmitFixups(); - -#ifdef TEST_EMIT_FIXUPS - HRESULT TestEmitFixups(); -#endif - -public: - HRESULT addFixup(CeeSection& secFixups, unsigned offset, CeeSectionRelocType reloc, CeeSection * relativeTo = NULL, CeeSectionRelocExtra *extra = 0); -#endif }; @@ -188,9 +149,7 @@ inline LPWSTR CeeFileGenWriter::getResourceFileName() { } inline HRESULT CeeFileGenWriter::setDllSwitch(bool dllSwitch) { - if((m_dllSwitch = dllSwitch)) - m_objSwitch = FALSE; - + m_dllSwitch = dllSwitch; return S_OK; } @@ -198,21 +157,6 @@ inline bool CeeFileGenWriter::getDllSwitch() { return m_dllSwitch; } -inline HRESULT CeeFileGenWriter::setObjSwitch(bool objSwitch) { - if((m_objSwitch = objSwitch)) - m_dllSwitch = FALSE; - - return S_OK; -} - -inline bool CeeFileGenWriter::getObjSwitch() { - return m_objSwitch; -} - -inline LPWSTR CeeFileGenWriter::getLibraryName() { - return m_libraryName; -} - inline mdMethodDef CeeFileGenWriter::getEntryPoint() { return m_entryPoint; } diff --git a/src/coreclr/inc/ceegen.h b/src/coreclr/inc/ceegen.h index f770797d5e543..66dfa6421e6ee 100644 --- a/src/coreclr/inc/ceegen.h +++ b/src/coreclr/inc/ceegen.h @@ -25,7 +25,7 @@ typedef DWORD StringRef; This is a description of the current implementation of these types for generating CLR modules. - ICeeGen - interface to generate in-memory CLR module. + ICeeGenInternal - interface to generate in-memory CLR module. CCeeGen - implementation of ICeeGen. Currently it uses both CeeSections as well as PESections (inside PESectionMan), and maintains a @@ -45,7 +45,7 @@ typedef DWORD StringRef; etc which are not implemented. These are left over from before +----------------------------+ - | ICeeGen | + | ICeeGenInternal | | | | COM-style version of | | ICeeFileGen. HCEEFILE is | @@ -174,16 +174,14 @@ class CeeSection { // Only handles in memory stuff // Base class for CeeFileGenWriter (which actually generates PEFiles) -class CCeeGen : public ICeeGen, ICeeGenInternal { +class CCeeGen : public ICeeGenInternal { LONG m_cRefs; - BOOL m_encMode; protected: short m_textIdx; // m_sections[] index for the .text section short m_metaIdx; // m_sections[] index for metadata (.text, or .cormeta for obj files) short m_corHdrIdx; // m_sections[] index for the COM+ header (.text0) short m_stringIdx; // m_sections[] index for strings (.text, or .rdata for EnC) short m_ilIdx; // m_sections[] index for IL (.text) - bool m_objSwitch; CeeGenTokenMapper *m_pTokenMap; BOOLEAN m_fTokenMapSupported; // temporary to support both models @@ -202,8 +200,6 @@ class CCeeGen : public ICeeGen, ICeeGenInternal { HRESULT addSection(CeeSection *section, short *sectionIdx); - HRESULT setEnCMode(); - // Init process: Call static CreateNewInstance() , not operator new protected: HRESULT Init(); @@ -217,7 +213,7 @@ class CCeeGen : public ICeeGen, ICeeGenInternal { virtual HRESULT Cleanup(); - // ICeeGen interfaces + // ICeeGenInternal interfaces ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); @@ -274,13 +270,7 @@ class CCeeGen : public ICeeGen, ICeeGenInternal { ULONG align=1, void **ppBytes=0); - STDMETHODIMP TruncateSection ( - HCEESECTION section, - ULONG len); - - STDMETHODIMP GenerateCeeMemoryImage (void **ppImage); - - STDMETHODIMP ComputePointer ( + STDMETHODIMP ComputePointer ( HCEESECTION section, ULONG RVA, // [IN] RVA for method to return UCHAR **lpBuffer); // [OUT] Returned buffer @@ -325,15 +315,6 @@ class CCeeGen : public ICeeGen, ICeeGenInternal { //Section data will be appended onto any information already in the section. //This is done to support the DynamicIL -> PersistedIL transform. virtual HRESULT cloneInstance(CCeeGen *destination); - -#ifdef EMIT_FIXUPS -public: - virtual HRESULT addFixup(CeeSection& sectionSource, unsigned offset, CeeSectionRelocType reloc, CeeSection * sectionTarget = NULL, CeeSectionRelocExtra *extra = 0) { - LIMITED_METHOD_CONTRACT; - - return(E_NOTIMPL); - } -#endif }; // ***** CeeSection inline methods diff --git a/src/coreclr/inc/clrconfig.h b/src/coreclr/inc/clrconfig.h index bd5e41cdbc0ae..97a76badaded6 100644 --- a/src/coreclr/inc/clrconfig.h +++ b/src/coreclr/inc/clrconfig.h @@ -6,13 +6,8 @@ // // -// Unified method of accessing configuration values from environment variables, registry and config file(s). -// This class replaces all GetConfigDWORD and GetConfigString methods in EEConfig and REGUTIL. To define a -// flag, add an entry in the table in file:CLRConfigValues.h. -// -// -// -// +// Unified method of accessing configuration values. +// To define a flag, add an entry in the table in file:CLRConfigValues.h. // -------------------------------------------------------------------------------------------------- @@ -25,28 +20,18 @@ class CLRConfig { public: - // - // Types - // + // Setting each option results in some change to the config value. + enum class LookupOptions + { + // Default options. + Default = 0, + + // If set, do not prepend prefix when doing environment variable lookup. + DontPrependPrefix = 0x1, - // Setting each option results in some change to the config value lookup method. Default behavior is (in - // the following order): - // * Look at environment variables (prepending COMPlus to the name) - // * Look at the framework registry keys (HKCU\Software\Microsoft\.NETFramework then - // HKLM\Software\Microsoft\.NETFramework) - // * Look at the available config files (system, application, host and user). For details see TODO: - // Link to BOTR documentation - enum LookupOptions { - // If set, don't look in environment variables. - IgnoreEnv = 0x1, - // If set, do not prepend "COMPlus_" when doing environment variable lookup. - DontPrependCOMPlus_ = 0x2, // Remove any whitespace at beginning and end of value. (Only applicable for // *string* configuration values.) - TrimWhiteSpaceFromStringValue = 0x100, - - // Legacy EEConfig-style lookup. - EEConfig_default = 0, + TrimWhiteSpaceFromStringValue = 0x2 }; // Struct used to store information about where/how to find a Config DWORD. @@ -82,14 +67,6 @@ class CLRConfig #define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ static const ConfigStringInfo symbol; - // TEMPORARY macros that declare strings. These are used for config value accesses that haven't been - // moved over to CLRConfig yet. Once all accesses have been moved, these macros (and corresponding - // instantiations in file:../utilcode/CLRConfig.cpp) should be removed. - #define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; - #define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; - // // Debug versions of the macros // @@ -102,18 +79,13 @@ class CLRConfig static const ConfigStringInfo symbol; #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ static const ConfigStringInfo symbol; - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - static const LPCWSTR symbol; #else #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) #define CONFIG_STRING_INFO(symbol, name, description) #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) #endif // _DEBUG + // Now that we have defined what what the macros in file:CLRConfigValues.h mean, include it to generate the code. #include "clrconfigvalues.h" @@ -121,14 +93,10 @@ class CLRConfig #undef RETAIL_CONFIG_STRING_INFO #undef RETAIL_CONFIG_DWORD_INFO_EX #undef RETAIL_CONFIG_STRING_INFO_EX - #undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS - #undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS #undef CONFIG_DWORD_INFO #undef CONFIG_STRING_INFO #undef CONFIG_DWORD_INFO_EX #undef CONFIG_STRING_INFO_EX - #undef CONFIG_DWORD_INFO_DIRECT_ACCESS - #undef CONFIG_STRING_INFO_DIRECT_ACCESS // // Methods to do config value (DWORD and String) lookups. @@ -139,7 +107,10 @@ class CLRConfig static DWORD GetConfigValue(const ConfigDWORDInfo & info); // Look up a DWORD config value. - static DWORD GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault); + static DWORD GetConfigValue(const ConfigDWORDInfo & info, /* [Out] */ bool *isDefault); + + // Look up a DWORD config value. + static DWORD GetConfigValue(const ConfigDWORDInfo & info, DWORD defaultValue); // Look up a string config value. // You own the string that's returned. @@ -159,39 +130,10 @@ class CLRConfig static BOOL IsConfigOptionSpecified(LPCWSTR name); // Free a string returned by GetConfigValue - static void FreeConfigString(__in __in_z LPWSTR name); - -private: + static void FreeConfigString(__in __in_z LPWSTR name); - // Helper method to translate LookupOptions to REGUTIL::CORConfigLevel - static REGUTIL::CORConfigLevel GetConfigLevel(LookupOptions options); - - // - // Helper methods. - // - - // Helper method to check if a certain option is set in a ConfigDWORDInfo struct. - static inline BOOL CheckLookupOption(const ConfigDWORDInfo & info, LookupOptions option) - { - LIMITED_METHOD_CONTRACT; - return ((info.options & option) == option) ? TRUE : FALSE; - } - - // Helper method to check if a certain option is set in a ConfigStringInfo struct. - static inline BOOL CheckLookupOption(const ConfigStringInfo & info, LookupOptions option) - { - LIMITED_METHOD_CONTRACT; - return ((info.options & option) == option) ? TRUE : FALSE; - } - - // Helper method to check if a certain option is set in an options enum. - static inline BOOL CheckLookupOption(LookupOptions infoOptions, LookupOptions optionToCheck) - { - LIMITED_METHOD_CONTRACT; - return ((infoOptions & optionToCheck) == optionToCheck) ? TRUE : FALSE; - } - - static HRESULT TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed); + // Initialize the configuration. + static void Initialize(); }; inline CLRConfig::LookupOptions operator|(CLRConfig::LookupOptions lhs, CLRConfig::LookupOptions rhs) @@ -199,6 +141,11 @@ inline CLRConfig::LookupOptions operator|(CLRConfig::LookupOptions lhs, CLRConfi return static_cast(static_cast(lhs) | static_cast(rhs)); } +inline CLRConfig::LookupOptions operator&(CLRConfig::LookupOptions lhs, CLRConfig::LookupOptions rhs) +{ + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + typedef Wrapper CLRConfigStringHolder; #endif //__CLRConfig_h__ diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index 6051c2aabd550..2a0de94724c9a 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -9,7 +9,7 @@ // registry and config file. // // Given any config knob below that looks like this example: -// RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogEnable, W("LogEnable"), "Turns on the traditional CLR log.") +// RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogEnable, W("LogEnable"), 0, "Turns on the traditional CLR log.") // --------- // | // -------------------- @@ -43,7 +43,7 @@ // CONFIG_DWORD_INFO(symbol, name, defaultValue, description) // -------------------------------------------------------------------------- // Use this macro to define a basic DWORD value. CLRConfig will look in environment variables (adding -// COMPlus_ to the name), the registry (HKLM and HKCU), and all the config files for this value. To customize +// COMPlus_ to the name) for this value. To customize // where CLRConfig looks, use the extended version of the macro below. IMPORTANT: please follow the // code:#NamingConventions for the symbol and the name! // @@ -56,7 +56,7 @@ // of options and their descriptions, see code:CLRConfig.LookupOptions // // Example: CONFIG_DWORD_INFO_EX(INTERNAL_EnableInternetHREFexes, W("EnableInternetHREFexes"), 0, "", -// (CLRConfig::LookupOptions) (CLRConfig::IgnoreEnv | CLRConfig::IgnoreHKCU)) +// (CLRConfig::LookupOptions) (CLRConfig::LookupOptions::DontPrependPrefix)) // // #Strings: // -------------------------------------------------------------------------- @@ -112,12 +112,7 @@ /// /// AppDomain /// -CONFIG_DWORD_INFO(INTERNAL_ADDumpSB, W("ADDumpSB"), 0, "Not used") -CONFIG_DWORD_INFO(INTERNAL_ADForceSB, W("ADForceSB"), 0, "Forces sync block creation for all objects") -CONFIG_DWORD_INFO(INTERNAL_ADLogMemory, W("ADLogMemory"), 0, "Superseded by test hooks") -CONFIG_DWORD_INFO(INTERNAL_ADTakeDHSnapShot, W("ADTakeDHSnapShot"), 0, "Superseded by test hooks") -CONFIG_DWORD_INFO(INTERNAL_ADTakeSnapShot, W("ADTakeSnapShot"), 0, "Superseded by test hooks") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_EnableFullDebug, W("EnableFullDebug"), "Heavy-weight checking for AD boundary violations (AD leaks)") +CONFIG_DWORD_INFO(INTERNAL_EnableFullDebug, W("EnableFullDebug"), 0, "Heavy-weight checking for AD boundary violations (AD leaks)") /// /// Jit Pitching @@ -133,103 +128,102 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitPitchMaxVal, W("JitPitchMaxVal"), (DWORD)0x /// /// Assembly Loader /// -CONFIG_DWORD_INFO_EX(INTERNAL_GetAssemblyIfLoadedIgnoreRidMap, W("GetAssemblyIfLoadedIgnoreRidMap"), 0, "Used to force loader to ignore assemblies cached in the rid-map", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_GetAssemblyIfLoadedIgnoreRidMap, W("GetAssemblyIfLoadedIgnoreRidMap"), 0, "Used to force loader to ignore assemblies cached in the rid-map") /// /// Conditional breakpoints /// -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnBadExit, W("BreakOnBadExit"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnBadExit, W("BreakOnBadExit"), 0, "") CONFIG_STRING_INFO(INTERNAL_BreakOnClassBuild, W("BreakOnClassBuild"), "Very useful for debugging class layout code.") CONFIG_STRING_INFO(INTERNAL_BreakOnClassLoad, W("BreakOnClassLoad"), "Very useful for debugging class loading code.") CONFIG_STRING_INFO(INTERNAL_BreakOnComToClrNativeInfoInit, W("BreakOnComToClrNativeInfoInit"), "Throws an assert when native information about a COM -> CLR call are about to be gathered.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDebugBreak, W("BreakOnDebugBreak"), 0, "Allows an assert in debug builds when a user break is hit", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDILoad, W("BreakOnDILoad"), 0, "Allows an assert when the DI is loaded", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnDumpToken, W("BreakOnDumpToken"), 0xffffffff, "Breaks when using internal logging on a particular token value.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnEELoad, W("BreakOnEELoad"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnDebugBreak, W("BreakOnDebugBreak"), 0, "Allows an assert in debug builds when a user break is hit") +CONFIG_DWORD_INFO(INTERNAL_BreakOnDILoad, W("BreakOnDILoad"), 0, "Allows an assert when the DI is loaded") +CONFIG_DWORD_INFO(INTERNAL_BreakOnDumpToken, W("BreakOnDumpToken"), 0xffffffff, "Breaks when using internal logging on a particular token value.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnEELoad, W("BreakOnEELoad"), 0, "") CONFIG_DWORD_INFO(INTERNAL_BreakOnEEShutdown, W("BreakOnEEShutdown"), 0, "") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnExceptionInGetThrowable, W("BreakOnExceptionInGetThrowable"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnExceptionInGetThrowable, W("BreakOnExceptionInGetThrowable"), 0, "") CONFIG_DWORD_INFO(INTERNAL_BreakOnFindMethod, W("BreakOnFindMethod"), 0, "Breaks in findMethodInternal when it searches for the specified token.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnFirstPass, W("BreakOnFirstPass"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnHR, W("BreakOnHR"), 0, "Debug.cpp, IfFailxxx use this macro to stop if hr matches ", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnFirstPass, W("BreakOnFirstPass"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_BreakOnHR, W("BreakOnHR"), 0, "Debug.cpp, IfFailxxx use this macro to stop if hr matches ") CONFIG_STRING_INFO(INTERNAL_BreakOnInstantiation, W("BreakOnInstantiation"), "Very useful for debugging generic class instantiation.") CONFIG_STRING_INFO(INTERNAL_BreakOnInteropStubSetup, W("BreakOnInteropStubSetup"), "Throws an assert when marshaling stub for the given method is about to be built.") -CONFIG_STRING_INFO_EX(INTERNAL_BreakOnInteropVTableBuild, W("BreakOnInteropVTableBuild"), "Specifies a type name for which an assert should be thrown when building interop v-table.", CLRConfig::EEConfig_default) +CONFIG_STRING_INFO(INTERNAL_BreakOnInteropVTableBuild, W("BreakOnInteropVTableBuild"), "Specifies a type name for which an assert should be thrown when building interop v-table.") CONFIG_STRING_INFO(INTERNAL_BreakOnMethodName, W("BreakOnMethodName"), "Very useful for debugging method override placement code.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnNotify, W("BreakOnNotify"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnRetailAssert, W("BreakOnRetailAssert"), 0, "Used for debugging \"retail\" asserts (fatal errors)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnSecondPass, W("BreakOnSecondPass"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_BreakOnSO, W("BreakOnSO"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnNotify, W("BreakOnNotify"), 0, "") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_BreakOnRetailAssert, W("BreakOnRetailAssert"), 0, "Used for debugging \"retail\" asserts (fatal errors)") +CONFIG_DWORD_INFO(INTERNAL_BreakOnSecondPass, W("BreakOnSecondPass"), 0, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_BreakOnSO, W("BreakOnSO"), 0, "") CONFIG_STRING_INFO(INTERNAL_BreakOnStructMarshalSetup, W("BreakOnStructMarshalSetup"), "Throws an assert when field marshalers for the given type with layout are about to be created.") -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnUEF, W("BreakOnUEF"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_BreakOnUncaughtException, W("BreakOnUncaughtException"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_BreakOnUEF, W("BreakOnUEF"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_BreakOnUncaughtException, W("BreakOnUncaughtException"), 0, "") /// - /// Debugger /// -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_EnableDiagnostics, W("EnableDiagnostics"), 1, "Allows the debugger, profiler, and EventPipe diagnostics to be disabled", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_D__FCE, W("D::FCE"), 0, "Allows an assert when crawling the managed stack for an exception handler", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakIfLocksUnavailable, W("DbgBreakIfLocksUnavailable"), 0, "Allows an assert when the debugger can't take a lock ", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnErr, W("DbgBreakOnErr"), 0, "Allows an assert when we get a failing hresult", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnMapPatchToDJI, W("DbgBreakOnMapPatchToDJI"), 0, "Allows an assert when mapping a patch to an address", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnRawInt3, W("DbgBreakOnRawInt3"), 0, "Allows an assert for test coverage for debug break or other int3 breaks", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnSendBreakpoint, W("DbgBreakOnSendBreakpoint"), 0, "Allows an assert when sending a breakpoint to the right side", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgBreakOnSetIP, W("DbgBreakOnSetIP"), 0, "Allows an assert when setting the IP", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgCheckInt3, W("DbgCheckInt3"), 0, "Asserts if the debugger explicitly writes int3 instead of calling SetUnmanagedBreakpoint", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_DbgDACAssertOnMismatch, W("DbgDACAssertOnMismatch"), "Allows an assert when the mscordacwks and mscorwks dll versions don't match") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgDACEnableAssert, W("DbgDACEnableAssert"), 0, "Enables extra validity checking in DAC - assumes target isn't corrupt", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgDACSkipVerifyDlls, W("DbgDACSkipVerifyDlls"), 0, "Allows disabling the check to ensure mscordacwks and mscorwks dll versions match", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgDelayHelper, W("DbgDelayHelper"), 0, "Varies the wait in the helper thread startup for testing race between threads", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgDisableDynamicSymsCompat, W("DbgDisableDynamicSymsCompat"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgDisableTargetConsistencyAsserts, W("DbgDisableTargetConsistencyAsserts"), 0, "Allows explicitly testing with corrupt targets", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_DbgEnableMixedModeDebugging, W("DbgEnableMixedModeDebuggingInternalOnly"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreads, W("DbgExtraThreads"), 0, "Allows extra unmanaged threads to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreadsCantStop, W("DbgExtraThreadsCantStop"), 0, "Allows extra unmanaged threads in can't stop region to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreadsIB, W("DbgExtraThreadsIB"), 0, "Allows extra in-band unmanaged threads to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgExtraThreadsOOB, W("DbgExtraThreadsOOB"), 0, "Allows extra out of band unmanaged threads to run and throw debug events for stress testing", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgFaultInHandleIPCEvent, W("DbgFaultInHandleIPCEvent"), 0, "Allows testing the unhandled event filter", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgInjectFEE, W("DbgInjectFEE"), 0, "Allows injecting a fatal execution error for testing Watson", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgLeakCheck, W("DbgLeakCheck"), 0, "Allows checking for leaked Cordb objects", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNo2ndChance, W("DbgNo2ndChance"), 0, "Allows breaking on (and catching bogus) 2nd chance exceptions", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNoDebugger, W("DbgNoDebugger"), 0, "Allows breaking if we don't want to lazily initialize the debugger", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgNoForceContinue, W("DbgNoForceContinue"), 1, "Used to force a continue on longhorn", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNoOpenMDByFile, W("DbgNoOpenMDByFile"), 0, "Allows opening MD by memory for perf testing", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableDiagnostics, W("EnableDiagnostics"), 1, "Allows the debugger, profiler, and EventPipe diagnostics to be disabled") +CONFIG_DWORD_INFO(INTERNAL_D__FCE, W("D::FCE"), 0, "Allows an assert when crawling the managed stack for an exception handler") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakIfLocksUnavailable, W("DbgBreakIfLocksUnavailable"), 0, "Allows an assert when the debugger can't take a lock ") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnErr, W("DbgBreakOnErr"), 0, "Allows an assert when we get a failing hresult") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnMapPatchToDJI, W("DbgBreakOnMapPatchToDJI"), 0, "Allows an assert when mapping a patch to an address") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnRawInt3, W("DbgBreakOnRawInt3"), 0, "Allows an assert for test coverage for debug break or other int3 breaks") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnSendBreakpoint, W("DbgBreakOnSendBreakpoint"), 0, "Allows an assert when sending a breakpoint to the right side") +CONFIG_DWORD_INFO(INTERNAL_DbgBreakOnSetIP, W("DbgBreakOnSetIP"), 0, "Allows an assert when setting the IP") +CONFIG_DWORD_INFO(INTERNAL_DbgCheckInt3, W("DbgCheckInt3"), 0, "Asserts if the debugger explicitly writes int3 instead of calling SetUnmanagedBreakpoint") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgForcePDBSymbols, W("DbgForcePDBSymbols"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_DbgDACAssertOnMismatch, W("DbgDACAssertOnMismatch"), 0, "Allows an assert when the mscordacwks and mscorwks dll versions don't match") +CONFIG_DWORD_INFO(INTERNAL_DbgDACEnableAssert, W("DbgDACEnableAssert"), 0, "Enables extra validity checking in DAC - assumes target isn't corrupt") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgDACSkipVerifyDlls, W("DbgDACSkipVerifyDlls"), 0, "Allows disabling the check to ensure mscordacwks and mscorwks dll versions match") +CONFIG_DWORD_INFO(INTERNAL_DbgDelayHelper, W("DbgDelayHelper"), 0, "Varies the wait in the helper thread startup for testing race between threads") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgDisableDynamicSymsCompat, W("DbgDisableDynamicSymsCompat"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_DbgDisableTargetConsistencyAsserts, W("DbgDisableTargetConsistencyAsserts"), 0, "Allows explicitly testing with corrupt targets") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_DbgEnableMixedModeDebugging, W("DbgEnableMixedModeDebuggingInternalOnly"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreads, W("DbgExtraThreads"), 0, "Allows extra unmanaged threads to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreadsCantStop, W("DbgExtraThreadsCantStop"), 0, "Allows extra unmanaged threads in can't stop region to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreadsIB, W("DbgExtraThreadsIB"), 0, "Allows extra in-band unmanaged threads to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgExtraThreadsOOB, W("DbgExtraThreadsOOB"), 0, "Allows extra out of band unmanaged threads to run and throw debug events for stress testing") +CONFIG_DWORD_INFO(INTERNAL_DbgFaultInHandleIPCEvent, W("DbgFaultInHandleIPCEvent"), 0, "Allows testing the unhandled event filter") +CONFIG_DWORD_INFO(INTERNAL_DbgInjectFEE, W("DbgInjectFEE"), 0, "Allows injecting a fatal execution error for testing Watson") +CONFIG_DWORD_INFO(INTERNAL_DbgLeakCheck, W("DbgLeakCheck"), 0, "Allows checking for leaked Cordb objects") +CONFIG_DWORD_INFO(INTERNAL_DbgNo2ndChance, W("DbgNo2ndChance"), 0, "Allows breaking on (and catching bogus) 2nd chance exceptions") +CONFIG_DWORD_INFO(INTERNAL_DbgNoDebugger, W("DbgNoDebugger"), 0, "Allows breaking if we don't want to lazily initialize the debugger") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgNoForceContinue, W("DbgNoForceContinue"), 1, "Used to force a continue on longhorn") +CONFIG_DWORD_INFO(INTERNAL_DbgNoOpenMDByFile, W("DbgNoOpenMDByFile"), 0, "Allows opening MD by memory for perf testing") CONFIG_DWORD_INFO(INTERNAL_DbgOOBinFEEE, W("DbgOOBinFEEE"), 0, "Allows forcing oob breakpoints when a fatal error occurs") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgPackShimPath, W("DbgPackShimPath"), "CoreCLR path to dbgshim.dll - we are trying to figure out if we can remove this") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgPingInterop, W("DbgPingInterop"), 0, "Allows checking for deadlocks in interop debugging", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgRace, W("DbgRace"), 0, "Allows pausing for native debug events to get hijicked", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgRedirect, W("DbgRedirect"), 0, "Allows for redirecting the event pipeline", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectApplication, W("DbgRedirectApplication"), "Specifies the auxiliary debugger application to launch.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectAttachCmd, W("DbgRedirectAttachCmd"), "Specifies command parameters for attaching the auxiliary debugger.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectCommonCmd, W("DbgRedirectCommonCmd"), "Specifies a command line format string for the auxiliary debugger.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(EXTERNAL_DbgRedirectCreateCmd, W("DbgRedirectCreateCmd"), "Specifies command parameters when creating the auxiliary debugger.") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgShortcutCanary, W("DbgShortcutCanary"), 0, "Allows a way to force canary to fail to be able to test failure paths", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgSkipMEOnStep, W("DbgSkipMEOnStep"), 0, "Turns off MethodEnter checks", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgSkipVerCheck, W("DbgSkipVerCheck"), 0, "Allows different RS and LS versions (for servicing work)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgTC, W("DbgTC"), 0, "Allows checking boundary compression for offset mappings", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgTransportFaultInject, W("DbgTransportFaultInject"), 0, "Allows injecting a fault for testing the debug transport", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_DbgTransportLog, W("DbgTransportLog"), "Turns on logging for the debug transport") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_DbgTransportLogClass, W("DbgTransportLogClass"), "Mask to control what is logged in DbgTransportLog") -RETAIL_CONFIG_STRING_INFO_EX(UNSUPPORTED_DbgTransportProxyAddress, W("DbgTransportProxyAddress"), "Allows specifying the transport proxy address", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgTrapOnSkip, W("DbgTrapOnSkip"), 0, "Allows breaking when we skip a breakpoint", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DbgWaitTimeout, W("DbgWaitTimeout"), 1, "Specifies the timeout value for waits", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgWFDETimeout, W("DbgWFDETimeout"), 25, "Specifies the timeout value for wait when waiting for a debug event", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_RaiseExceptionOnAssert, W("RaiseExceptionOnAssert"), 0, "Raise a first chance (if set to 1) or second chance (if set to 2) exception on asserts.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DebugBreakOnAssert, W("DebugBreakOnAssert"), 0, "If DACCESS_COMPILE is defined, break on asserts.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_DebugBreakOnVerificationFailure, W("DebugBreakOnVerificationFailure"), 0, "Halts the jit on verification failure", CLRConfig::EEConfig_default) -CONFIG_STRING_INFO_EX(INTERNAL_DebuggerBreakPoint, W("DebuggerBreakPoint"), "Allows counting various debug events", CLRConfig::EEConfig_default) -CONFIG_STRING_INFO_EX(INTERNAL_DebugVerify, W("DebugVerify"), "Control for tracing in peverify", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgPingInterop, W("DbgPingInterop"), 0, "Allows checking for deadlocks in interop debugging") +CONFIG_DWORD_INFO(INTERNAL_DbgRace, W("DbgRace"), 0, "Allows pausing for native debug events to get hijicked") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgRedirect, W("DbgRedirect"), 0, "Allows for redirecting the event pipeline") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectApplication, W("DbgRedirectApplication"), "Specifies the auxiliary debugger application to launch.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectAttachCmd, W("DbgRedirectAttachCmd"), "Specifies command parameters for attaching the auxiliary debugger.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectCommonCmd, W("DbgRedirectCommonCmd"), "Specifies a command line format string for the auxiliary debugger.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DbgRedirectCreateCmd, W("DbgRedirectCreateCmd"), "Specifies command parameters when creating the auxiliary debugger.") +CONFIG_DWORD_INFO(INTERNAL_DbgShortcutCanary, W("DbgShortcutCanary"), 0, "Allows a way to force canary to fail to be able to test failure paths") +CONFIG_DWORD_INFO(INTERNAL_DbgSkipMEOnStep, W("DbgSkipMEOnStep"), 0, "Turns off MethodEnter checks") +CONFIG_DWORD_INFO(INTERNAL_DbgSkipVerCheck, W("DbgSkipVerCheck"), 0, "Allows different RS and LS versions (for servicing work)") +CONFIG_DWORD_INFO(INTERNAL_DbgTC, W("DbgTC"), 0, "Allows checking boundary compression for offset mappings") +CONFIG_DWORD_INFO(INTERNAL_DbgTransportFaultInject, W("DbgTransportFaultInject"), 0, "Allows injecting a fault for testing the debug transport") +CONFIG_DWORD_INFO(INTERNAL_DbgTransportLog, W("DbgTransportLog"), 0 /* LE_None */, "Turns on logging for the debug transport") +CONFIG_DWORD_INFO(INTERNAL_DbgTransportLogClass, W("DbgTransportLogClass"), (DWORD)-1 /* LC_All */, "Mask to control what is logged in DbgTransportLog") +RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_DbgTransportProxyAddress, W("DbgTransportProxyAddress"), "Allows specifying the transport proxy address") +CONFIG_DWORD_INFO(INTERNAL_DbgTrapOnSkip, W("DbgTrapOnSkip"), 0, "Allows breaking when we skip a breakpoint") +CONFIG_DWORD_INFO(INTERNAL_DbgWaitTimeout, W("DbgWaitTimeout"), 1, "Specifies the timeout value for waits") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgWFDETimeout, W("DbgWFDETimeout"), 25, "Specifies the timeout value for wait when waiting for a debug event") +CONFIG_DWORD_INFO(INTERNAL_RaiseExceptionOnAssert, W("RaiseExceptionOnAssert"), 0, "Raise a first chance (if set to 1) or second chance (if set to 2) exception on asserts.") +CONFIG_DWORD_INFO(INTERNAL_DebugBreakOnAssert, W("DebugBreakOnAssert"), 0, "If DACCESS_COMPILE is defined, break on asserts.") +CONFIG_DWORD_INFO(INTERNAL_DebugBreakOnVerificationFailure, W("DebugBreakOnVerificationFailure"), 0, "Halts the jit on verification failure") +CONFIG_STRING_INFO(INTERNAL_DebuggerBreakPoint, W("DebuggerBreakPoint"), "Allows counting various debug events") +CONFIG_STRING_INFO(INTERNAL_DebugVerify, W("DebugVerify"), "Control for tracing in peverify") CONFIG_DWORD_INFO(INTERNAL_EncApplyChanges, W("EncApplyChanges"), 0, "Allows breaking when ApplyEditAndContinue is called") -CONFIG_DWORD_INFO_EX(INTERNAL_EnCBreakOnRemapComplete, W("EnCBreakOnRemapComplete"), 0, "Allows breaking after N RemapCompletes", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_EnCBreakOnRemapOpportunity, W("EnCBreakOnRemapOpportunity"), 0, "Allows breaking after N RemapOpportunities", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_EnCBreakOnRemapComplete, W("EnCBreakOnRemapComplete"), 0, "Allows breaking after N RemapCompletes") +CONFIG_DWORD_INFO(INTERNAL_EnCBreakOnRemapOpportunity, W("EnCBreakOnRemapOpportunity"), 0, "Allows breaking after N RemapOpportunities") CONFIG_DWORD_INFO(INTERNAL_EncDumpApplyChanges, W("EncDumpApplyChanges"), 0, "Allows dumping edits in delta metadata and il files") CONFIG_DWORD_INFO(INTERNAL_EncFixupFieldBreak, W("EncFixupFieldBreak"), 0, "Unlikely that this is used anymore.") CONFIG_DWORD_INFO(INTERNAL_EncJitUpdatedFunction, W("EncJitUpdatedFunction"), 0, "Allows breaking when an updated function is jitted") CONFIG_DWORD_INFO(INTERNAL_EnCResolveField, W("EnCResolveField"), 0, "Allows breaking when computing the address of an EnC-added field") CONFIG_DWORD_INFO(INTERNAL_EncResumeInUpdatedFunction, W("EncResumeInUpdatedFunction"), 0, "Allows breaking when execution resumes in a new EnC version of a function") -CONFIG_DWORD_INFO_EX(INTERNAL_DbgAssertOnDebuggeeDebugBreak, W("DbgAssertOnDebuggeeDebugBreak"), 0, "If non-zero causes the managed-only debugger to assert on unhandled breakpoints in the debuggee", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgAssertOnDebuggeeDebugBreak, W("DbgAssertOnDebuggeeDebugBreak"), 0, "If non-zero causes the managed-only debugger to assert on unhandled breakpoints in the debuggee") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException, W("UNSUPPORTED_DbgDontResumeThreadsOnUnhandledException"), 0, "If non-zero, then don't try to unsuspend threads after continuing a 2nd-chance native exception") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DbgSkipStackCheck, W("DbgSkipStackCheck"), 0, "Skip the stack pointer check during stackwalking", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DbgSkipStackCheck, W("DbgSkipStackCheck"), 0, "Skip the stack pointer check during stackwalking") #ifdef DACCESS_COMPILE CONFIG_DWORD_INFO(INTERNAL_DumpGeneration_IntentionallyCorruptDataFromTarget, W("IntentionallyCorruptDataFromTarget"), 0, "Intentionally fakes bad data retrieved from target to try and break dump generation.") #endif @@ -240,18 +234,17 @@ CONFIG_DWORD_INFO(UNSUPPORTED_Debugging_RequiredVersion, W("UNSUPPORTED_Debuggin RETAIL_CONFIG_DWORD_INFO(INTERNAL_MiniMdBufferCapacity, W("MiniMdBufferCapacity"), 64 * 1024, "The max size of the buffer to store mini metadata information for triage- and mini-dumps.") #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS -CONFIG_DWORD_INFO_EX(INTERNAL_DbgNativeCodeBpBindsAcrossVersions, W("DbgNativeCodeBpBindsAcrossVersions"), 0, "If non-zero causes native breakpoints at offset 0 to bind in all tiered compilation versions of the given method", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_DbgNativeCodeBpBindsAcrossVersions, W("DbgNativeCodeBpBindsAcrossVersions"), 0, "If non-zero causes native breakpoints at offset 0 to bind in all tiered compilation versions of the given method") /// /// Diagnostics (internal general-purpose) /// -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_ConditionalContracts, W("ConditionalContracts"), "If ENABLE_CONTRACTS_IMPL is defined, sets whether contracts are conditional. (?)") +CONFIG_DWORD_INFO(INTERNAL_ConditionalContracts, W("ConditionalContracts"), 0, "If ENABLE_CONTRACTS_IMPL is defined, sets whether contracts are conditional. (?)") CONFIG_DWORD_INFO(INTERNAL_ConsistencyCheck, W("ConsistencyCheck"), 0, "") -CONFIG_DWORD_INFO_EX(INTERNAL_ContinueOnAssert, W("ContinueOnAssert"), 0, "If set, doesn't break on asserts.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_disableStackOverflowProbing, W("disableStackOverflowProbing"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_InjectFatalError, W("InjectFatalError"), "") -CONFIG_DWORD_INFO_EX(INTERNAL_InjectFault, W("InjectFault"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_SuppressChecks, W("SuppressChecks"), "") +CONFIG_DWORD_INFO(INTERNAL_ContinueOnAssert, W("ContinueOnAssert"), 0, "If set, doesn't break on asserts.") +CONFIG_DWORD_INFO(INTERNAL_InjectFatalError, W("InjectFatalError"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_InjectFault, W("InjectFault"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_SuppressChecks, W("SuppressChecks"),0, "") #ifdef FEATURE_EH_FUNCLETS CONFIG_DWORD_INFO(INTERNAL_SuppressLockViolationsOnReentryFromOS, W("SuppressLockViolationsOnReentryFromOS"), 0, "64 bit OOM tests re-enter the CLR via RtlVirtualUnwind. This indicates whether to suppress resulting locking violations.") #endif // FEATURE_EH_FUNCLETS @@ -259,18 +252,18 @@ CONFIG_DWORD_INFO(INTERNAL_SuppressLockViolationsOnReentryFromOS, W("SuppressLoc /// /// Exception Handling /// -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_AssertOnFailFast, W("AssertOnFailFast"), "") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("legacyCorruptedStateExceptionsPolicy"), 0, "Enabled Pre-V4 CSE behavior", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_AssertOnFailFast, W("AssertOnFailFast"), 1, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_legacyCorruptedStateExceptionsPolicy, W("legacyCorruptedStateExceptionsPolicy"), 0, "Enabled Pre-V4 CSE behavior") +CONFIG_DWORD_INFO(INTERNAL_SuppressLostExceptionTypeAssert, W("SuppressLostExceptionTypeAssert"), 0, "") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_UseEntryPointFilter, W("UseEntryPointFilter"), 0, "") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_Corhost_Swallow_Uncaught_Exceptions, W("Corhost_Swallow_Uncaught_Exceptions"), 0, "") /// /// Garbage collector /// CONFIG_DWORD_INFO(INTERNAL_FastGCCheckStack, W("FastGCCheckStack"), 0, "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_FastGCStress, W("FastGCStress"), "Reduce the number of GCs done by enabling GCStress") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_GCBreakOnOOM, W("GCBreakOnOOM"), "Does a DebugBreak at the soonest time we detect an OOM") +CONFIG_DWORD_INFO(INTERNAL_FastGCStress, W("FastGCStress"), 0, "Reduce the number of GCs done by enabling GCStress") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCBreakOnOOM, W("GCBreakOnOOM"), 0, "Does a DebugBreak at the soonest time we detect an OOM") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_gcConcurrent, W("gcConcurrent"), (DWORD)-1, "Enables/Disables concurrent GC") #ifdef FEATURE_CONSERVATIVE_GC @@ -283,9 +276,9 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StatsUpdatePeriod, W("StatsUpdatePeriod"), RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCRetainVM, W("GCRetainVM"), 0, "When set we put the segments that should be deleted on a standby list (instead of releasing them back to the OS) which will be considered to satisfy new segment requests (note that the same thing can be specified via API which is the supported way)") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCLOHThreshold, W("GCLOHThreshold"), 0, "Specifies the size that will make objects go on LOH") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_gcAllowVeryLargeObjects, W("gcAllowVeryLargeObjects"), 1, "Allow allocation of 2GB+ objects on GC heap") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_GCStress, W("GCStress"), 0, "Trigger GCs at regular intervals", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, "Whether to trigger a GC on direct calls", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_HeapVerify, W("HeapVerify"), "When set verifies the integrity of the managed heap on entry and exit of each GC") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCStress, W("GCStress"), 0, "Trigger GCs at regular intervals") +CONFIG_DWORD_INFO(INTERNAL_GcStressOnDirectCalls, W("GcStressOnDirectCalls"), 0, "Whether to trigger a GC on direct calls") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_HeapVerify, W("HeapVerify"), 0, "When set verifies the integrity of the managed heap on entry and exit of each GC") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_GCNumaAware, W("GCNumaAware"), 1, "Specifies if to enable GC NUMA aware") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCCpuGroup, W("GCCpuGroup"), 0, "Specifies if to enable GC to support CPU groups") RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") @@ -293,33 +286,32 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "") /// /// IBC /// -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_ConvertIbcData, W("ConvertIbcData"), 1, "Converts between v1 and v2 IBC data", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_DisableHotCold, W("DisableHotCold"), "Master hot/cold splitting switch in Jit64") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_DisableIBC, W("DisableIBC"), 0, "Disables the use of IBC data", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_UseIBCFile, W("UseIBCFile"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ConvertIbcData, W("ConvertIbcData"), 1, "Converts between v1 and v2 IBC data") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DisableIBC, W("DisableIBC"), 0, "Disables the use of IBC data") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseIBCFile, W("UseIBCFile"), 0, "") /// /// JIT /// -CONFIG_DWORD_INFO_EX(INTERNAL_JitBreakEmit, W("JitBreakEmit"), (DWORD)-1, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_JitDebuggable, W("JitDebuggable"), "") +CONFIG_DWORD_INFO(INTERNAL_JitBreakEmit, W("JitBreakEmit"), (DWORD)-1, "") +CONFIG_DWORD_INFO(INTERNAL_JitDebuggable, W("JitDebuggable"), 0, "") #if !defined(DEBUG) && !defined(_DEBUG) #define INTERNAL_JitEnableNoWayAssert_Default 0 #else #define INTERNAL_JitEnableNoWayAssert_Default 1 #endif -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_JitFramed, W("JitFramed"), "Forces EBP frames") -CONFIG_DWORD_INFO_EX(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitEnableNoWayAssert, W("JitEnableNoWayAssert"), INTERNAL_JitEnableNoWayAssert_Default, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JitFramed, W("JitFramed"), 0, "Forces EBP frames") +CONFIG_DWORD_INFO(INTERNAL_JitGCStress, W("JitGCStress"), 0, "GC stress mode for jit") CONFIG_DWORD_INFO(INTERNAL_JitHeartbeat, W("JitHeartbeat"), 0, "") CONFIG_DWORD_INFO(INTERNAL_JitHelperLogging, W("JitHelperLogging"), 0, "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_JITMinOpts, W("JITMinOpts"), "Forces MinOpts") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_JITMinOpts, W("JITMinOpts"), 0, "Forces MinOpts") RETAIL_CONFIG_STRING_INFO(EXTERNAL_JitName, W("JitName"), "Primary Jit to use") #if defined(ALLOW_SXS_JIT) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJitName, W("AltJitName"), "Alternative Jit to use, will fall back to primary jit.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJit, W("AltJit"), "Enables AltJit and selectively limits it to the specified methods.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_AltJitExcludeAssemblies, W("AltJitExcludeAssemblies"), "Do not use AltJit on this semicolon-delimited list of assemblies.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(EXTERNAL_AltJitName, W("AltJitName"), "Alternative Jit to use, will fall back to primary jit.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_AltJit, W("AltJit"), "Enables AltJit and selectively limits it to the specified methods.") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_AltJitExcludeAssemblies, W("AltJitExcludeAssemblies"), "Do not use AltJit on this semicolon-delimited list of assemblies.") #endif // defined(ALLOW_SXS_JIT) #if defined(FEATURE_STACK_SAMPLING) @@ -330,21 +322,21 @@ RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StackSamplingNumMethods, W("StackSamplingNu #endif // defined(FEATURE_JIT_SAMPLING) #if defined(ALLOW_SXS_JIT_NGEN) -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_AltJitNgen, W("AltJitNgen"), "Enables AltJit for NGEN and selectively limits it to the specified methods.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_AltJitNgen, W("AltJitNgen"), "Enables AltJit for NGEN and selectively limits it to the specified methods.") #endif // defined(ALLOW_SXS_JIT_NGEN) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitHostMaxSlabCache, W("JitHostMaxSlabCache"), 0x1000000, "Sets jit host max slab cache size, 16MB default") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_JitOptimizeType, W("JitOptimizeType"), "") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitOptimizeType, W("JitOptimizeType"), 0 /* OPT_DEFAULT */, "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0, "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitTelemetry, W("JitTelemetry"), 1, "If non-zero, gather JIT telemetry data") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitTimeLogFile, W("JitTimeLogFile"), "If set, gather JIT throughput data and write to this file.") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitTimeLogCsv, W("JitTimeLogCsv"), "If set, gather JIT throughput data and write to a CSV file. This mode must be used in internal retail builds.") RETAIL_CONFIG_STRING_INFO(INTERNAL_JitFuncInfoLogFile, W("JitFuncInfoLogFile"), "If set, gather JIT function info and write to this file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_JitVerificationDisable, W("JitVerificationDisable"), "") +CONFIG_DWORD_INFO(INTERNAL_JitVerificationDisable, W("JitVerificationDisable"), 0, "") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitLockWrite, W("JitLockWrite"), 0, "Force all volatile writes to be 'locked'") -CONFIG_STRING_INFO_EX(INTERNAL_TailCallMax, W("TailCallMax"), "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_TailCallOpt, W("TailCallOpt"), "", CLRConfig::EEConfig_default) +CONFIG_STRING_INFO(INTERNAL_TailCallMax, W("TailCallMax"), "") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_TailCallOpt, W("TailCallOpt"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TailCallLoopOpt, W("TailCallLoopOpt"), 1, "Convert recursive tail calls to loops") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_Jit_NetFx40PInvokeStackResilience, W("NetFx40_PInvokeStackResilience"), (DWORD)-1, "Makes P/Invoke resilient against mismatched signature and calling convention (significant perf penalty).") @@ -354,10 +346,10 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_AltJitAssertOnNYI, W("AltJitAssertOnNYI"), 0, #else RETAIL_CONFIG_DWORD_INFO(INTERNAL_AltJitAssertOnNYI, W("AltJitAssertOnNYI"), 1, "Controls the AltJit behavior of NYI stuff") #endif -CONFIG_DWORD_INFO_EX(INTERNAL_JitLargeBranches, W("JitLargeBranches"), 0, "Force using the largest conditional branch format", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_JitRegisterFP, W("JitRegisterFP"), 3, "Control FP enregistration", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_JitLargeBranches, W("JitLargeBranches"), 0, "Force using the largest conditional branch format") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_JitRegisterFP, W("JitRegisterFP"), 3, "Control FP enregistration") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitELTHookEnabled, W("JitELTHookEnabled"), 0, "On ARM, setting this will emit Enter/Leave/TailCall callbacks") -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_JitMemStats, W("JitMemStats"), 0, "Display JIT memory usage statistics", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitMemStats, W("JitMemStats"), 0, "Display JIT memory usage statistics") RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitVNMapSelBudget, W("JitVNMapSelBudget"), 100, "Max # of MapSelect's considered for a particular top-level invocation.") #if defined(TARGET_AMD64) || defined(TARGET_X86) || defined(TARGET_ARM64) #define EXTERNAL_FeatureSIMD_Default 1 @@ -369,10 +361,10 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_JitVNMapSelBudget, W("JitVNMapSelBudget"), 100 #else // !(defined(TARGET_AMD64) || defined(TARGET_X86) #define EXTERNAL_JitEnableAVX_Default 0 #endif // !(defined(TARGET_AMD64) || defined(TARGET_X86) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_FeatureSIMD, W("FeatureSIMD"), EXTERNAL_FeatureSIMD_Default, "Enable SIMD intrinsics recognition in System.Numerics.dll and/or System.Numerics.Vectors.dll", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_FeatureSIMD, W("FeatureSIMD"), EXTERNAL_FeatureSIMD_Default, "Enable SIMD intrinsics recognition in System.Numerics.dll and/or System.Numerics.Vectors.dll") RETAIL_CONFIG_DWORD_INFO(INTERNAL_SIMD16ByteOnly, W("SIMD16ByteOnly"), 0, "Limit maximum SIMD vector length to 16 bytes (used by x64_arm64_altjit)") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_EnableAVX, W("EnableAVX"), EXTERNAL_JitEnableAVX_Default, "Enable AVX instruction set for wide operations as default", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_TrackDynamicMethodDebugInfo, W("TrackDynamicMethodDebugInfo"), 0, "Specifies whether debug info should be generated and tracked for dynamic methods", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableAVX, W("EnableAVX"), EXTERNAL_JitEnableAVX_Default, "Enable AVX instruction set for wide operations as default") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TrackDynamicMethodDebugInfo, W("TrackDynamicMethodDebugInfo"), 0, "Specifies whether debug info should be generated and tracked for dynamic methods") #ifdef FEATURE_MULTICOREJIT @@ -385,18 +377,18 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_MultiCoreJitProfileWriteDelay, W("MultiCoreJit /// /// Interpreter /// -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_Interpret, W("Interpret"), "Selectively uses the interpreter to execute the specified methods", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_InterpretExclude, W("InterpretExclude"), "Excludes the specified methods from the set selected by 'Interpret'", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_Interpret, W("Interpret"), "Selectively uses the interpreter to execute the specified methods") +RETAIL_CONFIG_STRING_INFO(INTERNAL_InterpretExclude, W("InterpretExclude"), "Excludes the specified methods from the set selected by 'Interpret'") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterMethHashMin, W("InterpreterMethHashMin"), 0, "Only interpret methods selected by 'Interpret' whose hash is at least this value. or after nth") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterMethHashMax, W("InterpreterMethHashMax"), UINT32_MAX, "If non-zero, only interpret methods selected by 'Interpret' whose hash is at most this value") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterStubMin, W("InterpreterStubMin"), 0, "Only interpret methods selected by 'Interpret' whose stub num is at least this value.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterStubMax, W("InterpreterStubMax"), UINT32_MAX, "If non-zero, only interpret methods selected by 'Interpret' whose stub number is at most this value.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterJITThreshold, W("InterpreterJITThreshold"), 10, "The number of times a method should be interpreted before being JITted") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterDoLoopMethods, W("InterpreterDoLoopMethods"), 0, "If set, don't check for loops, start by interpreting *all* methods") -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_InterpreterUseCaching, W("InterpreterUseCaching"), 1, "If non-zero, use the caching mechanism.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_InterpreterLooseRules, W("InterpreterLooseRules"), 1, "If non-zero, allow ECMA spec violations required by managed C++.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterUseCaching, W("InterpreterUseCaching"), 1, "If non-zero, use the caching mechanism.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterLooseRules, W("InterpreterLooseRules"), 1, "If non-zero, allow ECMA spec violations required by managed C++.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterPrintPostMortem, W("InterpreterPrintPostMortem"), 0, "Prints summary information about the execution to the console") -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_InterpreterLogFile, W("InterpreterLogFile"), "If non-null, append interpreter logging to this file, else use stdout", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_InterpreterLogFile, W("InterpreterLogFile"), "If non-null, append interpreter logging to this file, else use stdout") RETAIL_CONFIG_DWORD_INFO(INTERNAL_DumpInterpreterStubs, W("DumpInterpreterStubs"), 0, "Prints all interpreter stubs that are created to the console") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TraceInterpreterEntries, W("TraceInterpreterEntries"), 0, "Logs entries to interpreted methods to the console") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TraceInterpreterIL, W("TraceInterpreterIL"), 0, "Logs individual instructions of interpreted methods to the console") @@ -409,14 +401,10 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterHWIntrinsicsIsSupportedFalse, W("In // The JIT queries this ConfigDWORD but it doesn't know if FEATURE_INTERPRETER is enabled RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpreterFallback, W("InterpreterFallback"), 0, "Fallback to the interpreter when the JIT compiler fails") -/// -/// Loader -/// - /// /// Loader heap /// -CONFIG_DWORD_INFO_EX(INTERNAL_LoaderHeapCallTracing, W("LoaderHeapCallTracing"), 0, "Loader heap troubleshooting", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_LoaderHeapCallTracing, W("LoaderHeapCallTracing"), 0, "Loader heap troubleshooting") RETAIL_CONFIG_DWORD_INFO(INTERNAL_CodeHeapReserveForJumpStubs, W("CodeHeapReserveForJumpStubs"), 1, "Percentage of code heap to reserve for jump stubs") RETAIL_CONFIG_DWORD_INFO(INTERNAL_NGenReserveForJumpStubs, W("NGenReserveForJumpStubs"), 0, "Percentage of ngen image size to reserve for jump stubs") RETAIL_CONFIG_DWORD_INFO(INTERNAL_BreakOnOutOfMemoryWithinRange, W("BreakOnOutOfMemoryWithinRange"), 0, "Break before out of memory within range exception is thrown") @@ -424,48 +412,48 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_BreakOnOutOfMemoryWithinRange, W("BreakOnOutOf /// /// Log /// -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogEnable, W("LogEnable"), "Turns on the traditional CLR log.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFacility, W("LogFacility"), "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFacility2, W("LogFacility2"), "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogEnable, W("LogEnable"), 0, "Turns on the traditional CLR log.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogFacility, W("LogFacility"), 0, "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogFacility2, W("LogFacility2"), 0, "Specifies a facility mask for CLR log. (See 'loglf.h'; VM interprets string value as hex number.) Also used by stresslog.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_logFatalError, W("logFatalError"), 1, "Specifies whether EventReporter logs fatal errors in the Windows event log.") -CONFIG_STRING_INFO_EX(INTERNAL_LogFile, W("LogFile"), "Specifies a file name for the CLR log.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFileAppend, W("LogFileAppend"), "Specifies whether to append to or replace the CLR log file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogFlushFile, W("LogFlushFile"), "Specifies whether to flush the CLR log file on each write.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_LogLevel, W("LogLevel"), "4=10 msgs, 9=1000000, 10=everything") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogToConsole, W("LogToConsole"), "Writes the CLR log to console.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogToDebugger, W("LogToDebugger"), "Writes the CLR log to debugger (OutputDebugStringA).") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogToFile, W("LogToFile"), "Writes the CLR log to a file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_LogWithPid, W("LogWithPid"), "Appends pid to filename for the CLR log.") +CONFIG_STRING_INFO(INTERNAL_LogFile, W("LogFile"), "Specifies a file name for the CLR log.") +CONFIG_DWORD_INFO(INTERNAL_LogFileAppend, W("LogFileAppend"), 0 , "Specifies whether to append to or replace the CLR log file.") +CONFIG_DWORD_INFO(INTERNAL_LogFlushFile, W("LogFlushFile"), 0 , "Specifies whether to flush the CLR log file on each write.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_LogLevel, W("LogLevel"), 0 , "4=10 msgs, 9=1000000, 10=everything") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_LogToConsole, W("LogToConsole"), 0 , "Writes the CLR log to console.") +CONFIG_DWORD_INFO(INTERNAL_LogToDebugger, W("LogToDebugger"), 0 , "Writes the CLR log to debugger (OutputDebugStringA).") +CONFIG_DWORD_INFO(INTERNAL_LogToFile, W("LogToFile"), 0 , "Writes the CLR log to a file.") +CONFIG_DWORD_INFO(INTERNAL_LogWithPid, W("LogWithPid"), FALSE, "Appends pid to filename for the CLR log.") /// /// MetaData /// -CONFIG_DWORD_INFO_EX(INTERNAL_MD_ApplyDeltaBreak, W("MD_ApplyDeltaBreak"), 0, "ASSERT when applying EnC", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_AssertOnBadImageFormat, W("AssertOnBadImageFormat"), "ASSERT when invalid MD read") -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_MD_DeltaCheck, W("MD_DeltaCheck"), 1, "Some checks of GUID when applying EnC (?)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_EncDelta, W("MD_EncDelta"), 0, "Forces EnC Delta format in MD (?)", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_MD_ForceNoColDesSharing, W("MD_ForceNoColDesSharing"), 0, "Don't know - the only usage I could find is #if 0 (?)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_KeepKnownCA, W("MD_KeepKnownCA"), 0, "Something with known CAs (?)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_MiniMDBreak, W("MD_MiniMDBreak"), 0, "ASSERT when creating CMiniMdRw class", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_PreSaveBreak, W("MD_PreSaveBreak"), 0, "ASSERT when calling CMiniMdRw::PreSave", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_RegMetaBreak, W("MD_RegMetaBreak"), 0, "ASSERT when creating RegMeta class", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_MD_RegMetaDump, W("MD_RegMetaDump"), 0, "Dump MD in 4 functions (?)", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES, W("DOTNET_MODIFIABLE_ASSEMBLIES"), "Enables hot reload on debug built assemblies with the 'debug' keyword", CLRConfig::DontPrependCOMPlus_ | CLRConfig::TrimWhiteSpaceFromStringValue); +CONFIG_DWORD_INFO(INTERNAL_MD_ApplyDeltaBreak, W("MD_ApplyDeltaBreak"), 0, "ASSERT when applying EnC") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_AssertOnBadImageFormat, W("AssertOnBadImageFormat"), 0, "ASSERT when invalid MD read") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_MD_DeltaCheck, W("MD_DeltaCheck"), 1, "Some checks of GUID when applying EnC (?)") +CONFIG_DWORD_INFO(INTERNAL_MD_EncDelta, W("MD_EncDelta"), 0, "Forces EnC Delta format in MD (?)") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_MD_ForceNoColDesSharing, W("MD_ForceNoColDesSharing"), 0, "Don't know - the only usage I could find is #if 0 (?)") +CONFIG_DWORD_INFO(INTERNAL_MD_KeepKnownCA, W("MD_KeepKnownCA"), 0, "Something with known CAs (?)") +CONFIG_DWORD_INFO(INTERNAL_MD_MiniMDBreak, W("MD_MiniMDBreak"), 0, "ASSERT when creating CMiniMdRw class") +CONFIG_DWORD_INFO(INTERNAL_MD_PreSaveBreak, W("MD_PreSaveBreak"), 0, "ASSERT when calling CMiniMdRw::PreSave") +CONFIG_DWORD_INFO(INTERNAL_MD_RegMetaBreak, W("MD_RegMetaBreak"), 0, "ASSERT when creating RegMeta class") +CONFIG_DWORD_INFO(INTERNAL_MD_RegMetaDump, W("MD_RegMetaDump"), 0, "Dump MD in 4 functions (?)") +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_MODIFIABLE_ASSEMBLIES, W("MODIFIABLE_ASSEMBLIES"), "Enables hot reload on debug built assemblies with the 'debug' keyword", CLRConfig::LookupOptions::TrimWhiteSpaceFromStringValue); // Metadata - mscordbi only - this flag is only intended to mitigate potential issues in bug fix 458597. -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_MD_PreserveDebuggerMetadataMemory, W("MD_PreserveDebuggerMetadataMemory"), 0, "Save all versions of metadata memory in the debugger when debuggee metadata is updated", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_MD_PreserveDebuggerMetadataMemory, W("MD_PreserveDebuggerMetadataMemory"), 0, "Save all versions of metadata memory in the debugger when debuggee metadata is updated") /// /// Spinning heuristics /// // Note that these only take effect once the runtime has been started; prior to that the values hardcoded in g_SpinConstants (vars.cpp) are used -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinInitialDuration, W("SpinInitialDuration"), 0x32, "Hex value specifying the first spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinBackoffFactor, W("SpinBackoffFactor"), 0x3, "Hex value specifying the growth of each successive spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinLimitProcCap, W("SpinLimitProcCap"), 0xFFFFFFFF, "Hex value specifying the largest value of NumProcs to use when calculating the maximum spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinLimitProcFactor, W("SpinLimitProcFactor"), 0x4E20, "Hex value specifying the multiplier on NumProcs to use when calculating the maximum spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinLimitConstant, W("SpinLimitConstant"), 0x0, "Hex value specifying the constant to add when calculating the maximum spin duration", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_SpinRetryCount, W("SpinRetryCount"), 0xA, "Hex value specifying the number of times the entire spin process is repeated (when applicable)", EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_Monitor_SpinCount, W("Monitor_SpinCount"), 0x1e, "Hex value specifying the maximum number of spin iterations Monitor may perform upon contention on acquiring the lock before waiting.", EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinInitialDuration, W("SpinInitialDuration"), 0x32, "Hex value specifying the first spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinBackoffFactor, W("SpinBackoffFactor"), 0x3, "Hex value specifying the growth of each successive spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinLimitProcCap, W("SpinLimitProcCap"), 0xFFFFFFFF, "Hex value specifying the largest value of NumProcs to use when calculating the maximum spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinLimitProcFactor, W("SpinLimitProcFactor"), 0x4E20, "Hex value specifying the multiplier on NumProcs to use when calculating the maximum spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinLimitConstant, W("SpinLimitConstant"), 0x0, "Hex value specifying the constant to add when calculating the maximum spin duration") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_SpinRetryCount, W("SpinRetryCount"), 0xA, "Hex value specifying the number of times the entire spin process is repeated (when applicable)") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_Monitor_SpinCount, W("Monitor_SpinCount"), 0x1e, "Hex value specifying the maximum number of spin iterations Monitor may perform upon contention on acquiring the lock before waiting.") /// /// Native Binder @@ -475,27 +463,26 @@ CONFIG_DWORD_INFO(INTERNAL_NgenBind_ZapForbid, W("NgenBind_ZapForbid CONFIG_STRING_INFO(INTERNAL_NgenBind_ZapForbidExcludeList, W("NgenBind_ZapForbidExcludeList"), "") CONFIG_STRING_INFO(INTERNAL_NgenBind_ZapForbidList, W("NgenBind_ZapForbidList"), "") -CONFIG_DWORD_INFO_EX(INTERNAL_SymDiffDump, W("SymDiffDump"), 0, "Used to create the map file while binding the assembly. Used by SemanticDiffer", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_SymDiffDump, W("SymDiffDump"), 0, "Used to create the map file while binding the assembly. Used by SemanticDiffer") /// /// NGEN /// -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_NGen_JitName, W("NGen_JitName"), "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_NGenFramed, W("NGenFramed"), (DWORD)-1, "Same as JitFramed, but for ngen", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NGenOnlyOneMethod, W("NGenOnlyOneMethod"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenOrder, W("NgenOrder"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_partialNGenStress, W("partialNGenStress"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_ZapDoNothing, W("ZapDoNothing"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenForceFailureMask, W("NgenForceFailureMask"), (DWORD)-1, "Bitmask used to control which locations will check and raise the failure (defaults to bits: -1)", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenForceFailureCount, W("NgenForceFailureCount"), 0, "If set to >0 and we have IBC data we will force a failure after we reference an IBC data item times", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NgenForceFailureKind, W("NgenForceFailureKind"), 1, "If set to 1, We will throw a TypeLoad exception; If set to 2, We will cause an A/V", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_NGenFramed, W("NGenFramed"), (DWORD)-1, "Same as JitFramed, but for ngen") +CONFIG_DWORD_INFO(INTERNAL_NGenOnlyOneMethod, W("NGenOnlyOneMethod"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NgenOrder, W("NgenOrder"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_partialNGenStress, W("partialNGenStress"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_ZapDoNothing, W("ZapDoNothing"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NgenForceFailureMask, W("NgenForceFailureMask"), (DWORD)-1, "Bitmask used to control which locations will check and raise the failure (defaults to bits: -1)") +CONFIG_DWORD_INFO(INTERNAL_NgenForceFailureCount, W("NgenForceFailureCount"), 0, "If set to >0 and we have IBC data we will force a failure after we reference an IBC data item times") +CONFIG_DWORD_INFO(INTERNAL_NgenForceFailureKind, W("NgenForceFailureKind"), 1, "If set to 1, We will throw a TypeLoad exception; If set to 2, We will cause an A/V") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_NGenEnableCreatePdb, W("NGenEnableCreatePdb"), 0, "If set to >0 ngen.exe displays help on, recognizes createpdb in the command line") RETAIL_CONFIG_DWORD_INFO(INTERNAL_NGenSimulateDiskFull, W("NGenSimulateDiskFull"), 0, "If set to 1, ngen will throw a Disk full exception in ZapWriter.cpp:Save()") RETAIL_CONFIG_DWORD_INFO(INTERNAL_PartialNGen, W("PartialNGen"), (DWORD)-1, "Generate partial NGen images") CONFIG_DWORD_INFO(INTERNAL_NoASLRForNgen, W("NoASLRForNgen"), 0, "Turn off IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE bit in generated ngen images. Makes nidump output repeatable from run to run.") -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_NativeImageSearchPaths, W("NativeImageSearchPaths"), "Extra search paths for native composite R2R images", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_NativeImageSearchPaths, W("NativeImageSearchPaths"), "Extra search paths for native composite R2R images") #ifdef CROSSGEN_COMPILE RETAIL_CONFIG_DWORD_INFO(INTERNAL_CrossGenAssumeInputSigned, W("CrossGenAssumeInputSigned"), 1, "CrossGen should assume that its input assemblies will be signed before deployment") @@ -504,19 +491,19 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_CrossGenAssumeInputSigned, W("CrossGenAssumeIn /// /// Profiling API / ETW /// -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_COR_ENABLE_PROFILING, W("COR_ENABLE_PROFILING"), 0, "Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER, W("COR_PROFILER"), "Specifies GUID of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH, W("COR_PROFILER_PATH"), "Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_32, W("COR_PROFILER_PATH_32"), "Specifies the path to the DLL of profiler to load into currently running 32 bits process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_64, W("COR_PROFILER_PATH_64"), "Specifies the path to the DLL of profiler to load into currently running 64 bits process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_PROFILING, W("CORECLR_ENABLE_PROFILING"), 0, "CoreCLR only: Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER, W("CORECLR_PROFILER"), "CoreCLR only: Specifies GUID of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH, W("CORECLR_PROFILER_PATH"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_32, W("CORECLR_PROFILER_PATH_32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 32 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_64, W("CORECLR_PROFILER_PATH_64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 64 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM32, W("CORECLR_PROFILER_PATH_ARM32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM32 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM64, W("CORECLR_PROFILER_PATH_ARM64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM64 process", CLRConfig::DontPrependCOMPlus_) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, W("ProfAPI_ProfilerCompatibilitySetting"), "Specifies the profiler loading policy (the default is not to load a V2 profiler in V4)", CLRConfig::EEConfig_default | CLRConfig::TrimWhiteSpaceFromStringValue) +RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_COR_ENABLE_PROFILING, W("COR_ENABLE_PROFILING"), 0, "Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER, W("COR_PROFILER"), "Specifies GUID of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH, W("COR_PROFILER_PATH"), "Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_32, W("COR_PROFILER_PATH_32"), "Specifies the path to the DLL of profiler to load into currently running 32 bits process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_COR_PROFILER_PATH_64, W("COR_PROFILER_PATH_64"), "Specifies the path to the DLL of profiler to load into currently running 64 bits process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_CORECLR_ENABLE_PROFILING, W("CORECLR_ENABLE_PROFILING"), 0, "CoreCLR only: Flag to indicate whether profiling should be enabled for the currently running process.", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER, W("CORECLR_PROFILER"), "CoreCLR only: Specifies GUID of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH, W("CORECLR_PROFILER_PATH"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_32, W("CORECLR_PROFILER_PATH_32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 32 process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_64, W("CORECLR_PROFILER_PATH_64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running 64 process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM32, W("CORECLR_PROFILER_PATH_ARM32"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM32 process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_CORECLR_PROFILER_PATH_ARM64, W("CORECLR_PROFILER_PATH_ARM64"), "CoreCLR only: Specifies the path to the DLL of profiler to load into currently running ARM64 process", CLRConfig::LookupOptions::DontPrependPrefix) +RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ProfAPI_ProfilerCompatibilitySetting, W("ProfAPI_ProfilerCompatibilitySetting"), "Specifies the profiler loading policy (the default is not to load a V2 profiler in V4)", CLRConfig::LookupOptions::TrimWhiteSpaceFromStringValue) RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_DetachMinSleepMs, W("ProfAPI_DetachMinSleepMs"), 0, "The minimum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_DetachMaxSleepMs, W("ProfAPI_DetachMaxSleepMs"), 0, "The maximum time, in milliseconds, the CLR will wait before checking whether a profiler that is in the process of detaching is ready to be unloaded.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ProfAPI_RejitOnAttach, W("ProfApi_RejitOnAttach"), 1, "Enables the ability for profilers to rejit methods on attach.") @@ -528,13 +515,13 @@ CONFIG_DWORD_INFO(INTERNAL_TestOnlyEnableICorProfilerInfo, W("ProfAPI_TestOnlyEn CONFIG_DWORD_INFO(INTERNAL_TestOnlyEnableObjectAllocatedHook, W("TestOnlyEnableObjectAllocatedHook"), 0, "Test-only flag that forces CLR to initialize on startup as if ObjectAllocated callback were requested, to enable post-attach ObjectAllocated functionality.") CONFIG_DWORD_INFO(INTERNAL_TestOnlyEnableSlowELTHooks, W("TestOnlyEnableSlowELTHooks"), 0, "Test-only flag that forces CLR to initialize on startup as if slow-ELT were requested, to enable post-attach ELT functionality.") -RETAIL_CONFIG_STRING_INFO_EX(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W("ETW_ObjectAllocationEventsPerTypePerSec"), "Desired number of GCSampledObjectAllocation ETW events to be logged per type per second. If 0, then the default built in to the implementation for the enabled event (e.g., High, Low), will be used.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_ETW_ObjectAllocationEventsPerTypePerSec, W("ETW_ObjectAllocationEventsPerTypePerSec"), "Desired number of GCSampledObjectAllocation ETW events to be logged per type per second. If 0, then the default built in to the implementation for the enabled event (e.g., High, Low), will be used.") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ProfAPI_ValidateNGENInstrumentation, W("ProfAPI_ValidateNGENInstrumentation"), 0, "This flag enables additional validations when using the IMetaDataEmit APIs for NGEN'ed images to ensure only supported edits are made.") #ifdef FEATURE_PERFMAP -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default", CLRConfig::EEConfig_default) -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_PerfMapJitDumpPath, W("PerfMapJitDumpPath"), "Specifies a path to write the perf jitdump file. Defaults to GetTempPathA()", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapEnabled, W("PerfMapEnabled"), 0, "This flag is used on Linux to enable writing /tmp/perf-$pid.map. It is disabled by default") +RETAIL_CONFIG_STRING_INFO(EXTERNAL_PerfMapJitDumpPath, W("PerfMapJitDumpPath"), "Specifies a path to write the perf jitdump file. Defaults to GetTempPathA()") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapIgnoreSignal, W("PerfMapIgnoreSignal"), 0, "When perf map is enabled, this option will configure the specified signal to be accepted and ignored as a marker in the perf logs. It is disabled by default") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_PerfMapShowOptimizationTiers, W("PerfMapShowOptimizationTiers"), 1, "Shows optimization tiers in the perf map for methods, as part of the symbol name. Useful for seeing separate stack frames for different optimization tiers of each method.") RETAIL_CONFIG_STRING_INFO(EXTERNAL_NativeImagePerfMapFormat, W("NativeImagePerfMapFormat"), "Specifies the format of native image perfmap files generated by crossgen. Valid options are RVA or OFFSET.") #endif @@ -544,13 +531,12 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_StartupDelayMS, W("StartupDelayMS"), "") /// /// Stress /// -CONFIG_DWORD_INFO_EX(INTERNAL_StressCOMCall, W("StressCOMCall"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_StressLog, W("StressLog"), "Turns on the stress log.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_ForceEnc, W("ForceEnc"), "Forces Edit and Continue to be on for all eligible modules.") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_StressLogSize, W("StressLogSize"), "Stress log size in bytes per thread.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(UNSUPPORTED_StressLogFilename, W("StressLogFilename"), "Stress log filename for memory mapped stress log.") -CONFIG_DWORD_INFO_EX(INTERNAL_stressSynchronized, W("stressSynchronized"), 0, "Unknown if or where this is used; unless a test is specifically depending on this, it can be removed.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_StressThreadCount, W("StressThreadCount"), "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StressLog, W("StressLog"), 0, "Turns on the stress log.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_ForceEnc, W("ForceEnc"), 0, "Forces Edit and Continue to be on for all eligible modules.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_StressLogSize, W("StressLogSize"), 0, "Stress log size in bytes per thread.") +RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_StressLogFilename, W("StressLogFilename"), "Stress log filename for memory mapped stress log.") +CONFIG_DWORD_INFO(INTERNAL_stressSynchronized, W("stressSynchronized"), 0, "Unknown if or where this is used; unless a test is specifically depending on this, it can be removed.") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TotalStressLogSize, W("TotalStressLogSize"), 0, "Total stress log size in bytes.") /// /// Thread Suspend @@ -635,7 +621,7 @@ CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of e /// Profile Guided Opts /// #ifdef FEATURE_PGO -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data") RETAIL_CONFIG_DWORD_INFO(INTERNAL_TieredPGO, W("TieredPGO"), 0, "Instrument Tier0 code and make counts available to Tier1") @@ -656,14 +642,14 @@ CONFIG_DWORD_INFO(INTERNAL_TypeLoader_InjectInterfaceDuplicates, W("INTERNAL_Typ /// /// Virtual call stubs /// -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubCollideMonoPct, W("VirtualCallStubCollideMonoPct"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubCollideWritePct, W("VirtualCallStubCollideWritePct"), 100, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubDumpLogCounter, W("VirtualCallStubDumpLogCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubDumpLogIncr, W("VirtualCallStubDumpLogIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_VirtualCallStubLogging, W("VirtualCallStubLogging"), 0, "Worth keeping, but should be moved into \"#ifdef STUB_LOGGING\" blocks. This goes for most (or all) of the stub logging infrastructure.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubMissCount, W("VirtualCallStubMissCount"), 100, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubResetCacheCounter, W("VirtualCallStubResetCacheCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_VirtualCallStubResetCacheIncr, W("VirtualCallStubResetCacheIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubCollideMonoPct, W("VirtualCallStubCollideMonoPct"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubCollideWritePct, W("VirtualCallStubCollideWritePct"), 100, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubDumpLogCounter, W("VirtualCallStubDumpLogCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubDumpLogIncr, W("VirtualCallStubDumpLogIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_VirtualCallStubLogging, W("VirtualCallStubLogging"), 0, "Worth keeping, but should be moved into \"#ifdef STUB_LOGGING\" blocks. This goes for most (or all) of the stub logging infrastructure.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubMissCount, W("VirtualCallStubMissCount"), 100, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubResetCacheCounter, W("VirtualCallStubResetCacheCounter"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") +CONFIG_DWORD_INFO(INTERNAL_VirtualCallStubResetCacheIncr, W("VirtualCallStubResetCacheIncr"), 0, "Used only when STUB_LOGGING is defined, which by default is not.") /// /// Watson @@ -681,15 +667,15 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_CreateDumpDiagnostics, W("CreateDumpDiagnostic /// /// Zap /// -RETAIL_CONFIG_STRING_INFO_EX(INTERNAL_ZapBBInstr, W("ZapBBInstr"), "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(INTERNAL_ZapBBInstr, W("ZapBBInstr"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapBBInstrDir, W("ZapBBInstrDir"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ZapDisable, W("ZapDisable"), 0, "") -CONFIG_STRING_INFO_EX(INTERNAL_ZapExclude, W("ZapExclude"), "", CLRConfig::EEConfig_default) -CONFIG_STRING_INFO_EX(INTERNAL_ZapOnly, W("ZapOnly"), "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_ZapRequire, W("ZapRequire"), "") +CONFIG_STRING_INFO(INTERNAL_ZapExclude, W("ZapExclude"), "") +CONFIG_STRING_INFO(INTERNAL_ZapOnly, W("ZapOnly"), "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ZapRequire, W("ZapRequire"), 0, "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapRequireExcludeList, W("ZapRequireExcludeList"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapRequireList, W("ZapRequireList"), "") -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_ZapSet, W("ZapSet"), "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_STRING_INFO(EXTERNAL_ZapSet, W("ZapSet"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ReadyToRun, W("ReadyToRun"), 1, "Enable/disable use of ReadyToRun native code") // On by default for CoreCLR RETAIL_CONFIG_STRING_INFO(EXTERNAL_ReadyToRunExcludeList, W("ReadyToRunExcludeList"), "List of assemblies that cannot use Ready to Run images") @@ -704,12 +690,11 @@ RETAIL_CONFIG_STRING_INFO(INTERNAL_EventNameFilter, W("EventNameFilter"), "") /// /// Interop /// -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_ExposeExceptionsInCOM, W("ExposeExceptionsInCOM"), "") +CONFIG_DWORD_INFO(INTERNAL_ExposeExceptionsInCOM, W("ExposeExceptionsInCOM"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_InteropValidatePinnedObjects, W("InteropValidatePinnedObjects"), 0, "After returning from a managed-to-unmanaged interop call, validate GC heap around objects pinned by IL stubs.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_InteropLogArguments, W("InteropLogArguments"), 0, "Log all pinned arguments passed to an interop call") RETAIL_CONFIG_STRING_INFO(UNSUPPORTED_LogCCWRefCountChange, W("LogCCWRefCountChange"), "Outputs debug information and calls LogCCWRefCountChange_BREAKPOINT when AddRef or Release is called on a CCW.") RETAIL_CONFIG_DWORD_INFO(INTERNAL_EnableRCWCleanupOnSTAShutdown, W("EnableRCWCleanupOnSTAShutdown"), 0, "Performs RCW cleanup when STA shutdown is detected using IInitializeSpy in classic processes.") -RETAIL_CONFIG_DWORD_INFO(EXTERNAL_AllowDComReflection, W("AllowDComReflection"), 0, "Allows out of process DCOM clients to marshal blocked reflection types.") // // EventPipe @@ -733,14 +718,14 @@ RETAIL_CONFIG_STRING_INFO(INTERNAL_GCGenAnalysisCmd, W("GCGenAnalysisCmd"), "An // // Diagnostics Ports // -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DOTNET_DefaultDiagnosticPortSuspend, W("DOTNET_DefaultDiagnosticPortSuspend"), 0, "This sets the deafult diagnostic port to suspend causing the runtime to pause during startup before major subsystems are started. Resume using the Diagnostics IPC ResumeStartup command on the default diagnostic port.", CLRConfig::DontPrependCOMPlus_); -RETAIL_CONFIG_STRING_INFO_EX(EXTERNAL_DOTNET_DiagnosticPorts, W("DOTNET_DiagnosticPorts"), "A semicolon delimited list of additional Diagnostic Ports, where a Diagnostic Port is a NamedPipe path without '\\\\.\\pipe\\' on Windows or the full path of Unix Domain Socket on Linux/Unix followed by optional tags, e.g., ',connect,nosuspend;'", CLRConfig::DontPrependCOMPlus_); +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DOTNET_DefaultDiagnosticPortSuspend, W("DefaultDiagnosticPortSuspend"), 0, "This sets the deafult diagnostic port to suspend causing the runtime to pause during startup before major subsystems are started. Resume using the Diagnostics IPC ResumeStartup command on the default diagnostic port."); +RETAIL_CONFIG_STRING_INFO(EXTERNAL_DOTNET_DiagnosticPorts, W("DiagnosticPorts"), "A semicolon delimited list of additional Diagnostic Ports, where a Diagnostic Port is a NamedPipe path without '\\\\.\\pipe\\' on Windows or the full path of Unix Domain Socket on Linux/Unix followed by optional tags, e.g., ',connect,nosuspend;'"); // // LTTng // RETAIL_CONFIG_STRING_INFO(INTERNAL_LTTngConfig, W("LTTngConfig"), "Configuration for LTTng.") -RETAIL_CONFIG_DWORD_INFO(UNSUPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_LTTng, W("LTTng"), 1, "If COMPlus_LTTng is set to 0, this will prevent the LTTng library from being loaded at runtime") #ifdef FEATURE_GDBJIT @@ -765,37 +750,33 @@ RETAIL_CONFIG_DWORD_INFO(INTERNAL_GDBJitEmitDebugFrame, W("GDBJitEmitDebugFrame" // // DO NOT ADD ANY MORE CONFIG SWITCHES TO THIS SECTION! // ** -CONFIG_DWORD_INFO_EX(INTERNAL_ActivatePatchSkip, W("ActivatePatchSkip"), 0, "Allows an assert when ActivatePatchSkip is called", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_AlwaysUseMetadataInterfaceMapLayout, W("AlwaysUseMetadataInterfaceMapLayout"), "Used for debugging generic interface map layout.") +CONFIG_DWORD_INFO(INTERNAL_ActivatePatchSkip, W("ActivatePatchSkip"), 0, "Allows an assert when ActivatePatchSkip is called") +CONFIG_DWORD_INFO(INTERNAL_AlwaysUseMetadataInterfaceMapLayout, W("AlwaysUseMetadataInterfaceMapLayout"), 0, "Used for debugging generic interface map layout.") CONFIG_DWORD_INFO(INTERNAL_AssertOnUnneededThis, W("AssertOnUnneededThis"), 0, "While the ConfigDWORD is unnecessary, the contained ASSERT should be kept. This may result in some work tracking down violating MethodDescCallSites.") -CONFIG_DWORD_INFO_EX(INTERNAL_AssertStacktrace, W("AssertStacktrace"), 1, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_clearNativeImageStress, W("clearNativeImageStress"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_CPUFamily, W("CPUFamily"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_CPUFeatures, W("CPUFeatures"), "") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_DisableConfigCache, W("DisableConfigCache"), 0, "Used to disable the \"probabilistic\" config cache, which walks through the appropriate config registry keys on init and probabilistically keeps track of which exist.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_DisableStackwalkCache, W("DisableStackwalkCache"), "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_DoubleArrayToLargeObjectHeap, W("DoubleArrayToLargeObjectHeap"), "Controls double[] placement") +CONFIG_DWORD_INFO(INTERNAL_AssertStacktrace, W("AssertStacktrace"), 1, "") +CONFIG_DWORD_INFO(INTERNAL_clearNativeImageStress, W("clearNativeImageStress"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_CPUFamily, W("CPUFamily"), 0xFFFFFFFF, "") +CONFIG_DWORD_INFO(INTERNAL_CPUFeatures, W("CPUFeatures"), 0xFFFFFFFF, "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DisableConfigCache, W("DisableConfigCache"), 0, "Used to disable the \"probabilistic\" config cache, which walks through the appropriate config registry keys on init and probabilistically keeps track of which exist.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DisableStackwalkCache, W("DisableStackwalkCache"), 0, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_DoubleArrayToLargeObjectHeap, W("DoubleArrayToLargeObjectHeap"), 0, "Controls double[] placement") CONFIG_STRING_INFO(INTERNAL_DumpOnClassLoad, W("DumpOnClassLoad"), "Dumps information about loaded class to log.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_ExpandAllOnLoad, W("ExpandAllOnLoad"), "") -CONFIG_STRING_INFO_DIRECT_ACCESS(INTERNAL_ForcedRuntime, W("ForcedRuntime"), "Verify version of CLR loaded") -CONFIG_DWORD_INFO_EX(INTERNAL_ForceRelocs, W("ForceRelocs"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_GenerateLongJumpDispatchStubRatio, W("GenerateLongJumpDispatchStubRatio"), "Useful for testing VSD on AMD64") -CONFIG_DWORD_INFO_EX(INTERNAL_HashStack, W("HashStack"), 0, "", CLRConfig::EEConfig_default) +CONFIG_DWORD_INFO(INTERNAL_ExpandAllOnLoad, W("ExpandAllOnLoad"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_ForceRelocs, W("ForceRelocs"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_GenerateLongJumpDispatchStubRatio, W("GenerateLongJumpDispatchStubRatio"), 0, "Useful for testing VSD on AMD64") +CONFIG_DWORD_INFO(INTERNAL_HashStack, W("HashStack"), 0, "") CONFIG_DWORD_INFO(INTERNAL_HostManagerConfig, W("HostManagerConfig"), (DWORD)-1, "") CONFIG_DWORD_INFO(INTERNAL_HostTestThreadAbort, W("HostTestThreadAbort"), 0, "") CONFIG_STRING_INFO(INTERNAL_InvokeHalt, W("InvokeHalt"), "Throws an assert when the given method is invoked through reflection.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_MaxStackDepth, W("MaxStackDepth"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_MaxStubUnwindInfoSegmentSize, W("MaxStubUnwindInfoSegmentSize"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_MaxThreadRecord, W("MaxThreadRecord"), "") +CONFIG_DWORD_INFO(INTERNAL_MaxStubUnwindInfoSegmentSize, W("MaxStubUnwindInfoSegmentSize"), 0, "") CONFIG_DWORD_INFO(INTERNAL_MessageDebugOut, W("MessageDebugOut"), 0, "") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_NativeImageRequire, W("NativeImageRequire"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NestedEhOom, W("NestedEhOom"), 0, "", CLRConfig::EEConfig_default) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_NativeImageRequire, W("NativeImageRequire"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NestedEhOom, W("NestedEhOom"), 0, "") #define INTERNAL_NoGuiOnAssert_Default 1 -RETAIL_CONFIG_DWORD_INFO_EX(INTERNAL_NoGuiOnAssert, W("NoGuiOnAssert"), INTERNAL_NoGuiOnAssert_Default, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_NoProcedureSplitting, W("NoProcedureSplitting"), 0, "", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_EX(INTERNAL_NoStringInterning, W("NoStringInterning"), 1, "Disallows string interning. I see no value in it anymore.", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_NotifyBadAppCfg, W("NotifyBadAppCfg"), "Whether to show a message box for bad application config file.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_PauseOnLoad, W("PauseOnLoad"), "Stops in SystemDomain::init. I think it can be removed.") +RETAIL_CONFIG_DWORD_INFO(INTERNAL_NoGuiOnAssert, W("NoGuiOnAssert"), INTERNAL_NoGuiOnAssert_Default, "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_NoProcedureSplitting, W("NoProcedureSplitting"), 0, "") +CONFIG_DWORD_INFO(INTERNAL_NoStringInterning, W("NoStringInterning"), 1, "Disallows string interning. I see no value in it anymore.") +CONFIG_DWORD_INFO(INTERNAL_PauseOnLoad, W("PauseOnLoad"), 0, "Stops in SystemDomain::init. I think it can be removed.") CONFIG_DWORD_INFO(INTERNAL_PerfAllocsSizeThreshold, W("PerfAllocsSizeThreshold"), 0x3FFFFFFF, "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") CONFIG_DWORD_INFO(INTERNAL_PerfNumAllocsThreshold, W("PerfNumAllocsThreshold"), 0x3FFFFFFF, "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") CONFIG_STRING_INFO(INTERNAL_PerfTypesToLog, W("PerfTypesToLog"), "Log facility LF_GCALLOC logs object allocations. This flag controls which ones also log stacktraces. Predates ClrProfiler.") @@ -803,33 +784,24 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_Prepopulate1, W("Prepopulate1"), 1, "") CONFIG_STRING_INFO(INTERNAL_PrestubGC, W("PrestubGC"), "") CONFIG_STRING_INFO(INTERNAL_PrestubHalt, W("PrestubHalt"), "") RETAIL_CONFIG_STRING_INFO(EXTERNAL_RestrictedGCStressExe, W("RestrictedGCStressExe"), "") -CONFIG_DWORD_INFO_EX(INTERNAL_ReturnSourceTypeForTesting, W("ReturnSourceTypeForTesting"), 0, "Allows returning the (internal only) source type of an IL to Native mapping for debugging purposes", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_RSStressLog, W("RSStressLog"), 0, "Allows turning on logging for RS startup", CLRConfig::EEConfig_default) -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_SaveThreadInfo, W("SaveThreadInfo"), "") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_SaveThreadInfoMask, W("SaveThreadInfoMask"), "") +CONFIG_DWORD_INFO(INTERNAL_ReturnSourceTypeForTesting, W("ReturnSourceTypeForTesting"), 0, "Allows returning the (internal only) source type of an IL to Native mapping for debugging purposes") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_RSStressLog, W("RSStressLog"), 0, "Allows turning on logging for RS startup") CONFIG_DWORD_INFO(INTERNAL_SBDumpOnNewIndex, W("SBDumpOnNewIndex"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") CONFIG_DWORD_INFO(INTERNAL_SBDumpOnResize, W("SBDumpOnResize"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") CONFIG_DWORD_INFO(INTERNAL_SBDumpStyle, W("SBDumpStyle"), 0, "Used for Syncblock debugging. It's been a while since any of those have been used.") -RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(UNSUPPORTED_ShimDatabaseVersion, W("ShimDatabaseVersion"), "Force using shim database version in registry") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_SleepOnExit, W("SleepOnExit"), 0, "Used for lrak detection. I'd say deprecated by umdh.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_StubLinkerUnwindInfoVerificationOn, W("StubLinkerUnwindInfoVerificationOn"), "") -RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_SuccessExit, W("SuccessExit"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_SymbolReadingPolicy, W("SymbolReadingPolicy"), "Specifies when PDBs may be read") +CONFIG_DWORD_INFO(INTERNAL_StubLinkerUnwindInfoVerificationOn, W("StubLinkerUnwindInfoVerificationOn"), 0, "") +RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_SuccessExit, W("SuccessExit"), 0, "") RETAIL_CONFIG_DWORD_INFO(UNSUPPORTED_TestDataConsistency, W("TestDataConsistency"), FALSE, "Allows ensuring the left side is not holding locks (and may thus be in an inconsistent state) when inspection occurs") -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_ThreadGuardPages, W("ThreadGuardPages"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_EX(EXTERNAL_Timeline, W("Timeline"), 0, "", CLRConfig::EEConfig_default) -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(UNSUPPORTED_TotalStressLogSize, W("TotalStressLogSize"), "Total stress log size in bytes.") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_ThreadGuardPages, W("ThreadGuardPages"), 0, "") #ifdef _DEBUG -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_TraceIUnknown, W("TraceIUnknown"), "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_TraceWrap, W("TraceWrap"), "") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TraceWrap, W("TraceWrap"), 0, "") #endif -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_TURNOFFDEBUGINFO, W("TURNOFFDEBUGINFO"), "") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseMethodDataCache, W("UseMethodDataCache"), FALSE, "Used during feature development; may now be removed.") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_UseParentMethodData, W("UseParentMethodData"), TRUE, "Used during feature development; may now be removed.") -CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_VerifierOff, W("VerifierOff"), "") -RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_VerifyAllOnLoad, W("VerifyAllOnLoad"), "") +CONFIG_DWORD_INFO(INTERNAL_VerifierOff, W("VerifierOff"), 0, "") // ** // PLEASE MOVE ANY CONFIG SWITCH YOU OWN OUT OF THIS SECTION INTO A CATEGORY ABOVE // diff --git a/src/coreclr/inc/clrprivbinding.idl b/src/coreclr/inc/clrprivbinding.idl index 08743d215cc3a..9f4ee2f3d6ff8 100644 --- a/src/coreclr/inc/clrprivbinding.idl +++ b/src/coreclr/inc/clrprivbinding.idl @@ -3,14 +3,11 @@ import "unknwn.idl"; import "objidl.idl"; -import "fusion.idl"; // Forward declarations interface ICLRPrivBinder; interface ICLRPrivAssembly; -typedef LPCSTR LPCUTF8; - /************************************************************************************** ** This IDL file defines the assembly binding host interfaces. Some things to keep ** in mind: @@ -35,11 +32,11 @@ interface ICLRPrivBinder : IUnknown ** BindAssemblyByName -- Binds an assembly by name. ** NOTE: This method is required to be idempotent. See general comment above. ** - ** pAssemblyName - name of the assembly for which a bind is being requested. + ** pAssemblyFullName - name of the assembly for which a bind is being requested. ** ppAssembly - upon success, receives the bound assembly. **********************************************************************************/ HRESULT BindAssemblyByName( - [in] IAssemblyName * pAssemblyName, + [in] struct AssemblyNameData* pAssemblyNameData, [out, retval] ICLRPrivAssembly ** ppAssembly); /********************************************************************************** diff --git a/src/coreclr/inc/contract.h b/src/coreclr/inc/contract.h index 68fea025a2775..a50c09ed32255 100644 --- a/src/coreclr/inc/contract.h +++ b/src/coreclr/inc/contract.h @@ -54,22 +54,6 @@ // then it's ok for this function to as well. If LIMITED_METHOD_CONTRACT is specified, // however, then CANNOT_TAKE_LOCK is assumed. // -// EE_THREAD_NOT_REQUIRED the function does not assume an EE Thread object is available in TLS. -// Either GetThread() is never called, or any code path that requires a Thread -// has another code path that deals with the absence of a Thread. Any call to -// to GetThread() must be bracketed with BEGIN_GETTHREAD_ALLOWED / -// END_GETTHREAD_ALLOWED to avoid bogus asserts (the short-form -// GetThreadNULLOk() may be used as well). However, this is only allowed if visual -// inspection of the call site makes it patently obvious that the function deals -// appropriately with the GetThread() == NULL case. -// -or- EE_THREAD_REQUIRED the function requires an EE Thread object in TLS (i.e., GetThread() != NULL) -// If this contract is used, we will ASSERT on entry to the function that -// GetThread() != NULL. -// -or- the default is DISABLED(EE_THREAD_REQUIRED). i.e., we do not assert -// GetThread() != NULL on entry to the function and do not assert on any -// unprotected uses of GetThread(). -// See code:GetThreadGenericFullCheck for info on how these -// contracts are enforced. // // SUPPORTS_DAC The function has been written to be callable from out-of-process using DAC. // In builds where DACCESS_COMPILE is defined, such functions can only call @@ -398,9 +382,8 @@ struct DbgStateLockState #define CONTRACT_BITMASK_SOTOLERANT 0x1 << 3 #define CONTRACT_BITMASK_DEBUGONLY 0x1 << 4 #define CONTRACT_BITMASK_SONOTMAINLINE 0x1 << 5 -#define CONTRACT_BITMASK_ALLOWGETTHREAD 0x1 << 6 -#define CONTRACT_BITMASK_OK_TO_LOCK 0x1 << 7 -#define CONTRACT_BITMASK_OK_TO_RETAKE_LOCK 0x1 << 8 +#define CONTRACT_BITMASK_OK_TO_LOCK 0x1 << 6 +#define CONTRACT_BITMASK_OK_TO_RETAKE_LOCK 0x1 << 7 #define CONTRACT_BITMASK_IS_SET(whichbit) ((m_flags & (whichbit)) != 0) @@ -441,7 +424,6 @@ struct ClrDebugState m_flags = CONTRACT_BITMASK_OK_TO_THROW| CONTRACT_BITMASK_HOSTCALLS| CONTRACT_BITMASK_SOTOLERANT| - CONTRACT_BITMASK_ALLOWGETTHREAD| CONTRACT_BITMASK_OK_TO_LOCK| CONTRACT_BITMASK_OK_TO_RETAKE_LOCK; @@ -581,30 +563,6 @@ struct ClrDebugState CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_DEBUGONLY); } - //--// - BOOL IsGetThreadAllowed() - { - return CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_ALLOWGETTHREAD); - - } - void SetGetThreadAllowed() - { - CONTRACT_BITMASK_SET(CONTRACT_BITMASK_ALLOWGETTHREAD); - } - - - BOOL SetGetThreadAllowed(BOOL value) - { - BOOL prevState = CONTRACT_BITMASK_IS_SET(CONTRACT_BITMASK_ALLOWGETTHREAD); - CONTRACT_BITMASK_UPDATE(CONTRACT_BITMASK_ALLOWGETTHREAD,value); - return prevState; - } - - void ResetGetThreadAllowed() - { - CONTRACT_BITMASK_RESET(CONTRACT_BITMASK_ALLOWGETTHREAD); - } - //--// BOOL IsOkToLock() { @@ -944,17 +902,6 @@ class BaseContract HOST_NoCalls = 0x00001000, HOST_Disabled = 0x00000000, // the default - // This enforces the EE_THREAD_NOT_REQUIRED contract by clearing - // ClrDebugState::m_allowGetThread in its scope. That causes raw calls - // to GetThread() to assert, unless inside a temporary "don't worry it's ok" scope - // via BEGIN/END_GETTHREAD_ALLOWED. Useful for enforcing our docs that - // state certain unmanaged API entrypoints (e.g., some from profiling API) - // are callable without an EE Thread in TLS. - EE_THREAD_Mask = 0x0000C000, - EE_THREAD_Disabled = 0x00000000, // the default - EE_THREAD_Required = 0x00004000, - EE_THREAD_Not_Required = 0x00008000, - // These enforce the CAN_TAKE_LOCK / CANNOT_TAKE_LOCK contracts CAN_TAKE_LOCK_Mask = 0x00060000, CAN_TAKE_LOCK_Yes = 0x00020000, @@ -973,7 +920,7 @@ class BaseContract LOADS_TYPE_Disabled = 0x00000000, // the default ALL_Disabled = THROWS_Disabled|GC_Disabled|FAULT_Disabled|MODE_Disabled|LOADS_TYPE_Disabled| - HOST_Disabled|EE_THREAD_Disabled|CAN_TAKE_LOCK_Disabled|CAN_RETAKE_LOCK_No_Disabled + HOST_Disabled|CAN_TAKE_LOCK_Disabled|CAN_RETAKE_LOCK_No_Disabled }; @@ -1178,7 +1125,6 @@ enum ContractViolationBits LoadsTypeViolation = 0x00000040, // suppress LOADS_TYPE tags in this scope TakesLockViolation = 0x00000080, // suppress CAN_TAKE_LOCK tags in this scope HostViolation = 0x00000100, // suppress HOST_CALLS tags in this scope - EEThreadViolation = 0x00000200, // suppress EE_THREAD_REQUIRED tags in this scope //These are not violation bits. We steal some bits out of the violation mask to serve as // general flag bits. @@ -2106,68 +2052,6 @@ class HostNoCallHolder } #endif -#ifdef ENABLE_CONTRACTS_IMPL - -class GetThreadAllowedHolder -{ - public: - GetThreadAllowedHolder(BOOL newState) - { - m_clrDebugState = ::GetClrDebugState(); - m_previousState = m_clrDebugState->SetGetThreadAllowed(newState); - } - - ~GetThreadAllowedHolder() - { - m_clrDebugState->SetGetThreadAllowed(m_previousState); - } - -private: - BOOL m_previousState; - ClrDebugState* m_clrDebugState; -}; - -// When in an EE_THREAD_NOT_REQUIRED contracted scope, it's expected that the -// function does not assume an EE Thread object is available in TLS. Either -// GetThread() is never called, or any code path that requires a Thread -// has another code path that deals with the absence of a Thread. Any call to -// to GetThread() must be bracketed with BEGIN_GETTHREAD_ALLOWED / -// END_GETTHREAD_ALLOWED to avoid bogus asserts (the short-form -// GetThreadNULLOk() may be used as well). However, this is only allowed if visual -// inspection of the call site makes it patently obvious that the function deals -// appropriately with the GetThread() == NULL case (or that case has already been -// dealt with and control has exited before the BEGIN_GETTHREAD_ALLOWED / -// END_GETTHREAD_ALLOWED block. -// -// These use holder objects, which causes the compiler to generate EH code and prevent -// inlining. So try to avoid these in small, downstream functions (like inline -// EE Thread member functions). Use the _IN_NO_THROW_REGION variants below instead. - -#define BEGIN_GETTHREAD_ALLOWED \ - { \ - GetThreadAllowedHolder __getThreadAllowedHolder(TRUE); \ - -#define END_GETTHREAD_ALLOWED \ - } - -// These are lighter-weight versions of BEGIN_GETTHREAD_ALLOWED / -// END_GETTHREAD_ALLOWED. These don't use holders, so be sure only to -// use these to bracket code that won't throw exceptions -#define BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION \ - { \ - ClrDebugState * __clrDebugState = ::GetClrDebugState(); \ - BOOL __previousState = __clrDebugState->SetGetThreadAllowed(TRUE); \ - -#define END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION \ - __clrDebugState->SetGetThreadAllowed(__previousState); \ - } - -#else // ENABLE_CONTRACTS_IMPL -#define BEGIN_GETTHREAD_ALLOWED -#define END_GETTHREAD_ALLOWED -#define BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION -#define END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION -#endif #if defined(ENABLE_CONTRACTS_IMPL) diff --git a/src/coreclr/inc/cor.h b/src/coreclr/inc/cor.h index 413000b920eed..a77a9f1077742 100644 --- a/src/coreclr/inc/cor.h +++ b/src/coreclr/inc/cor.h @@ -122,8 +122,6 @@ interface IMetaDataImport; interface IMetaDataAssemblyEmit; interface IMetaDataAssemblyImport; interface IMetaDataEmit; -interface ICeeGen; - typedef UNALIGNED void const *UVCP_CONSTANT; @@ -1458,202 +1456,6 @@ DECLARE_INTERFACE_(IMetaDataDispenserEx, IMetaDataDispenser) }; -//***************************************************************************** -//***************************************************************************** -// -// Registration declarations. Will be replace by Services' Registration -// implementation. -// -//***************************************************************************** -//***************************************************************************** -// Various flags for use in installing a module or a composite -typedef enum -{ - regNoCopy = 0x00000001, // Don't copy files into destination - regConfig = 0x00000002, // Is a configuration - regHasRefs = 0x00000004 // Has class references -} CorRegFlags; - -typedef GUID CVID; - -typedef struct { - short Major; - short Minor; - short Sub; - short Build; -} CVStruct; - - -//***************************************************************************** -//***************************************************************************** -// -// CeeGen interfaces for generating in-memory Common Language Runtime files -// -//***************************************************************************** -//***************************************************************************** - -typedef void *HCEESECTION; - -typedef enum { - sdNone = 0, - sdReadOnly = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA, - sdReadWrite = sdReadOnly | IMAGE_SCN_MEM_WRITE, - sdExecute = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE -} CeeSectionAttr; - -// -// Relocation types. -// - -typedef enum { - // generate only a section-relative reloc, nothing into .reloc section - srRelocAbsolute, - - // generate a .reloc for a pointer sized location, - // This is transformed into BASED_HIGHLOW or BASED_DIR64 based on the platform - srRelocHighLow = 3, - - // generate a .reloc for the top 16-bits of a 32 bit number, where the - // bottom 16 bits are included in the next word in the .reloc table - srRelocHighAdj, // Never Used - - // generate a token map relocation, nothing into .reloc section - srRelocMapToken, - - // relative address fixup - srRelocRelative, - - // Generate only a section-relative reloc, nothing into .reloc - // section. This reloc is relative to the file position of the - // section, not the section's virtual address. - srRelocFilePos, - - // code relative address fixup - srRelocCodeRelative, - - // generate a .reloc for a 64 bit address in an ia64 movl instruction - srRelocIA64Imm64, - - // generate a .reloc for a 64 bit address - srRelocDir64, - - // generate a .reloc for a 25-bit PC relative address in an ia64 br.call instruction - srRelocIA64PcRel25, - - // generate a .reloc for a 64-bit PC relative address in an ia64 brl.call instruction - srRelocIA64PcRel64, - - // generate a 30-bit section-relative reloc, used for tagged pointer values - srRelocAbsoluteTagged, - - - // A sentinel value to help ensure any additions to this enum are reflected - // in PEWriter.cpp's RelocName array. - srRelocSentinel, - - // Flags that can be used with the above reloc types - - // do not emit base reloc - srNoBaseReloc = 0x4000, - - // pre-fixup contents of memory are ptr rather than a section offset - srRelocPtr = 0x8000, - - // legal enums which include the Ptr flag - srRelocAbsolutePtr = srRelocPtr + srRelocAbsolute, - srRelocHighLowPtr = srRelocPtr + srRelocHighLow, - srRelocRelativePtr = srRelocPtr + srRelocRelative, - srRelocIA64Imm64Ptr = srRelocPtr + srRelocIA64Imm64, - srRelocDir64Ptr = srRelocPtr + srRelocDir64, - -} CeeSectionRelocType; - -typedef union { - USHORT highAdj; -} CeeSectionRelocExtra; - -//------------------------------------- -//--- ICeeGen -//------------------------------------- -// {7ED1BDFF-8E36-11d2-9C56-00A0C9B7CC45} -EXTERN_GUID(IID_ICeeGen, 0x7ed1bdff, 0x8e36, 0x11d2, 0x9c, 0x56, 0x0, 0xa0, 0xc9, 0xb7, 0xcc, 0x45); - -DECLARE_INTERFACE_(ICeeGen, IUnknown) -{ - STDMETHOD (EmitString) ( - _In_ - LPWSTR lpString, // [IN] String to emit - ULONG *RVA) PURE; // [OUT] RVA for string emitted string - - STDMETHOD (GetString) ( - ULONG RVA, // [IN] RVA for string to return - _Out_opt_ - LPWSTR *lpString) PURE; // [OUT] Returned string - - STDMETHOD (AllocateMethodBuffer) ( - ULONG cchBuffer, // [IN] Length of buffer to create - UCHAR **lpBuffer, // [OUT] Returned buffer - ULONG *RVA) PURE; // [OUT] RVA for method - - STDMETHOD (GetMethodBuffer) ( - ULONG RVA, // [IN] RVA for method to return - UCHAR **lpBuffer) PURE; // [OUT] Returned buffer - - STDMETHOD (GetIMapTokenIface) ( - IUnknown **pIMapToken) PURE; - - STDMETHOD (GenerateCeeFile) () PURE; - - STDMETHOD (GetIlSection) ( - HCEESECTION *section) PURE; - - STDMETHOD (GetStringSection) ( - HCEESECTION *section) PURE; - - STDMETHOD (AddSectionReloc) ( - HCEESECTION section, - ULONG offset, - HCEESECTION relativeTo, - CeeSectionRelocType relocType) PURE; - - // use these only if you have special section requirements not handled - // by other APIs - STDMETHOD (GetSectionCreate) ( - const char *name, - DWORD flags, - HCEESECTION *section) PURE; - - STDMETHOD (GetSectionDataLen) ( - HCEESECTION section, - ULONG *dataLen) PURE; - - STDMETHOD (GetSectionBlock) ( - HCEESECTION section, - ULONG len, - ULONG align=1, - void **ppBytes=0) PURE; - - STDMETHOD (TruncateSection) ( - HCEESECTION section, - ULONG len) PURE; - - STDMETHOD (GenerateCeeMemoryImage) ( - void **ppImage) PURE; - - STDMETHOD (ComputePointer) ( - HCEESECTION section, - ULONG RVA, // [IN] RVA for method to return - UCHAR **lpBuffer) PURE; // [OUT] Returned buffer - -}; - -//***************************************************************************** -//***************************************************************************** -// -// End of CeeGen declarations. -// -//***************************************************************************** - //********************************************************************** //********************************************************************** //--- IMetaDataTables diff --git a/src/coreclr/inc/corerror.xml b/src/coreclr/inc/corerror.xml index b1a0b5b53ca09..40f49f8ba1baa 100644 --- a/src/coreclr/inc/corerror.xml +++ b/src/coreclr/inc/corerror.xml @@ -2189,6 +2189,12 @@ The delegate contains a delegate currently not supported by the API. + + CORDBG_E_ASSEMBLY_UPDATES_APPLIED + "The operation is not supported because assembly updates have been applied." + The operation is not supported because assembly updates have been applied. + + PEFMT_E_64BIT "File is PE32+." diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 028a9c6f52ec3..de3e95df31e06 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -16,18 +16,6 @@ #ifndef __CORHDR_H__ #define __CORHDR_H__ -#define FRAMEWORK_REGISTRY_KEY "Software\\Microsoft\\.NETFramework" -#define FRAMEWORK_REGISTRY_KEY_W W("Software\\Microsoft\\.NETFramework") - -// keys for HKCU -#ifdef HOST_64BIT -#define USER_FRAMEWORK_REGISTRY_KEY "Software\\Microsoft\\.NETFramework64" -#define USER_FRAMEWORK_REGISTRY_KEY_W W("Software\\Microsoft\\.NETFramework64") -#else -#define USER_FRAMEWORK_REGISTRY_KEY "Software\\Microsoft\\.NETFramework" -#define USER_FRAMEWORK_REGISTRY_KEY_W W("Software\\Microsoft\\.NETFramework") -#endif - #include #ifdef _MSC_VER @@ -1821,10 +1809,6 @@ typedef enum CorAttributeTargets #define SUBJECT_ASSEMBLY_TYPE_NAME "IgnoresAccessChecksToAttribute" #define SUBJECT_ASSEMBLY_SIG {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 1, ELEMENT_TYPE_VOID, ELEMENT_TYPE_STRING} -#define DISABLED_PRIVATE_REFLECTION_TYPE_W W("System.Runtime.CompilerServices.DisablePrivateReflectionAttribute") -#define DISABLED_PRIVATE_REFLECTION_TYPE "System.Runtime.CompilerServices.DisablePrivateReflectionAttribute" -#define DISABLED_PRIVATE_REFLECTION_SIG {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0, ELEMENT_TYPE_VOID} - #define DEFAULTDOMAIN_STA_TYPE_W W("System.STAThreadAttribute") #define DEFAULTDOMAIN_STA_TYPE "System.STAThreadAttribute" #define DEFAULTDOMAIN_STA_SIG {IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS, 0, ELEMENT_TYPE_VOID} @@ -1898,6 +1882,7 @@ typedef enum LoadHintEnum #define CMOD_CALLCONV_NAME_THISCALL "CallConvThiscall" #define CMOD_CALLCONV_NAME_FASTCALL "CallConvFastcall" #define CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION "CallConvSuppressGCTransition" +#define CMOD_CALLCONV_NAME_MEMBERFUNCTION "CallConvMemberFunction" #endif // MACROS_NOT_SUPPORTED diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index c7922d3fff579..c971d807a800b 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -595,7 +595,7 @@ enum CorInfoHelpFunc CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument - CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, // Transition to cooperative mode in reverse P/Invoke prolog, frame is the first argument + CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, // Transition to cooperative mode in reverse P/Invoke prolog, frame is the first argument CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER_TRACK_TRANSITIONS, // Transition to cooperative mode and track transitions in reverse P/Invoke prolog. CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, // Transition to preemptive mode in reverse P/Invoke epilog, frame is the first argument CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT_TRACK_TRANSITIONS, // Transition to preemptive mode and track transitions in reverse P/Invoke prolog. @@ -711,17 +711,20 @@ enum class CorInfoCallConvExtension C, Stdcall, Thiscall, - Fastcall + Fastcall, // New calling conventions supported with the extensible calling convention encoding go here. + CMemberFunction, + StdcallMemberFunction, + FastcallMemberFunction }; #ifdef TARGET_X86 inline bool IsCallerPop(CorInfoCallConvExtension callConv) { #ifdef UNIX_X86_ABI - return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C; + return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C || callConv == CorInfoCallConvExtension::CMemberFunction; #else - return callConv == CorInfoCallConvExtension::C; + return callConv == CorInfoCallConvExtension::C || callConv == CorInfoCallConvExtension::CMemberFunction; #endif // UNIX_X86_ABI } #endif @@ -729,7 +732,7 @@ inline bool IsCallerPop(CorInfoCallConvExtension callConv) // Determines whether or not this calling convention is an instance method calling convention. inline bool callConvIsInstanceMethodCallConv(CorInfoCallConvExtension callConv) { - return callConv == CorInfoCallConvExtension::Thiscall; + return callConv == CorInfoCallConvExtension::Thiscall || callConv == CorInfoCallConvExtension::CMemberFunction || callConv == CorInfoCallConvExtension::StdcallMemberFunction || callConv == CorInfoCallConvExtension::FastcallMemberFunction; } // These are returned from getMethodOptions @@ -1524,6 +1527,9 @@ enum CorInfoTokenKind // token comes from CEE_LDVIRTFTN CORINFO_TOKENKIND_Ldvirtftn = 0x400 | CORINFO_TOKENKIND_Method, + + // token comes from devirtualizing a method + CORINFO_TOKENKIND_DevirtualizedMethod = 0x800 | CORINFO_TOKENKIND_Method, }; struct CORINFO_RESOLVED_TOKEN diff --git a/src/coreclr/inc/corjitflags.h b/src/coreclr/inc/corjitflags.h index e8c330e3e41b3..c749e876d2e0f 100644 --- a/src/coreclr/inc/corjitflags.h +++ b/src/coreclr/inc/corjitflags.h @@ -97,7 +97,12 @@ class CORJIT_FLAGS CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method +#if defined(TARGET_ARM) + CORJIT_FLAG_SOFTFP_ABI = 43, // JIT should generate PC-relative address computations instead of EE relocation records +#else // !defined(TARGET_ARM) CORJIT_FLAG_UNUSED16 = 43, +#endif // !defined(TARGET_ARM) + CORJIT_FLAG_UNUSED17 = 44, CORJIT_FLAG_UNUSED18 = 45, CORJIT_FLAG_UNUSED19 = 46, diff --git a/src/coreclr/inc/corpriv.h b/src/coreclr/inc/corpriv.h index 49056335cc763..e480706e3d553 100644 --- a/src/coreclr/inc/corpriv.h +++ b/src/coreclr/inc/corpriv.h @@ -19,7 +19,6 @@ #include "corimage.h" #include "metadata.h" #include -#include "peinformation.h" // interface IAssemblyName; @@ -294,17 +293,162 @@ typedef enum CorOpenFlagsInternal #define COR_MODULE_CLASS "" #define COR_WMODULE_CLASS W("") +//***************************************************************************** +//***************************************************************************** +// +// CeeGen interfaces for generating in-memory Common Language Runtime files +// +//***************************************************************************** +//***************************************************************************** + +typedef void* HCEESECTION; + +typedef enum { + sdNone = 0, + sdReadOnly = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA, + sdReadWrite = sdReadOnly | IMAGE_SCN_MEM_WRITE, + sdExecute = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE +} CeeSectionAttr; + +// +// Relocation types. +// + +typedef enum { + // generate only a section-relative reloc, nothing into .reloc section + srRelocAbsolute, + + // generate a .reloc for a pointer sized location, + // This is transformed into BASED_HIGHLOW or BASED_DIR64 based on the platform + srRelocHighLow = 3, + + // generate a .reloc for the top 16-bits of a 32 bit number, where the + // bottom 16 bits are included in the next word in the .reloc table + srRelocHighAdj, // Never Used + + // generate a token map relocation, nothing into .reloc section + srRelocMapToken, + + // relative address fixup + srRelocRelative, + + // Generate only a section-relative reloc, nothing into .reloc + // section. This reloc is relative to the file position of the + // section, not the section's virtual address. + srRelocFilePos, + + // code relative address fixup + srRelocCodeRelative, + + // generate a .reloc for a 64 bit address in an ia64 movl instruction + srRelocIA64Imm64, + + // generate a .reloc for a 64 bit address + srRelocDir64, + + // generate a .reloc for a 25-bit PC relative address in an ia64 br.call instruction + srRelocIA64PcRel25, + + // generate a .reloc for a 64-bit PC relative address in an ia64 brl.call instruction + srRelocIA64PcRel64, + + // generate a 30-bit section-relative reloc, used for tagged pointer values + srRelocAbsoluteTagged, + + + // A sentinel value to help ensure any additions to this enum are reflected + // in PEWriter.cpp's RelocName array. + srRelocSentinel, + + // Flags that can be used with the above reloc types + + // do not emit base reloc + srNoBaseReloc = 0x4000, + + // pre-fixup contents of memory are ptr rather than a section offset + srRelocPtr = 0x8000, + + // legal enums which include the Ptr flag + srRelocAbsolutePtr = srRelocPtr + srRelocAbsolute, + srRelocHighLowPtr = srRelocPtr + srRelocHighLow, + srRelocRelativePtr = srRelocPtr + srRelocRelative, + srRelocIA64Imm64Ptr = srRelocPtr + srRelocIA64Imm64, + srRelocDir64Ptr = srRelocPtr + srRelocDir64, + +} CeeSectionRelocType; + +typedef union { + USHORT highAdj; +} CeeSectionRelocExtra; + //------------------------------------- //--- ICeeGenInternal //------------------------------------- -// {9fd3c7af-dc4e-4b9b-be22-9cf8cc577489} -EXTERN_GUID(IID_ICeeGenInternal, 0x9fd3c7af, 0xdc4e, 0x4b9b, 0xbe, 0x22, 0x9c, 0xf8, 0xcc, 0x57, 0x74, 0x89); - +// {8C26FC02-BE39-476D-B835-E17EDD120246} +EXTERN_GUID(IID_ICeeGenInternal, 0x8c26fc02, 0xbe39, 0x476d, 0xb8, 0x35, 0xe1, 0x7e, 0xdd, 0x12, 0x2, 0x46); #undef INTERFACE #define INTERFACE ICeeGenInternal DECLARE_INTERFACE_(ICeeGenInternal, IUnknown) { - STDMETHOD (SetInitialGrowth) (DWORD growth) PURE; + STDMETHOD(EmitString) ( + _In_ + LPWSTR lpString, // [IN] String to emit + ULONG * RVA) PURE; // [OUT] RVA for string emitted string + + STDMETHOD(GetString) ( + ULONG RVA, // [IN] RVA for string to return + _Out_opt_ + LPWSTR * lpString) PURE; // [OUT] Returned string + + STDMETHOD(AllocateMethodBuffer) ( + ULONG cchBuffer, // [IN] Length of buffer to create + UCHAR * *lpBuffer, // [OUT] Returned buffer + ULONG * RVA) PURE; // [OUT] RVA for method + + STDMETHOD(GetMethodBuffer) ( + ULONG RVA, // [IN] RVA for method to return + UCHAR * *lpBuffer) PURE; // [OUT] Returned buffer + + STDMETHOD(GetIMapTokenIface) ( + IUnknown * *pIMapToken) PURE; + + STDMETHOD(GenerateCeeFile) () PURE; + + STDMETHOD(GetIlSection) ( + HCEESECTION * section) PURE; + + STDMETHOD(GetStringSection) ( + HCEESECTION * section) PURE; + + STDMETHOD(AddSectionReloc) ( + HCEESECTION section, + ULONG offset, + HCEESECTION relativeTo, + CeeSectionRelocType relocType) PURE; + + // use these only if you have special section requirements not handled + // by other APIs + STDMETHOD(GetSectionCreate) ( + const char* name, + DWORD flags, + HCEESECTION * section) PURE; + + STDMETHOD(GetSectionDataLen) ( + HCEESECTION section, + ULONG * dataLen) PURE; + + STDMETHOD(GetSectionBlock) ( + HCEESECTION section, + ULONG len, + ULONG align = 1, + void** ppBytes = 0) PURE; + + STDMETHOD(ComputePointer) ( + HCEESECTION section, + ULONG RVA, // [IN] RVA for method to return + UCHAR * *lpBuffer) PURE; // [OUT] Returned buffer + + STDMETHOD(SetInitialGrowth) (DWORD growth) PURE; }; // diff --git a/src/coreclr/inc/crsttypes.h b/src/coreclr/inc/crsttypes.h index 071d767f9ec7d..a1bab2ecb906c 100644 --- a/src/coreclr/inc/crsttypes.h +++ b/src/coreclr/inc/crsttypes.h @@ -149,7 +149,7 @@ int g_rgCrstLevelMap[] = 14, // CrstAppDomainHandleTable 0, // CrstArgBasedStubCache 0, // CrstAssemblyList - 7, // CrstAssemblyLoader + 12, // CrstAssemblyLoader 3, // CrstAvailableClass 4, // CrstAvailableParamTypes 7, // CrstBaseDomain diff --git a/src/coreclr/inc/cvconst.h b/src/coreclr/inc/cvconst.h index 3a0e3b98d92d7..3fbbfdd011a2c 100644 --- a/src/coreclr/inc/cvconst.h +++ b/src/coreclr/inc/cvconst.h @@ -1580,10 +1580,12 @@ typedef enum CV_HREG_e { CV_ARM64_LR = 80, CV_ARM64_SP = 81, CV_ARM64_ZR = 82, + CV_ARM64_PC = 83, - // statue register + // status registers CV_ARM64_NZCV = 90, + CV_ARM64_CPSR = 91, // 32-bit floating point registers diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 9f2b5c96fae44..e27c122b5645a 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -248,5 +248,9 @@ DEFINE_DACVAR(ULONG, TADDR, dac__g_MiniMetaDataBuffAddress, ::g_MiniMetaDataBuff DEFINE_DACVAR(ULONG, SIZE_T, dac__g_clrNotificationArguments, ::g_clrNotificationArguments) +#ifdef EnC_SUPPORTED +DEFINE_DACVAR(ULONG, bool, dac__g_metadataUpdatesApplied, ::g_metadataUpdatesApplied) +#endif + #undef DEFINE_DACVAR #undef DEFINE_DACVAR_NO_DUMP diff --git a/src/coreclr/inc/ex.h b/src/coreclr/inc/ex.h index d7d06781ace4e..d97fad1da9a0b 100644 --- a/src/coreclr/inc/ex.h +++ b/src/coreclr/inc/ex.h @@ -719,7 +719,7 @@ class CAutoTryCleanup // Special define to be used in EEStartup that will also check for VM initialization before // commencing on a path that may use the managed thread object. #define RethrowTerminalExceptionsWithInitCheck \ - if ((g_fEEStarted == TRUE) && (GetThread() != NULL)) \ + if ((g_fEEStarted == TRUE) && (GetThreadNULLOk() != NULL)) \ { \ RethrowTerminalExceptions \ } diff --git a/src/coreclr/inc/fusion.idl b/src/coreclr/inc/fusion.idl deleted file mode 100644 index 7ffa9837bbd47..0000000000000 --- a/src/coreclr/inc/fusion.idl +++ /dev/null @@ -1,137 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -//+--------------------------------------------------------------------------- -// -// Microsoft Windows -// File: fusion.idl -// -// Contents: Fusion Interfaces -// -// Classes: -// -// Functions: -// -// -//---------------------------------------------------------------------------- - -cpp_quote("") -cpp_quote("#ifdef _MSC_VER") -cpp_quote("#pragma comment(lib,\"uuid.lib\")") -cpp_quote("#endif") -cpp_quote("") -cpp_quote("//---------------------------------------------------------------------------=") -cpp_quote("// Fusion Interfaces.") -cpp_quote("") - -import "objidl.idl"; - -cpp_quote("#ifdef _MSC_VER") -cpp_quote("#pragma once") -cpp_quote("#endif") - - -typedef enum _tagAssemblyContentType -{ - AssemblyContentType_Default = 0x00000000, - AssemblyContentType_WindowsRuntime = 0x00000001, - AssemblyContentType_Invalid = 0xffffffff -} AssemblyContentType; - - -/////////////////////////////////////////////////////////////////////////////// -// -// IAssemblyName -// -/////////////////////////////////////////////////////////////////////////////// - -cpp_quote("// {CD193BC0-B4BC-11d2-9833-00C04FC31D2E}") -cpp_quote("EXTERN_GUID(IID_IAssemblyName, 0xCD193BC0, 0xB4BC, 0x11d2, 0x98, 0x33, 0x00, 0xC0, 0x4F, 0xC3, 0x1D, 0x2E);") - - -[ - local, - object, - uuid(CD193BC0-B4BC-11d2-9833-00C04FC31D2E), - pointer_default(unique) -] -interface IAssemblyName: IUnknown -{ - typedef [unique] IAssemblyName *LPASSEMBLYNAME; - - typedef enum - { - ASM_NAME_PUBLIC_KEY = 0, // 0 - ASM_NAME_PUBLIC_KEY_TOKEN, // 1 - ASM_NAME_HASH_VALUE, // 2 - ASM_NAME_NAME, // 3 - ASM_NAME_MAJOR_VERSION, // 4 - ASM_NAME_MINOR_VERSION, // 5 - ASM_NAME_BUILD_NUMBER, // 6 - ASM_NAME_REVISION_NUMBER, // 7 - ASM_NAME_CULTURE, // 8 - ASM_NAME_PROCESSOR_ID_ARRAY, // 9 - ASM_NAME_OSINFO_ARRAY, // 10 ... 0x0a - ASM_NAME_HASH_ALGID, // 11 ... 0x0b - ASM_NAME_ALIAS, // 12 ... 0x0c - ASM_NAME_CODEBASE_URL, // 13 ... 0x0d - ASM_NAME_CODEBASE_LASTMOD, // 14 ... 0x0e - ASM_NAME_NULL_PUBLIC_KEY, // 15 ... 0x0f - ASM_NAME_NULL_PUBLIC_KEY_TOKEN, // 16 ... 0x10 - ASM_NAME_CUSTOM, // 17 ... 0x11 - ASM_NAME_NULL_CUSTOM, // 18 ... 0x12 - ASM_NAME_MVID, // 19 ... 0x13 - ASM_NAME_FILE_MAJOR_VERSION, // 20 ... 0x14 - ASM_NAME_FILE_MINOR_VERSION, // 21 ... 0x15 - ASM_NAME_FILE_BUILD_NUMBER, // 22 ... 0x16 - ASM_NAME_FILE_REVISION_NUMBER, // 23 ... 0x17 - ASM_NAME_RETARGET, // 24 ... 0x18 - ASM_NAME_SIGNATURE_BLOB, // 25 ... 0x19 - ASM_NAME_CONFIG_MASK, // 26 ... 0x1a - ASM_NAME_ARCHITECTURE, // 27 ... 0x1b - ASM_NAME_CONTENT_TYPE, // 28 ... 0x1c - ASM_NAME_MAX_PARAMS // 29 ... 0x1d - } ASM_NAME; - - typedef enum - { - ASM_DISPLAYF_VERSION = 0x1, - ASM_DISPLAYF_CULTURE = 0x2, - ASM_DISPLAYF_PUBLIC_KEY_TOKEN = 0x4, - ASM_DISPLAYF_PUBLIC_KEY = 0x8, - ASM_DISPLAYF_CUSTOM = 0x10, - ASM_DISPLAYF_PROCESSORARCHITECTURE = 0x20, - ASM_DISPLAYF_LANGUAGEID = 0x40, - ASM_DISPLAYF_RETARGET = 0x80, - ASM_DISPLAYF_CONFIG_MASK = 0x100, - ASM_DISPLAYF_MVID = 0x200, - ASM_DISPLAYF_CONTENT_TYPE = 0x400, - - - // ASM_DISPLAYF_FULL shows the full identity of the assembly. - // It should be used when you are working with APIs taking - // assembly full identity, such as GAC APIs. - // - // WARNING!!! ASM_DISPLAYF_FULL will change in the future, - // when we introduce new attributes. This means the identity returned - // will change from CLR version to version. Don't ever assume - // the identity will not change. !!! - ASM_DISPLAYF_FULL = ASM_DISPLAYF_VERSION - | ASM_DISPLAYF_CULTURE - | ASM_DISPLAYF_PUBLIC_KEY_TOKEN - | ASM_DISPLAYF_RETARGET - | ASM_DISPLAYF_PROCESSORARCHITECTURE - | ASM_DISPLAYF_CONTENT_TYPE, - } ASM_DISPLAY_FLAGS; - - HRESULT SetProperty( - [in] DWORD PropertyId, - [in] void const * pvProperty, - [in] DWORD cbProperty); - - HRESULT GetProperty( - [in] DWORD PropertyId, - [out] LPVOID pvProperty, - [in, out] LPDWORD pcbProperty); -} - diff --git a/src/coreclr/inc/iceefilegen.h b/src/coreclr/inc/iceefilegen.h index cb4b5ef868e05..f0fb5b091c9ca 100644 --- a/src/coreclr/inc/iceefilegen.h +++ b/src/coreclr/inc/iceefilegen.h @@ -32,7 +32,7 @@ pEmit = IMetaDataEmit object; // Get a metadata emitter GetSectionBlock(...);, AddSectionReloc(...); ... // Get blocks, write non-metadata information, and add necessary relocation EmitMetaDataEx(pEmit); // Write out the metadata - GenerateCeeFile(...); // Write out the file. Implicitly calls LinkCeeFile and FixupCeeFile + GenerateCeeFile(...); // Write out the file. DestroyICeeFileGen(...); // Release the ICeeFileGen object */ @@ -42,7 +42,7 @@ #define _ICEEFILEGEN_H_ #include -#include "cor.h" +#include "corpriv.h" class ICeeFileGen; @@ -58,7 +58,6 @@ typedef HRESULT (__stdcall * PFN_DestroyICeeFileGen)(ICeeFileGen ** ceeFileGen); #define ICEE_CREATE_FILE_PE64 0x00000002 // Create a PE+ (64-bit) #define ICEE_CREATE_FILE_CORMAIN_STUB 0x00000004 // add a mscoree!_Cor___Main call stub #define ICEE_CREATE_FILE_STRIP_RELOCS 0x00000008 // strip the .reloc section -#define ICEE_CREATE_FILE_EMIT_FIXUPS 0x00000010 // emit fixups for use by Vulcan #define ICEE_CREATE_MACHINE_MASK 0x0000FF00 // space for up to 256 machine targets (note: most users just do a bit check, not an equality compare after applying the mask) #define ICEE_CREATE_MACHINE_ILLEGAL 0x00000000 // An illegal machine name @@ -79,12 +78,7 @@ class ICeeFileGen { virtual HRESULT CreateCeeFile(HCEEFILE *ceeFile); // call this to instantiate a file handle - // @FUTURE: remove this function. We no longer support mdScope. - virtual HRESULT EmitMetaData (HCEEFILE ceeFile, IMetaDataEmit *emitter, mdScope scope); - virtual HRESULT EmitLibraryName (HCEEFILE ceeFile, IMetaDataEmit *emitter, mdScope scope); - virtual HRESULT EmitMethod (); // @FUTURE: remove virtual HRESULT GetMethodRVA (HCEEFILE ceeFile, ULONG codeOffset, ULONG *codeRVA); - virtual HRESULT EmitSignature (); // @FUTURE: remove virtual HRESULT EmitString (HCEEFILE ceeFile,_In_ LPWSTR strValue, ULONG *strRef); virtual HRESULT GenerateCeeFile (HCEEFILE ceeFile); @@ -102,24 +96,9 @@ class ICeeFileGen { virtual HRESULT SetSubsystem(HCEEFILE ceeFile, DWORD subsystem, DWORD major, DWORD minor); - virtual HRESULT SetEntryClassToken (); //@FUTURE: remove - virtual HRESULT GetEntryClassToken (); //@FUTURE: remove - - virtual HRESULT SetEntryPointDescr (); //@FUTURE: remove - virtual HRESULT GetEntryPointDescr (); //@FUTURE: remove - - virtual HRESULT SetEntryPointFlags (); //@FUTURE: remove - virtual HRESULT GetEntryPointFlags (); //@FUTURE: remove - virtual HRESULT SetDllSwitch (HCEEFILE ceeFile, BOOL dllSwitch); virtual HRESULT GetDllSwitch (HCEEFILE ceeFile, BOOL *dllSwitch); - virtual HRESULT SetLibraryName (HCEEFILE ceeFile, _In_ LPWSTR LibraryName); - _Return_type_success_( return == S_OK ) - virtual HRESULT GetLibraryName (HCEEFILE ceeFile, _Out_ LPWSTR *LibraryName); - - virtual HRESULT SetLibraryGuid (HCEEFILE ceeFile, _In_ LPWSTR LibraryGuid); - virtual HRESULT DestroyCeeFile(HCEEFILE *ceeFile); // call this to delete a file handle virtual HRESULT GetSectionCreate (HCEEFILE ceeFile, const char *name, DWORD flags, HCEESECTION *section); @@ -128,18 +107,8 @@ class ICeeFileGen { virtual HRESULT GetSectionDataLen (HCEESECTION section, ULONG *dataLen); virtual HRESULT GetSectionBlock (HCEESECTION section, ULONG len, ULONG align=1, void **ppBytes=0); - virtual HRESULT TruncateSection (HCEESECTION section, ULONG len); virtual HRESULT AddSectionReloc (HCEESECTION section, ULONG offset, HCEESECTION relativeTo, CeeSectionRelocType relocType); - // deprecated: use SetDirectoryEntry instead - virtual HRESULT SetSectionDirectoryEntry (HCEESECTION section, ULONG num); - - virtual HRESULT CreateSig (); //@FUTURE: Remove - virtual HRESULT AddSigArg (); //@FUTURE: Remove - virtual HRESULT SetSigReturnType (); //@FUTURE: Remove - virtual HRESULT SetSigCallingConvention (); //@FUTURE: Remove - virtual HRESULT DeleteSig (); //@FUTURE: Remove - virtual HRESULT SetEntryPoint (HCEEFILE ceeFile, mdMethodDef method); virtual HRESULT GetEntryPoint (HCEEFILE ceeFile, mdMethodDef *method); @@ -154,18 +123,13 @@ class ICeeFileGen { // Use EmitMetaDataAt() for more control virtual HRESULT EmitMetaDataEx (HCEEFILE ceeFile, IMetaDataEmit *emitter); - virtual HRESULT EmitLibraryNameEx (HCEEFILE ceeFile, IMetaDataEmit *emitter); virtual HRESULT GetIMapTokenIfaceEx(HCEEFILE ceeFile, IMetaDataEmit *emitter, IUnknown **pIMapToken); - virtual HRESULT EmitMacroDefinitions(HCEEFILE ceeFile, void *pData, DWORD cData); virtual HRESULT CreateCeeFileFromICeeGen( - ICeeGen *pFromICeeGen, HCEEFILE *ceeFile, DWORD createFlags = ICEE_CREATE_FILE_PURE_IL); // call this to instantiate a file handle + ICeeGenInternal *pFromICeeGen, HCEEFILE *ceeFile, DWORD createFlags = ICEE_CREATE_FILE_PURE_IL); // call this to instantiate a file handle virtual HRESULT SetManifestEntry(HCEEFILE ceeFile, ULONG size, ULONG offset); - virtual HRESULT SetEnCRVABase(HCEEFILE ceeFile, ULONG dataBase, ULONG rdataBase); - virtual HRESULT GenerateCeeMemoryImage (HCEEFILE ceeFile, void **ppImage); - virtual HRESULT ComputeSectionOffset(HCEESECTION section, _In_ char *ptr, unsigned *offset); @@ -178,9 +142,6 @@ class ICeeFileGen { // Layout the sections and assign their starting addresses virtual HRESULT LinkCeeFile (HCEEFILE ceeFile); - // Apply relocations to any pointer data. Also generate PE base relocs - virtual HRESULT FixupCeeFile (HCEEFILE ceeFile); - // Base RVA assinged to the section. To be called only after LinkCeeFile() virtual HRESULT GetSectionRVA (HCEESECTION section, ULONG *rva); @@ -188,8 +149,6 @@ class ICeeFileGen { virtual HRESULT ComputeSectionPointer(HCEESECTION section, ULONG offset, _Out_ char **ptr); - virtual HRESULT SetObjSwitch (HCEEFILE ceeFile, BOOL objSwitch); - virtual HRESULT GetObjSwitch (HCEEFILE ceeFile, BOOL *objSwitch); virtual HRESULT SetVTableEntry(HCEEFILE ceeFile, ULONG size, ULONG offset); // See the end of interface for another overload of AetVTableEntry diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 07843243c24f6..cda748635c721 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 73d20c3a-75a9-4eea-a952-60419d67b6a6 */ - 0x73d20c3a, - 0x75a9, - 0x4eea, - {0xa9, 0x52, 0x60, 0x41, 0x9d, 0x67, 0xb6, 0xa6} +constexpr GUID JITEEVersionIdentifier = { /* a1f5e9a1-ee44-42f9-9319-e2a2dbf8c5c9 */ + 0xa1f5e9a1, + 0xee44, + 0x42f9, + {0x93, 0x19, 0xe2, 0xa2, 0xdb, 0xf8, 0xc5, 0xc9} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/inc/peinformation.h b/src/coreclr/inc/peinformation.h deleted file mode 100644 index 48039fd9fe175..0000000000000 --- a/src/coreclr/inc/peinformation.h +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -------------------------------------------------------------------------------- -// PEInformation.h -// - -// -------------------------------------------------------------------------------- - -#ifndef PEINFORMATION_H -#define PEINFORMATION_H - -#ifndef PEKIND_ENUM_DEFINED -#define PEKIND_ENUM_DEFINED -// This must match the definition of pekind in fusion.idl -typedef enum _tagPEKIND -{ - peNone = 0x00000000, - peMSIL = 0x00000001, - peI386 = 0x00000002, - peIA64 = 0x00000003, - peAMD64 = 0x00000004, - peARM = 0x00000005, - peARM64 = 0x00000006, - peInvalid = 0xffffffff -} PEKIND; - -#endif - - -inline bool IsPE64(PEKIND x) -{ - return ( (x == peIA64) || (x == peAMD64) || (x == peARM64) ); -} - -inline bool IsPE32(PEKIND x) -{ - return ( (x == peI386) || (x == peARM) ); -} - -inline bool IsPEMSIL(PEKIND x) -{ - return ( (x == peMSIL) ); -} - -#ifdef HOST_64BIT -inline bool IsProcess32() { return false; } -#else -inline bool IsProcess32() { return true; } -#endif - -#if defined(TARGET_X86) -inline PEKIND TargetNativePEKIND() { return peI386; } -#elif defined(TARGET_AMD64) -inline PEKIND TargetNativePEKIND() { return peAMD64; } -#elif defined(TARGET_ARM) -inline PEKIND TargetNativePEKIND() { return peARM; } -#elif defined(TARGET_ARM64) -inline PEKIND TargetNativePEKIND() { return peARM64; } -#else -#error Need to define valid TargetNativePEKIND() -#endif - -STDAPI RuntimeIsValidAssemblyOnThisPlatform_CheckProcessorArchitecture(PEKIND processorArchitecture, BOOL bForInstall); - -//***************************************************************************** -// Intreprets CLRPeKind and dwImageType to get PeKind as per the CLRBitness -// API, CLRPeKind and dwImageType can be recoved from GetPEKind() if you -// have the metadata, or retrieved directly from the headers as per the -// implementation in shim.cpp:_CorValidateImage. -//***************************************************************************** -HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, PEKIND * PeKind); -HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, DWORD dwAssemblyFlags, PEKIND * PeKind); - -#endif // PEINFORMATION_H diff --git a/src/coreclr/inc/readme.md b/src/coreclr/inc/readme.md index ee47be8e514ed..bb685385d022b 100644 --- a/src/coreclr/inc/readme.md +++ b/src/coreclr/inc/readme.md @@ -4,9 +4,10 @@ This directory has a variety of .idl files (such as corprof.idl) that need a lit the build rules would automatically convert the idls into corresponding .h/.c files and include them in compilations. On non-windows platforms we don't have an equivalent for midl.exe which did that conversion so we work around the issue by doing: -- Build on Windows as normal, which will generate files in artifacts\obj\windows.x64.Debug\src\inc\idls_out\ -- Copy any updated headers into src\pal\prebuilt\inc\ -- If needed, adjust any of the .cpp files in src\pal\prebuilt\idl\ by hand, using the corresponding artifacts\obj\windows.x64.Debug\src\inc\idls_out\*_i.c as a guide. Typically -this is just adding MIDL_DEFINE_GUID(...) for any new classes/interfaces that have been added to the idl file. +- Build on Windows as normal, which will generate files in `artifacts\obj\windows.x64.Debug\inc\idls_out\` +- Copy any updated headers into `src\coreclr\pal\prebuilt\inc\` +- If needed, adjust any of the .cpp files in `src\coreclr\pal\prebuilt\idl\` by hand, using the corresponding `artifacts\obj\windows.x64.Debug\inc\idls_out\*_i.c` as a guide. + - Typically +this is just adding `MIDL_DEFINE_GUID(...)` for any new classes/interfaces that have been added to the idl file. Include these src changes with the remainder of your work when you submit a PR. diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index fb2b641b70e4f..f35d80c37d145 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -16,7 +16,7 @@ // Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs #define READYTORUN_MAJOR_VERSION 0x0005 -#define READYTORUN_MINOR_VERSION 0x0002 +#define READYTORUN_MINOR_VERSION 0x0003 #define MINIMUM_READYTORUN_MAJOR_VERSION 0x003 @@ -79,7 +79,8 @@ enum class ReadyToRunSectionType : uint32_t InliningInfo2 = 114, // Added in V4.1 ComponentAssemblies = 115, // Added in V4.1 OwnerCompositeExecutable = 116, // Added in V4.1 - PgoInstrumentationData = 117, // Added in 5.2 + PgoInstrumentationData = 117, // Added in V5.2 + ManifestAssemblyMvids = 118, // Added in V5.3 // If you add a new section consider whether it is a breaking or non-breaking change. // Usually it is non-breaking, but if it is preferable to have older runtimes fail diff --git a/src/coreclr/inc/safemath.h b/src/coreclr/inc/safemath.h index d93e4a57011e5..3c020de688478 100644 --- a/src/coreclr/inc/safemath.h +++ b/src/coreclr/inc/safemath.h @@ -24,7 +24,7 @@ // we can rely on that is available everywhere. In // several other tools we just take the recourse of disabling asserts, // we'll do the same here. -// Ideally we'd have a collection of common utilities available evererywhere. +// Ideally we'd have a collection of common utilities available everywhere. #define _ASSERTE_SAFEMATH(a) #endif #endif diff --git a/src/coreclr/inc/stresslog.h b/src/coreclr/inc/stresslog.h index 706f3c942377d..d125591b7364e 100644 --- a/src/coreclr/inc/stresslog.h +++ b/src/coreclr/inc/stresslog.h @@ -267,7 +267,7 @@ struct StressLogMsg; class StressLog { public: static void Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, - ULONGLONG maxBytesTotal, void* moduleBase, LPWSTR logFilename = nullptr); + unsigned maxBytesTotal, void* moduleBase, LPWSTR logFilename = nullptr); static void Terminate(BOOL fProcessDetach=FALSE); static void ThreadDetach(); // call at DllMain THREAD_DETACH if you want to recycle thread logs #ifndef STRESS_LOG_ANALYZER diff --git a/src/coreclr/inc/switches.h b/src/coreclr/inc/switches.h index 8fb65335116b8..322a71ea11f44 100644 --- a/src/coreclr/inc/switches.h +++ b/src/coreclr/inc/switches.h @@ -150,7 +150,7 @@ // do not work reliably with conservative GC. #define FEATURE_CONSERVATIVE_GC 1 -#if (defined(TARGET_ARM) && !defined(ARM_SOFTFP)) || defined(TARGET_ARM64) +#if (defined(TARGET_ARM) && (!defined(ARM_SOFTFP) || defined(CONFIGURABLE_ARM_ABI))) || defined(TARGET_ARM64) #define FEATURE_HFA #endif diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 22193da0ff362..a3c446bb74788 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -1010,177 +1010,6 @@ void SplitPath(__in SString const &path, __inout_opt SString *fname, __inout_opt SString *ext); -#if !defined(NO_CLRCONFIG) - -//***************************************************************************** -// -// **** REGUTIL - Static helper functions for reading/writing to Windows registry. -// -//***************************************************************************** - - -class REGUTIL -{ -public: -//***************************************************************************** - - enum CORConfigLevel - { - COR_CONFIG_ENV = 0x01, - COR_CONFIG_USER = 0x02, - COR_CONFIG_MACHINE = 0x04, - - COR_CONFIG_REGISTRY = (COR_CONFIG_USER|COR_CONFIG_MACHINE), - COR_CONFIG_ALL = (COR_CONFIG_ENV|COR_CONFIG_USER|COR_CONFIG_MACHINE), - }; - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigDWORD_DontUse_( - LPCWSTR name, - DWORD defValue, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static HRESULT GetConfigDWORD_DontUse_( - LPCWSTR name, - DWORD defValue, - __out DWORD * result, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - static ULONGLONG GetConfigULONGLONG_DontUse_( - LPCWSTR name, - ULONGLONG defValue, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigFlag_DontUse_( - LPCWSTR name, - DWORD bitToSet, - BOOL defValue = FALSE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static LPWSTR GetConfigString_DontUse_( - LPCWSTR name, - BOOL fPrependCOMPLUS = TRUE, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fUsePerfCache = TRUE); - - static void FreeConfigString(__in __in_z LPWSTR name); - -private: - static LPWSTR EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS); - -private: -//***************************************************************************** -// Get either a DWORD or ULONGLONG. Always puts the result in a ULONGLONG that -// you can safely cast to a DWORD if fGetDWORD is TRUE. -//***************************************************************************** - static HRESULT GetConfigInteger( - LPCWSTR name, - ULONGLONG defValue, - __out ULONGLONG * result, - BOOL fGetDWORD = TRUE, - CORConfigLevel level = COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); -public: - - -//***************************************************************************** -// (Optional) Initialize the config registry cache -// (see ConfigCacheValueNameSeenPerhaps, below.) -//***************************************************************************** - static void InitOptionalConfigCache(); - -private: - - -//***************************************************************************** -// Return TRUE if the registry value name might have been seen in the registry -// at startup; -// return FALSE if the value was definitely not seen at startup. -// -// Perf Optimization for VSWhidbey:113373. -//***************************************************************************** - static BOOL RegCacheValueNameSeenPerhaps( - LPCWSTR name); -//***************************************************************************** -// Return TRUE if the environment variable name might have been seen at startup; -// return FALSE if the value was definitely not seen at startup. -//***************************************************************************** - static BOOL EnvCacheValueNameSeenPerhaps( - LPCWSTR name); - - static BOOL s_fUseRegCache; // Enable registry cache; if FALSE, CCVNSP - // always returns TRUE. - static BOOL s_fUseEnvCache; // Enable env cache. - - // Open the .NetFramework keys once and cache the handles - static HKEY s_hMachineFrameworkKey; - static HKEY s_hUserFrameworkKey; -}; - -// need this here because CLRConfig depends on REGUTIL, and ConfigStringHolder depends on CLRConfig -#include "clrconfig.h" - -//----------------------------------------------------------------------------- -// Wrapper for configuration strings. -// This serves as a holder to call FreeConfigString. -class ConfigStringHolder -{ -public: - ConfigStringHolder() { m_wszString = NULL; } - ~ConfigStringHolder() - { - Clear(); - } - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - void Init_DontUse_(LPCWSTR wszName) - { - Clear(); - m_wszString = REGUTIL::GetConfigString_DontUse_(wszName); - } - - // Free resources. - void Clear() - { - if (m_wszString != NULL) - { - REGUTIL::FreeConfigString(m_wszString); - m_wszString = NULL; - } - } - - // Get the string value. NULL if not set. - LPCWSTR Value() - { - return m_wszString; - } - -private: - LPWSTR m_wszString; -}; - -#endif // defined(NO_CLRCONFIG) - #include "ostype.h" #define CLRGetTickCount64() GetTickCount64() @@ -3784,10 +3613,10 @@ class MethodNamesList : public MethodNamesListBase } }; -#if !defined(NO_CLRCONFIG) +#include "clrconfig.h" /**************************************************************************/ -/* simple wrappers around the REGUTIL and MethodNameList routines that make +/* simple wrappers around the CLRConfig and MethodNameList routines that make the lookup lazy */ /* to be used as static variable - no constructor/destructor, assumes zero @@ -3796,19 +3625,6 @@ class MethodNamesList : public MethodNamesListBase class ConfigDWORD { public: - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - inline DWORD val_DontUse_(__in __in_z LPCWSTR keyName, DWORD defaultVal=0) - { - WRAPPER_NO_CONTRACT; - // make sure that the memory was zero initialized - _ASSERTE(m_inited == 0 || m_inited == 1); - - if (!m_inited) init_DontUse_(keyName, defaultVal); - return m_value; - } inline DWORD val(const CLRConfig::ConfigDWORDInfo & info) { WRAPPER_NO_CONTRACT; @@ -3820,7 +3636,6 @@ class ConfigDWORD } private: - void init_DontUse_(__in __in_z LPCWSTR keyName, DWORD defaultVal=0); void init(const CLRConfig::ConfigDWORDInfo & info); private: @@ -3892,8 +3707,6 @@ class ConfigMethodSet BYTE m_inited; }; -#endif // !defined(NO_CLRCONFIG) - //***************************************************************************** // Convert a pointer to a string into a GUID. //***************************************************************************** diff --git a/src/coreclr/inc/winwrap.h b/src/coreclr/inc/winwrap.h index 1ece44791b444..4d97618984fd2 100644 --- a/src/coreclr/inc/winwrap.h +++ b/src/coreclr/inc/winwrap.h @@ -134,8 +134,6 @@ // // winbase.h -#define WszGetEnvironmentStrings GetEnvironmentStringsW -#define WszFreeEnvironmentStrings FreeEnvironmentStringsW #define WszFormatMessage FormatMessageW #define Wszlstrcmp lstrcmpW #define Wszlstrcmpi lstrcmpiW @@ -331,17 +329,6 @@ InterlockedCompareExchangePointer ( #endif // HOST_X86 && _MSC_VER -#if defined(HOST_ARM) & !defined(HOST_UNIX) -// -// InterlockedCompareExchangeAcquire/InterlockedCompareExchangeRelease is not mapped in SDK to the correct intrinsics. Remove once -// the SDK definition is fixed (OS Bug #516255) -// -#undef InterlockedCompareExchangeAcquire -#define InterlockedCompareExchangeAcquire _InterlockedCompareExchange_acq -#undef InterlockedCompareExchangeRelease -#define InterlockedCompareExchangeRelease _InterlockedCompareExchange_rel -#endif - #if defined(HOST_X86) & !defined(InterlockedIncrement64) // Interlockedxxx64 that do not have intrinsics are only supported on Windows Server 2003 diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index f5c5429a77c19..8d30df4c74bed 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -8,14 +8,11 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-Wno-error) endif() -add_compile_options($<$:-W4>) - function(create_standalone_jit) set(oneValueArgs TARGET OS ARCH) - set(multiValueArgs ADDITIONAL_DESTINATIONS) - set(options) - cmake_parse_arguments(TARGETDETAILS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(multiValueArgs DESTINATIONS) + cmake_parse_arguments(TARGETDETAILS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(TARGETDETAILS_OS STREQUAL "unix_osx") if (NOT (TARGETDETAILS_ARCH STREQUAL "arm64")) @@ -38,10 +35,10 @@ function(create_standalone_jit) clr_unknown_arch() endif() - if (TARGETDETAILS_ADDITIONAL_DESTINATIONS STREQUAL "") + if (TARGETDETAILS_DESTINATIONS STREQUAL "") add_jit(${TARGETDETAILS_TARGET}) else() - add_jit(${TARGETDETAILS_TARGET} ADDITIONAL_DESTINATIONS "${TARGETDETAILS_ADDITIONAL_DESTINATIONS}") + add_jit(${TARGETDETAILS_TARGET} DESTINATIONS "${TARGETDETAILS_DESTINATIONS}") endif() set_target_definitions_to_custom_os_and_arch(${ARGN}) @@ -422,7 +419,7 @@ else() endif(CLR_CMAKE_HOST_UNIX) # Shared function for generating JIT -# optional arguments: ADDITIONAL_DESTINATIONS path +# optional arguments: DESTINATIONS path function(add_jit jitName) set_source_files_properties(${JIT_EXPORTS_FILE} PROPERTIES GENERATED TRUE) @@ -447,6 +444,7 @@ function(add_jit jitName) set_property(TARGET ${jitName} APPEND_STRING PROPERTY LINK_FLAGS ${JIT_EXPORTS_LINKER_OPTION}) set_property(TARGET ${jitName} APPEND_STRING PROPERTY LINK_DEPENDS ${JIT_EXPORTS_FILE}) + set_target_properties(${jitName} PROPERTIES MSVC_WARNING_LEVEL 4) target_link_libraries(${jitName} ${JIT_LINK_LIBRARIES} @@ -454,7 +452,7 @@ function(add_jit jitName) ) # add the install targets - install_clr(TARGETS ${jitName} ${ARGN}) + install_clr(TARGETS ${jitName} ${ARGN} COMPONENT alljits) endfunction() set(JIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) @@ -474,35 +472,37 @@ else() set(TARGET_OS_NAME win) endif() -create_standalone_jit(TARGET clrjit OS ${TARGET_OS_NAME} ARCH ${ARCH_TARGET_NAME} ADDITIONAL_DESTINATIONS sharedFramework) +create_standalone_jit(TARGET clrjit OS ${TARGET_OS_NAME} ARCH ${ARCH_TARGET_NAME} DESTINATIONS . sharedFramework) +install_clr(TARGETS clrjit DESTINATIONS . sharedFramework COMPONENT jit) # Enable profile guided optimization add_pgo(clrjit) -if (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) - if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) - create_standalone_jit(TARGET clrjit_unix_arm64_${ARCH_HOST_NAME} OS unix ARCH arm64) - create_standalone_jit(TARGET clrjit_unix_osx_arm64_${ARCH_HOST_NAME} OS unix_osx ARCH arm64) - create_standalone_jit(TARGET clrjit_unix_x64_${ARCH_HOST_NAME} OS unix ARCH x64) - create_standalone_jit(TARGET clrjit_win_arm64_${ARCH_HOST_NAME} OS win ARCH arm64) - create_standalone_jit(TARGET clrjit_win_x64_${ARCH_HOST_NAME} OS win ARCH x64) - endif (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) - - create_standalone_jit(TARGET clrjit_unix_armel_${ARCH_HOST_NAME} OS unix ARCH armel) - create_standalone_jit(TARGET clrjit_unix_arm_${ARCH_HOST_NAME} OS unix ARCH arm) - create_standalone_jit(TARGET clrjit_win_arm_${ARCH_HOST_NAME} OS win ARCH arm) - create_standalone_jit(TARGET clrjit_win_x86_${ARCH_HOST_NAME} OS win ARCH x86) -else() - if (CLR_CMAKE_TARGET_UNIX) - create_standalone_jit(TARGET clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} OS unix ARCH ${ARCH_TARGET_NAME}) - endif(CLR_CMAKE_TARGET_UNIX) -endif (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) - +if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) + create_standalone_jit(TARGET clrjit_unix_arm64_${ARCH_HOST_NAME} OS unix ARCH arm64 DESTINATIONS .) + create_standalone_jit(TARGET clrjit_unix_osx_arm64_${ARCH_HOST_NAME} OS unix_osx ARCH arm64 DESTINATIONS .) + create_standalone_jit(TARGET clrjit_unix_x64_${ARCH_HOST_NAME} OS unix ARCH x64 DESTINATIONS .) + create_standalone_jit(TARGET clrjit_win_arm64_${ARCH_HOST_NAME} OS win ARCH arm64 DESTINATIONS .) + create_standalone_jit(TARGET clrjit_win_x64_${ARCH_HOST_NAME} OS win ARCH x64 DESTINATIONS .) +endif (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) + +create_standalone_jit(TARGET clrjit_unix_armel_${ARCH_HOST_NAME} OS unix ARCH armel DESTINATIONS .) +create_standalone_jit(TARGET clrjit_unix_arm_${ARCH_HOST_NAME} OS unix ARCH arm DESTINATIONS .) +target_compile_definitions(clrjit_unix_arm_${ARCH_HOST_NAME} PRIVATE ARM_SOFTFP CONFIGURABLE_ARM_ABI) +create_standalone_jit(TARGET clrjit_win_arm_${ARCH_HOST_NAME} OS win ARCH arm DESTINATIONS .) +create_standalone_jit(TARGET clrjit_win_x86_${ARCH_HOST_NAME} OS win ARCH x86 DESTINATIONS .) + +if (CLR_CMAKE_TARGET_UNIX) + install_clr(TARGETS clrjit_unix_${ARCH_TARGET_NAME}_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT jit) + if (ARCH_TARGET_NAME STREQUAL arm) + target_compile_definitions(clrjit_unix_arm_${ARCH_HOST_NAME} PRIVATE ARM_SOFTFP CONFIGURABLE_ARM_ABI) + endif (ARCH_TARGET_NAME STREQUAL arm) +endif() if (CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_PGO_INSTRUMENT) # Copy PGO dependency to target dir set(PGORT_DLL "pgort140.dll") find_path(PGORT_DIR ${PGORT_DLL} REQUIRED) - _install(FILES "${PGORT_DIR}/${PGORT_DLL}" DESTINATION ${CMAKE_INSTALL_PREFIX}) - _install(FILES "${PGORT_DIR}/${PGORT_DLL}" DESTINATION ${CMAKE_INSTALL_PREFIX}/sharedFramework) + install(FILES "${PGORT_DIR}/${PGORT_DLL}" DESTINATION ${CMAKE_INSTALL_PREFIX}) + install(FILES "${PGORT_DIR}/${PGORT_DLL}" DESTINATION ${CMAKE_INSTALL_PREFIX}/sharedFramework) endif () diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index fd4eec7118ce4..7dd138082fc56 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -3679,7 +3679,7 @@ GenTree* Compiler::optAssertionProp_Comma(ASSERT_VALARG_TP assertions, GenTree* if ((tree->gtGetOp1()->OperGet() == GT_ARR_BOUNDS_CHECK) && ((tree->gtGetOp1()->gtFlags & GTF_ARR_BOUND_INBND) != 0)) { - optRemoveRangeCheck(tree, stmt); + optRemoveCommaBasedRangeCheck(tree, stmt); return optAssertionProp_Update(tree, tree, stmt); } return nullptr; @@ -4005,10 +4005,9 @@ GenTree* Compiler::optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCal /***************************************************************************** * - * Given a tree consisting of a comma node with a bounds check, remove any - * redundant bounds check that has already been checked in the program flow. + * Given a tree with a bounds check, remove it if it has already been checked in the program flow. */ -GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree* tree) +GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt) { if (optLocalAssertionProp) { @@ -4126,12 +4125,23 @@ GenTree* Compiler::optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree gtDispTree(tree, nullptr, nullptr, true); } #endif + if (arrBndsChk == stmt->GetRootNode()) + { + // We have a top-level bounds check node. + // This can happen when trees are broken up due to inlining. + // optRemoveStandaloneRangeCheck will return the modified tree (side effects or a no-op). + GenTree* newTree = optRemoveStandaloneRangeCheck(arrBndsChk, stmt); + + return optAssertionProp_Update(newTree, arrBndsChk, stmt); + } // Defer actually removing the tree until processing reaches its parent comma, since - // optRemoveRangeCheck needs to rewrite the whole comma tree. + // optRemoveCommaBasedRangeCheck needs to rewrite the whole comma tree. arrBndsChk->gtFlags |= GTF_ARR_BOUND_INBND; + return nullptr; } + return nullptr; } @@ -4224,7 +4234,7 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree, return optAssertionProp_Ind(assertions, tree, stmt); case GT_ARR_BOUNDS_CHECK: - return optAssertionProp_BndsChk(assertions, tree); + return optAssertionProp_BndsChk(assertions, tree, stmt); case GT_COMMA: return optAssertionProp_Comma(assertions, tree, stmt); diff --git a/src/coreclr/jit/bitsetasshortlong.h b/src/coreclr/jit/bitsetasshortlong.h index 078cdc810e9de..0eda55e1e1058 100644 --- a/src/coreclr/jit/bitsetasshortlong.h +++ b/src/coreclr/jit/bitsetasshortlong.h @@ -910,17 +910,18 @@ const char* BitSetOps> 16; - bits = bits >> 16; } } return res; diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index f88626a581543..96d593d8b695f 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -993,21 +993,6 @@ Statement* BasicBlock::FirstNonPhiDefOrCatchArgAsg() return stmt; } -/***************************************************************************** - * - * Mark a block as rarely run, we also don't want to have a loop in a - * rarely run block, and we set it's weight to zero. - */ - -void BasicBlock::bbSetRunRarely() -{ - setBBWeight(BB_ZERO_WEIGHT); - if (bbWeight == BB_ZERO_WEIGHT) - { - bbFlags |= BBF_RUN_RARELY; // This block is never/rarely run - } -} - /***************************************************************************** * * Can a BasicBlock be inserted after this without altering the flowgraph diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 863b253528f47..aa15fa4f311df 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -44,6 +44,9 @@ typedef BitVec_ValRet_T ASSERT_VALRET_TP; // This define is used with string concatenation to put this in printf format strings (Note that %u means unsigned int) #define FMT_BB "BB%02u" +// Use this format for loop table indices. +#define FMT_LP "L%02u" + // And this format for profile weights #define FMT_WT "%.7g" @@ -545,17 +548,6 @@ struct BasicBlock : private LIR::Range return ((this->bbFlags & BBF_PROF_WEIGHT) != 0); } - // setBBWeight -- if the block weight is not derived from a profile, - // then set the weight to the input weight, making sure to not overflow BB_MAX_WEIGHT - // Note to set the weight from profile data, instead use setBBProfileWeight - void setBBWeight(weight_t weight) - { - if (!hasProfileWeight()) - { - this->bbWeight = min(weight, BB_MAX_WEIGHT); - } - } - // setBBProfileWeight -- Set the profile-derived weight for a basic block // and update the run rarely flag as appropriate. void setBBProfileWeight(weight_t weight) @@ -573,16 +565,6 @@ struct BasicBlock : private LIR::Range } } - // modifyBBWeight -- same as setBBWeight, but also make sure that if the block is rarely run, it stays that - // way, and if it's not rarely run then its weight never drops below 1. - void modifyBBWeight(weight_t weight) - { - if (this->bbWeight != BB_ZERO_WEIGHT) - { - setBBWeight(max(weight, 1)); - } - } - // this block will inherit the same weight and relevant bbFlags as bSrc // void inheritWeight(BasicBlock* bSrc) @@ -593,30 +575,40 @@ struct BasicBlock : private LIR::Range // Similar to inheritWeight(), but we're splitting a block (such as creating blocks for qmark removal). // So, specify a percentage (0 to 100) of the weight the block should inherit. // + // Can be invoked as a self-rescale, eg: block->inheritWeightPecentage(block, 50)) + // void inheritWeightPercentage(BasicBlock* bSrc, unsigned percentage) { assert(0 <= percentage && percentage <= 100); - // Check for overflow - if ((bSrc->bbWeight * 100) <= bSrc->bbWeight) + this->bbWeight = (bSrc->bbWeight * percentage) / 100; + + if (bSrc->hasProfileWeight()) { - this->bbWeight = bSrc->bbWeight; + this->bbFlags |= BBF_PROF_WEIGHT; } else { - this->bbWeight = (bSrc->bbWeight * percentage) / 100; + this->bbFlags &= ~BBF_PROF_WEIGHT; } - if (bSrc->hasProfileWeight()) + if (this->bbWeight == BB_ZERO_WEIGHT) { - this->bbFlags |= BBF_PROF_WEIGHT; + this->bbFlags |= BBF_RUN_RARELY; } else { - this->bbFlags &= ~BBF_PROF_WEIGHT; + this->bbFlags &= ~BBF_RUN_RARELY; } + } - if (this->bbWeight == 0) + // Scale a blocks' weight by some factor. + // + void scaleBBWeight(BasicBlock::weight_t scale) + { + this->bbWeight = this->bbWeight * scale; + + if (this->bbWeight == BB_ZERO_WEIGHT) { this->bbFlags |= BBF_RUN_RARELY; } @@ -626,6 +618,13 @@ struct BasicBlock : private LIR::Range } } + // Set block weight to zero, and set run rarely flag. + // + void bbSetRunRarely() + { + this->scaleBBWeight(BB_ZERO_WEIGHT); + } + // makeBlockHot() // This is used to override any profiling data // and force a block to be in the hot region. @@ -1041,7 +1040,6 @@ struct BasicBlock : private LIR::Range unsigned bbStackDepthOnEntry(); void bbSetStack(void* stackBuffer); StackEntry* bbStackOnEntry(); - void bbSetRunRarely(); // "bbNum" is one-based (for unknown reasons); it is sometimes useful to have the corresponding // zero-based number for use as an array index. diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 75f038612b3c2..6f9d5f1c5ed40 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -31,30 +31,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*****************************************************************************/ -const BYTE genTypeSizes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) sz, -#include "typelist.h" -#undef DEF_TP -}; - -const BYTE genTypeAlignments[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) al, -#include "typelist.h" -#undef DEF_TP -}; - -const BYTE genTypeStSzs[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) st, -#include "typelist.h" -#undef DEF_TP -}; - -const BYTE genActualTypes[] = { -#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) jitType, -#include "typelist.h" -#undef DEF_TP -}; - void CodeGenInterface::setFramePointerRequiredEH(bool value) { m_cgFramePointerRequired = value; @@ -2203,13 +2179,19 @@ void CodeGen::genGenerateMachineCode() if (compiler->fgHaveProfileData()) { - printf("; with IBC profile data, edge weights are %s, and fgCalledCount is %.0f\n", + printf("; with PGO: edge weights are %s, and fgCalledCount is " FMT_WT "\n", compiler->fgHaveValidEdgeWeights ? "valid" : "invalid", compiler->fgCalledCount); } - if (compiler->fgProfileData_ILSizeMismatch) + if (compiler->fgPgoFailReason != nullptr) + { + printf("; %s\n", compiler->fgPgoFailReason); + } + + if ((compiler->fgPgoInlineePgo + compiler->fgPgoInlineeNoPgo + compiler->fgPgoInlineeNoPgoSingleBlock) > 0) { - printf("; discarded IBC profile data due to mismatch in ILSize\n"); + printf("; %u inlinees with PGO data; %u single block inlinees; %u inlinees without PGO data\n", + compiler->fgPgoInlineePgo, compiler->fgPgoInlineeNoPgoSingleBlock, compiler->fgPgoInlineeNoPgo); } if (compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_ALT_JIT)) @@ -4004,40 +3986,38 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere if (doingFloat) { -#if defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) - insCopy = ins_Copy(TYP_DOUBLE); - // Compute xtraReg here when we have a float argument - assert(xtraReg == REG_NA); +#ifndef UNIX_AMD64_ABI + if (GlobalJitOptions::compFeatureHfa) +#endif // !UNIX_AMD64_ABI + { + insCopy = ins_Copy(TYP_DOUBLE); + // Compute xtraReg here when we have a float argument + assert(xtraReg == REG_NA); - regMaskTP fpAvailMask; + regMaskTP fpAvailMask; - fpAvailMask = RBM_FLT_CALLEE_TRASH & ~regArgMaskLive; -#if defined(FEATURE_HFA) - fpAvailMask &= RBM_ALLDOUBLE; -#else -#if !defined(UNIX_AMD64_ABI) -#error Error. Wrong architecture. -#endif // !defined(UNIX_AMD64_ABI) -#endif // defined(FEATURE_HFA) + fpAvailMask = RBM_FLT_CALLEE_TRASH & ~regArgMaskLive; + if (GlobalJitOptions::compFeatureHfa) + { + fpAvailMask &= RBM_ALLDOUBLE; + } - if (fpAvailMask == RBM_NONE) - { - fpAvailMask = RBM_ALLFLOAT & ~regArgMaskLive; -#if defined(FEATURE_HFA) - fpAvailMask &= RBM_ALLDOUBLE; -#else -#if !defined(UNIX_AMD64_ABI) -#error Error. Wrong architecture. -#endif // !defined(UNIX_AMD64_ABI) -#endif // defined(FEATURE_HFA) - } + if (fpAvailMask == RBM_NONE) + { + fpAvailMask = RBM_ALLFLOAT & ~regArgMaskLive; + if (GlobalJitOptions::compFeatureHfa) + { + fpAvailMask &= RBM_ALLDOUBLE; + } + } - assert(fpAvailMask != RBM_NONE); + assert(fpAvailMask != RBM_NONE); - // We pick the lowest avail register number - regMaskTP tempMask = genFindLowestBit(fpAvailMask); - xtraReg = genRegNumFromMask(tempMask); -#elif defined(TARGET_X86) + // We pick the lowest avail register number + regMaskTP tempMask = genFindLowestBit(fpAvailMask); + xtraReg = genRegNumFromMask(tempMask); + } +#if defined(TARGET_X86) // This case shouldn't occur on x86 since NYI gets converted to an assert NYI("Homing circular FP registers via xtraReg"); #endif @@ -4684,6 +4664,12 @@ void CodeGen::genCheckUseBlockInit() continue; } + if (varDsc->lvIsTemp && !varDsc->HasGCPtr()) + { + varDsc->lvMustInit = 0; + continue; + } + if (compiler->info.compInitMem || varDsc->HasGCPtr() || varDsc->lvMustInit) { if (varDsc->lvTracked) @@ -4730,8 +4716,7 @@ void CodeGen::genCheckUseBlockInit() unless they are untracked GC type or structs that contain GC pointers */ CLANG_FORMAT_COMMENT_ANCHOR; - if ((!varDsc->lvTracked || (varDsc->lvType == TYP_STRUCT)) && varDsc->lvOnFrame && - (!varDsc->lvIsTemp || varDsc->HasGCPtr())) + if ((!varDsc->lvTracked || (varDsc->lvType == TYP_STRUCT)) && varDsc->lvOnFrame) { varDsc->lvMustInit = true; @@ -9562,20 +9547,26 @@ bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass) bool Compiler::IsHfa(GenTree* tree) { -#ifdef FEATURE_HFA - return IsHfa(gtGetStructHandleIfPresent(tree)); -#else - return false; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(gtGetStructHandleIfPresent(tree)); + } + else + { + return false; + } } var_types Compiler::GetHfaType(GenTree* tree) { -#ifdef FEATURE_HFA - return GetHfaType(gtGetStructHandleIfPresent(tree)); -#else - return TYP_UNDEF; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return GetHfaType(gtGetStructHandleIfPresent(tree)); + } + else + { + return TYP_UNDEF; + } } unsigned Compiler::GetHfaCount(GenTree* tree) @@ -9585,18 +9576,19 @@ unsigned Compiler::GetHfaCount(GenTree* tree) var_types Compiler::GetHfaType(CORINFO_CLASS_HANDLE hClass) { -#ifdef FEATURE_HFA - if (hClass != NO_CLASS_HANDLE) + if (GlobalJitOptions::compFeatureHfa) { - CorInfoHFAElemType elemKind = info.compCompHnd->getHFAType(hClass); - if (elemKind != CORINFO_HFA_ELEM_NONE) + if (hClass != NO_CLASS_HANDLE) { - // This type may not appear elsewhere, but it will occupy a floating point register. - compFloatingPointUsed = true; + CorInfoHFAElemType elemKind = info.compCompHnd->getHFAType(hClass); + if (elemKind != CORINFO_HFA_ELEM_NONE) + { + // This type may not appear elsewhere, but it will occupy a floating point register. + compFloatingPointUsed = true; + } + return HfaTypeFromElemKind(elemKind); } - return HfaTypeFromElemKind(elemKind); } -#endif // FEATURE_HFA return TYP_UNDEF; } @@ -11294,7 +11286,6 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) // genConsumeReg will return the valid register, either from the COPY // or from the original source. assert(reg != REG_NA); - regNumber varReg = REG_NA; if (isMultiRegVar) { // Each field is passed in its own register, use the field types. @@ -11404,7 +11395,6 @@ void CodeGen::genRegCopy(GenTree* treeNode) // // There should never be any circular dependencies, and we will check that here. - GenTreeCopyOrReload* copyNode = treeNode->AsCopyOrReload(); // GenTreeCopyOrReload only reports the highest index that has a valid register. // However, we need to ensure that we consume all the registers of the child node, // so we use its regCount. @@ -11530,7 +11520,6 @@ regNumber CodeGen::genRegCopy(GenTree* treeNode, unsigned multiRegIndex) if (targetReg != REG_NA) { // We shouldn't specify a no-op move. - regMaskTP targetRegMask = genRegMask(targetReg); assert(sourceReg != targetReg); var_types type; if (op1->IsMultiRegLclVar()) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 4260548d9ab14..2744bfdfd1603 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -346,6 +346,9 @@ void CodeGen::genCodeForBBlist() if ((block->bbPrev != nullptr) && (block->bbPrev->bbJumpKind == BBJ_COND) && (block->bbWeight != block->bbPrev->bbWeight)) { + JITDUMP("Adding label due to BB weight difference: BBJ_COND " FMT_BB " with weight " FMT_WT + " different from " FMT_BB " with weight " FMT_WT "\n", + block->bbPrev->bbNum, block->bbPrev->bbWeight, block->bbNum, block->bbWeight); needLabel = true; } @@ -588,7 +591,7 @@ void CodeGen::genCodeForBBlist() { if (!foundMismatchedRegVar) { - JITDUMP("Mismatched live reg vars after BB%02u:", block->bbNum); + JITDUMP("Mismatched live reg vars after " FMT_BB ":", block->bbNum); foundMismatchedRegVar = true; } JITDUMP(" V%02u", compiler->lvaTrackedIndexToLclNum(mismatchLiveVarIndex)); @@ -1401,8 +1404,6 @@ regNumber CodeGen::genConsumeReg(GenTree* tree, unsigned multiRegIndex) unsigned fieldVarNum = varDsc->lvFieldLclStart + multiRegIndex; LclVarDsc* fldVarDsc = compiler->lvaGetDesc(fieldVarNum); assert(fldVarDsc->lvLRACandidate); - bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - bool isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr; bool isFieldDying = lcl->IsLastUse(multiRegIndex); if (fldVarDsc->GetRegNum() == REG_STK) @@ -1507,8 +1508,6 @@ regNumber CodeGen::genConsumeReg(GenTree* tree) { reg = lcl->AsLclVar()->GetRegNumByIdx(i); } - bool isInReg = fldVarDsc->lvIsInReg() && reg != REG_NA; - bool isInMemory = !isInReg || fldVarDsc->lvLiveInOutOfHndlr; bool isFieldDying = lcl->IsLastUse(i); if (fldVarDsc->GetRegNum() == REG_STK) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index f1f08a68e855e..756a2811a12e4 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -193,7 +193,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) // we are generating GS cookie check after a GT_RETURN block. // Note: On Amd64 System V RDX is an arg register - REG_ARG_2 - as well // as return register for two-register-returned structs. - if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvRegister && + if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvIsInReg() && (compiler->lvaTable[compiler->info.compThisArg].GetRegNum() == REG_ARG_0)) { regGSCheck = REG_ARG_1; @@ -4230,7 +4230,6 @@ void CodeGen::genCodeForShiftLong(GenTree* tree) void CodeGen::genCodeForShiftRMW(GenTreeStoreInd* storeInd) { GenTree* data = storeInd->Data(); - GenTree* addr = storeInd->Addr(); assert(data->OperIsShift() || data->OperIsRotate()); @@ -4265,7 +4264,6 @@ void CodeGen::genCodeForShiftRMW(GenTreeStoreInd* storeInd) // We must have the number of bits to shift stored in ECX, since we constrained this node to // sit in ECX. In case this didn't happen, LSRA expects the code generator to move it since it's a single // register destination requirement. - regNumber shiftReg = shiftBy->GetRegNum(); genCopyRegIfNeeded(shiftBy, REG_RCX); // The shiftBy operand is implicit, so call the unary version of emitInsRMW. diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7a8fb916abfc1..0219f104b47f2 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -54,6 +54,12 @@ bool Compiler::s_pJitFunctionFileInitialized = false; MethodSet* Compiler::s_pJitMethodSet = nullptr; #endif // DEBUG +#ifdef CONFIGURABLE_ARM_ABI +// static +bool GlobalJitOptions::compFeatureHfa = false; +LONG GlobalJitOptions::compUseSoftFPConfigured = 0; +#endif // CONFIGURABLE_ARM_ABI + /***************************************************************************** * * Little helpers to grab the current cycle counter value; this is done @@ -107,6 +113,30 @@ inline bool _our_GetThreadCycles(unsigned __int64* cycleOut) #endif // which host OS +const BYTE genTypeSizes[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) sz, +#include "typelist.h" +#undef DEF_TP +}; + +const BYTE genTypeAlignments[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) al, +#include "typelist.h" +#undef DEF_TP +}; + +const BYTE genTypeStSzs[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) st, +#include "typelist.h" +#undef DEF_TP +}; + +const BYTE genActualTypes[] = { +#define DEF_TP(tn, nm, jitType, verType, sz, sze, asze, st, al, tf, howUsed) jitType, +#include "typelist.h" +#undef DEF_TP +}; + #endif // FEATURE_JIT_METHOD_PERF /*****************************************************************************/ inline unsigned getCurTime() @@ -468,49 +498,6 @@ var_types Compiler::getJitGCType(BYTE gcType) return result; } -#ifdef ARM_SOFTFP -//--------------------------------------------------------------------------- -// IsSingleFloat32Struct: -// Check if the given struct type contains only one float32 value type -// -// Arguments: -// clsHnd - the handle for the struct type -// -// Return Value: -// true if the given struct type contains only one float32 value type, -// false otherwise. -// - -bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd) -{ - for (;;) - { - // all of class chain must be of value type and must have only one field - if (!info.compCompHnd->isValueClass(clsHnd) || info.compCompHnd->getClassNumInstanceFields(clsHnd) != 1) - { - return false; - } - - CORINFO_CLASS_HANDLE* pClsHnd = &clsHnd; - CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0); - CorInfoType fieldType = info.compCompHnd->getFieldType(fldHnd, pClsHnd); - - switch (fieldType) - { - case CORINFO_TYPE_VALUECLASS: - clsHnd = *pClsHnd; - break; - - case CORINFO_TYPE_FLOAT: - return true; - - default: - return false; - } - } -} -#endif // ARM_SOFTFP - #ifdef TARGET_X86 //--------------------------------------------------------------------------- // isTrivialPointerSizedStruct: @@ -623,53 +610,44 @@ var_types Compiler::getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS var_types useType = TYP_UNKNOWN; -// Start by determining if we have an HFA/HVA with a single element. -#ifdef FEATURE_HFA + // Start by determining if we have an HFA/HVA with a single element. + if (GlobalJitOptions::compFeatureHfa) + { #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - // Arm64 Windows VarArg methods arguments will not classify HFA types, they will need to be treated - // as if they are not HFA types. - if (!isVarArg) + // Arm64 Windows VarArg methods arguments will not classify HFA types, they will need to be treated + // as if they are not HFA types. + if (!isVarArg) #endif // defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - { - switch (structSize) { - case 4: - case 8: + switch (structSize) + { + case 4: + case 8: #ifdef TARGET_ARM64 - case 16: + case 16: #endif // TARGET_ARM64 - { - var_types hfaType; -#ifdef ARM_SOFTFP - // For ARM_SOFTFP, HFA is unsupported so we need to check in another way. - // This matters only for size-4 struct because bigger structs would be processed with RetBuf. - if (isSingleFloat32Struct(clsHnd)) - { - hfaType = TYP_FLOAT; - } -#else // !ARM_SOFTFP - hfaType = GetHfaType(clsHnd); -#endif // ARM_SOFTFP - // We're only interested in the case where the struct size is equal to the size of the hfaType. - if (varTypeIsValidHfaType(hfaType)) { - if (genTypeSize(hfaType) == structSize) - { - useType = hfaType; - } - else + var_types hfaType = GetHfaType(clsHnd); + // We're only interested in the case where the struct size is equal to the size of the hfaType. + if (varTypeIsValidHfaType(hfaType)) { - return TYP_UNKNOWN; + if (genTypeSize(hfaType) == structSize) + { + useType = hfaType; + } + else + { + return TYP_UNKNOWN; + } } } } - } - if (useType != TYP_UNKNOWN) - { - return useType; + if (useType != TYP_UNKNOWN) + { + return useType; + } } } -#endif // FEATURE_HFA // Now deal with non-HFA/HVA structs. switch (structSize) @@ -1513,6 +1491,8 @@ void Compiler::compShutdown() fclose(jitTimeLogFile); } } + + JitTimer::Shutdown(); #endif // FEATURE_JIT_METHOD_PERF #if COUNT_AST_OPERS @@ -2656,7 +2636,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags) opts.compJitAlignLoopAdaptive = true; opts.compJitAlignLoopBoundary = DEFAULT_ALIGN_LOOP_BOUNDARY; opts.compJitAlignLoopMinBlockWeight = DEFAULT_ALIGN_LOOP_MIN_BLOCK_WEIGHT; + opts.compJitAlignLoopMaxCodeSize = DEFAULT_MAX_LOOPSIZE_FOR_ALIGN; #endif + if (opts.compJitAlignLoopAdaptive) { opts.compJitAlignPaddingLimit = (opts.compJitAlignLoopBoundary >> 1) - 1; @@ -2808,8 +2790,6 @@ void Compiler::compInitOptions(JitFlags* jitFlags) if (!altJitConfig || opts.altJit) { - LPCWSTR dumpIRFormat = nullptr; - // We should only enable 'verboseDump' when we are actually compiling a matching method // and not enable it when we are just considering inlining a matching method. // @@ -2876,11 +2856,12 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // Profile data // - fgPgoSchema = nullptr; - fgPgoData = nullptr; - fgPgoSchemaCount = 0; - fgPgoQueryResult = E_FAIL; - fgProfileData_ILSizeMismatch = false; + fgPgoSchema = nullptr; + fgPgoData = nullptr; + fgPgoSchemaCount = 0; + fgPgoQueryResult = E_FAIL; + fgPgoFailReason = nullptr; + if (jitFlags->IsSet(JitFlags::JIT_FLAG_BBOPT)) { fgPgoQueryResult = info.compCompHnd->getPgoInstrumentationResults(info.compMethodHnd, &fgPgoSchema, @@ -2892,13 +2873,44 @@ void Compiler::compInitOptions(JitFlags* jitFlags) // // We will discard the IBC data in this case // - if (FAILED(fgPgoQueryResult) && (fgPgoSchema != nullptr)) + if (FAILED(fgPgoQueryResult)) + { + fgPgoFailReason = (fgPgoSchema != nullptr) ? "No matching PGO data" : "No PGO data"; + fgPgoData = nullptr; + fgPgoSchema = nullptr; + } + // Optionally, disable use of profile data. + // + else if (JitConfig.JitDisablePgo() > 0) { - fgProfileData_ILSizeMismatch = true; - fgPgoData = nullptr; - fgPgoSchema = nullptr; + fgPgoFailReason = "PGO data available, but JitDisablePgo > 0"; + fgPgoQueryResult = E_FAIL; + fgPgoData = nullptr; + fgPgoSchema = nullptr; + fgPgoDisabled = true; } #ifdef DEBUG + // Optionally, enable use of profile data for only some methods. + // + else + { + static ConfigMethodRange JitEnablePgoRange; + JitEnablePgoRange.EnsureInit(JitConfig.JitEnablePgoRange()); + + // Base this decision on the root method hash, so a method either sees all available + // profile data (including that for inlinees), or none of it. + // + const unsigned hash = impInlineRoot()->info.compMethodHash(); + if (!JitEnablePgoRange.Contains(hash)) + { + fgPgoFailReason = "PGO data available, but method hash NOT within JitEnablePgoRange"; + fgPgoQueryResult = E_FAIL; + fgPgoData = nullptr; + fgPgoSchema = nullptr; + fgPgoDisabled = true; + } + } + // A successful result implies a non-NULL fgPgoSchema // if (SUCCEEDED(fgPgoQueryResult)) @@ -3273,6 +3285,27 @@ void Compiler::compInitOptions(JitFlags* jitFlags) } #endif // FEATURE_FASTTAILCALL +#ifdef CONFIGURABLE_ARM_ABI + opts.compUseSoftFP = jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI); + unsigned int softFPConfig = opts.compUseSoftFP ? 2 : 1; + unsigned int oldSoftFPConfig = + InterlockedCompareExchange(&GlobalJitOptions::compUseSoftFPConfigured, softFPConfig, 0); + if (oldSoftFPConfig != softFPConfig && oldSoftFPConfig != 0) + { + // There are no current scenarios where the abi can change during the lifetime of a process + // that uses the JIT. If such a change occurs, either compFeatureHfa will need to change to a TLS static + // or we will need to have some means to reset the flag safely. + NO_WAY("SoftFP ABI setting changed during lifetime of process"); + } + + GlobalJitOptions::compFeatureHfa = !opts.compUseSoftFP; +#elif defined(ARM_SOFTFP) && defined(TARGET_ARM) + // Armel is unconditionally enabled in the JIT. Verify that the VM side agrees. + assert(jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI)); +#elif defined(TARGET_ARM) + assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI)); +#endif // CONFIGURABLE_ARM_ABI + opts.compScopeInfo = opts.compDbgInfo; #ifdef LATE_DISASM @@ -3389,9 +3422,9 @@ void Compiler::compInitOptions(JitFlags* jitFlags) printf("OPTIONS: optimized using profile data\n"); } - if (fgProfileData_ILSizeMismatch) + if (fgPgoFailReason != nullptr) { - printf("OPTIONS: discarded IBC profile data due to mismatch in ILSize\n"); + printf("OPTIONS: %s\n", fgPgoFailReason); } if (jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)) @@ -3594,6 +3627,31 @@ bool Compiler::compStressCompileHelper(compStressArea stressArea, unsigned weigh return (hash < weight); } +//------------------------------------------------------------------------ +// compPromoteFewerStructs: helper to determine if the local +// should not be promoted under a stress mode. +// +// Arguments: +// lclNum - local number to test +// +// Returns: +// true if this local should not be promoted. +// +// Notes: +// Reject ~50% of the potential promotions if STRESS_PROMOTE_FEWER_STRUCTS is active. +// +bool Compiler::compPromoteFewerStructs(unsigned lclNum) +{ + bool rejectThisPromo = false; + const bool promoteLess = compStressCompile(STRESS_PROMOTE_FEWER_STRUCTS, 50); + if (promoteLess) + { + + rejectThisPromo = (((info.compMethodHash() ^ lclNum) & 1) == 0); + } + return rejectThisPromo; +} + #endif // DEBUG void Compiler::compInitDebuggingInfo() @@ -4857,7 +4915,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl DoPhase(this, PHASE_FIND_LOOPS, &Compiler::optFindLoops); // Clone loops with optimization opportunities, and - // choose the one based on dynamic condition evaluation. + // choose one based on dynamic condition evaluation. // DoPhase(this, PHASE_CLONE_LOOPS, &Compiler::optCloneLoops); @@ -5600,18 +5658,6 @@ int Compiler::compCompile(CORINFO_MODULE_HANDLE classPtr, info.compProfilerCallback = false; // Assume false until we are told to hook this method. -#if defined(DEBUG) || defined(LATE_DISASM) - const char* classNamePtr; - - info.compMethodName = eeGetMethodName(info.compMethodHnd, &classNamePtr); - unsigned len = (unsigned)roundUp(strlen(classNamePtr) + 1); - info.compClassName = getAllocator(CMK_DebugOnly).allocate(len); - strcpy_s((char*)info.compClassName, len, classNamePtr); - - info.compFullName = eeGetMethodFullName(info.compMethodHnd); - info.compPerfScore = 0.0; -#endif // defined(DEBUG) || defined(LATE_DISASM) - #ifdef DEBUG if (!compIsForInlining()) { @@ -7581,7 +7627,7 @@ void Compiler::compDispCallArgStats(FILE* fout) CritSecObject CompTimeSummaryInfo::s_compTimeSummaryLock; CompTimeSummaryInfo CompTimeSummaryInfo::s_compTimeSummary; #if MEASURE_CLRAPI_CALLS -double JitTimer::s_cyclesPerSec = CycleTimer::CyclesPerSecond(); +double JitTimer::s_cyclesPerSec = CachedCyclesPerSecond(); #endif #endif // FEATURE_JIT_METHOD_PERF @@ -7776,7 +7822,7 @@ void CompTimeSummaryInfo::Print(FILE* f) return; } // Otherwise... - double countsPerSec = CycleTimer::CyclesPerSecond(); + double countsPerSec = CachedCyclesPerSecond(); if (countsPerSec == 0.0) { fprintf(f, "Processor does not have a high-frequency timer.\n"); @@ -8170,6 +8216,10 @@ void JitTimer::CLRApiCallLeave(unsigned apix) CritSecObject JitTimer::s_csvLock; +// It's expensive to constantly open and close the file, so open it once and close it +// when the process exits. This should be accessed under the s_csvLock. +FILE* JitTimer::s_csvFile = nullptr; + LPCWSTR Compiler::JitTimeLogCsv() { LPCWSTR jitTimeLogCsv = JitConfig.JitTimeLogCsv(); @@ -8186,40 +8236,50 @@ void JitTimer::PrintCsvHeader() CritSecHolder csvLock(s_csvLock); - FILE* fp = _wfopen(jitTimeLogCsv, W("a")); - if (fp != nullptr) + if (s_csvFile == nullptr) + { + s_csvFile = _wfopen(jitTimeLogCsv, W("a")); + } + if (s_csvFile != nullptr) { // Seek to the end of the file s.t. `ftell` doesn't lie to us on Windows - fseek(fp, 0, SEEK_END); + fseek(s_csvFile, 0, SEEK_END); // Write the header if the file is empty - if (ftell(fp) == 0) - { - fprintf(fp, "\"Method Name\","); - fprintf(fp, "\"Assembly or SPMI Index\","); - fprintf(fp, "\"IL Bytes\","); - fprintf(fp, "\"Basic Blocks\","); - fprintf(fp, "\"Min Opts\","); - fprintf(fp, "\"Loops Cloned\","); - + if (ftell(s_csvFile) == 0) + { + fprintf(s_csvFile, "\"Method Name\","); + fprintf(s_csvFile, "\"Assembly or SPMI Index\","); + fprintf(s_csvFile, "\"IL Bytes\","); + fprintf(s_csvFile, "\"Basic Blocks\","); + fprintf(s_csvFile, "\"Min Opts\","); + fprintf(s_csvFile, "\"Loops\","); + fprintf(s_csvFile, "\"Loops Cloned\","); +#if FEATURE_LOOP_ALIGN +#ifdef DEBUG + fprintf(s_csvFile, "\"Alignment Candidates\","); + fprintf(s_csvFile, "\"Loops Aligned\","); +#endif // DEBUG +#endif // FEATURE_LOOP_ALIGN for (int i = 0; i < PHASE_NUMBER_OF; i++) { - fprintf(fp, "\"%s\",", PhaseNames[i]); + fprintf(s_csvFile, "\"%s\",", PhaseNames[i]); if ((JitConfig.JitMeasureIR() != 0) && PhaseReportsIRSize[i]) { - fprintf(fp, "\"Node Count After %s\",", PhaseNames[i]); + fprintf(s_csvFile, "\"Node Count After %s\",", PhaseNames[i]); } } - InlineStrategy::DumpCsvHeader(fp); + InlineStrategy::DumpCsvHeader(s_csvFile); - fprintf(fp, "\"Executable Code Bytes\","); - fprintf(fp, "\"GC Info Bytes\","); - fprintf(fp, "\"Total Bytes Allocated\","); - fprintf(fp, "\"Total Cycles\","); - fprintf(fp, "\"CPS\"\n"); + fprintf(s_csvFile, "\"Executable Code Bytes\","); + fprintf(s_csvFile, "\"GC Info Bytes\","); + fprintf(s_csvFile, "\"Total Bytes Allocated\","); + fprintf(s_csvFile, "\"Total Cycles\","); + fprintf(s_csvFile, "\"CPS\"\n"); + + fflush(s_csvFile); } - fclose(fp); } } @@ -8233,8 +8293,14 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) return; } - // eeGetMethodFullName uses locks, so don't enter crit sec before this call. - const char* methName = comp->eeGetMethodFullName(comp->info.compMethodHnd); +// eeGetMethodFullName uses locks, so don't enter crit sec before this call. +#if defined(DEBUG) || defined(LATE_DISASM) + // If we already have computed the name because for some reason we're generating the CSV + // for a DEBUG build (presumably not for the time info), just re-use it. + const char* methName = comp->info.compFullName; +#else + const char* methName = comp->eeGetMethodFullName(comp->info.compMethodHnd); +#endif // Try and access the SPMI index to report in the data set. // @@ -8247,22 +8313,33 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) CritSecHolder csvLock(s_csvLock); - FILE* fp = _wfopen(jitTimeLogCsv, W("a")); - fprintf(fp, "\"%s\",", methName); + if (s_csvFile == nullptr) + { + return; + } + + fprintf(s_csvFile, "\"%s\",", methName); if (index != 0) { - fprintf(fp, "%d,", index); + fprintf(s_csvFile, "%d,", index); } else { const char* methodAssemblyName = comp->info.compCompHnd->getAssemblyName( comp->info.compCompHnd->getModuleAssembly(comp->info.compCompHnd->getClassModule(comp->info.compClassHnd))); - fprintf(fp, "\"%s\",", methodAssemblyName); - } - fprintf(fp, "%u,", comp->info.compILCodeSize); - fprintf(fp, "%u,", comp->fgBBcount); - fprintf(fp, "%u,", comp->opts.MinOpts()); - fprintf(fp, "%u,", comp->optLoopsCloned); + fprintf(s_csvFile, "\"%s\",", methodAssemblyName); + } + fprintf(s_csvFile, "%u,", comp->info.compILCodeSize); + fprintf(s_csvFile, "%u,", comp->fgBBcount); + fprintf(s_csvFile, "%u,", comp->opts.MinOpts()); + fprintf(s_csvFile, "%u,", comp->optLoopCount); + fprintf(s_csvFile, "%u,", comp->optLoopsCloned); +#if FEATURE_LOOP_ALIGN +#ifdef DEBUG + fprintf(s_csvFile, "%u,", comp->loopAlignCandidates); + fprintf(s_csvFile, "%u,", comp->loopsAligned); +#endif // DEBUG +#endif // FEATURE_LOOP_ALIGN unsigned __int64 totCycles = 0; for (int i = 0; i < PHASE_NUMBER_OF; i++) { @@ -8270,22 +8347,35 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp) { totCycles += m_info.m_cyclesByPhase[i]; } - fprintf(fp, "%I64u,", m_info.m_cyclesByPhase[i]); + fprintf(s_csvFile, "%I64u,", m_info.m_cyclesByPhase[i]); if ((JitConfig.JitMeasureIR() != 0) && PhaseReportsIRSize[i]) { - fprintf(fp, "%u,", m_info.m_nodeCountAfterPhase[i]); + fprintf(s_csvFile, "%u,", m_info.m_nodeCountAfterPhase[i]); } } - comp->m_inlineStrategy->DumpCsvData(fp); + comp->m_inlineStrategy->DumpCsvData(s_csvFile); + + fprintf(s_csvFile, "%u,", comp->info.compNativeCodeSize); + fprintf(s_csvFile, "%Iu,", comp->compInfoBlkSize); + fprintf(s_csvFile, "%Iu,", comp->compGetArenaAllocator()->getTotalBytesAllocated()); + fprintf(s_csvFile, "%I64u,", m_info.m_totalCycles); + fprintf(s_csvFile, "%f\n", CachedCyclesPerSecond()); + + fflush(s_csvFile); +} - fprintf(fp, "%u,", comp->info.compNativeCodeSize); - fprintf(fp, "%Iu,", comp->compInfoBlkSize); - fprintf(fp, "%Iu,", comp->compGetArenaAllocator()->getTotalBytesAllocated()); - fprintf(fp, "%I64u,", m_info.m_totalCycles); - fprintf(fp, "%f\n", CycleTimer::CyclesPerSecond()); - fclose(fp); +// Perform process shutdown actions. +// +// static +void JitTimer::Shutdown() +{ + CritSecHolder csvLock(s_csvLock); + if (s_csvFile != nullptr) + { + fclose(s_csvFile); + } } // Completes the timing of the current method, and adds it to "sum". @@ -9035,6 +9125,10 @@ void cTreeFlags(Compiler* comp, GenTree* tree) { chars += printf("[IND_INVARIANT]"); } + if (tree->gtFlags & GTF_IND_NONNULL) + { + chars += printf("[IND_NONNULL]"); + } break; case GT_CLS_VAR: diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 64635e15cb296..e3afb451f6995 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -139,7 +139,6 @@ const int BAD_STK_OFFS = 0xBAADF00D; // for LclVarDsc::lvStkOffs //------------------------------------------------------------------------ // HFA info shared by LclVarDsc and fgArgTabEntry //------------------------------------------------------------------------ -#ifdef FEATURE_HFA inline bool IsHfa(CorInfoHFAElemType kind) { return kind != CORINFO_HFA_ELEM_NONE; @@ -186,7 +185,6 @@ inline CorInfoHFAElemType HfaElemKindFromType(var_types type) return CORINFO_HFA_ELEM_NONE; } } -#endif // FEATURE_HFA // The following holds the Local var info (scope information) typedef const char* VarName; // Actual ASCII string @@ -489,9 +487,9 @@ class LclVarDsc unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call -#ifdef FEATURE_HFA +#ifdef FEATURE_HFA_FIELDS_PRESENT CorInfoHFAElemType _lvHfaElemKind : 3; // What kind of an HFA this is (CORINFO_HFA_ELEM_NONE if it is not an HFA). -#endif // FEATURE_HFA +#endif // FEATURE_HFA_FIELDS_PRESENT #ifdef DEBUG // TODO-Cleanup: See the note on lvSize() - this flag is only in use by asserts that are checking for struct @@ -541,6 +539,10 @@ class LclVarDsc unsigned char lvFldOffset; unsigned char lvFldOrdinal; +#ifdef DEBUG + unsigned char lvDisqualifyEHVarReason = 'H'; +#endif + #if FEATURE_MULTIREG_ARGS regNumber lvRegNumForSlot(unsigned slotNum) { @@ -561,22 +563,47 @@ class LclVarDsc } #endif // FEATURE_MULTIREG_ARGS - bool lvIsHfa() const + CorInfoHFAElemType GetLvHfaElemKind() const { -#ifdef FEATURE_HFA - return IsHfa(_lvHfaElemKind); +#ifdef FEATURE_HFA_FIELDS_PRESENT + return _lvHfaElemKind; #else - return false; -#endif + NOWAY_MSG("GetLvHfaElemKind"); + return CORINFO_HFA_ELEM_NONE; +#endif // FEATURE_HFA_FIELDS_PRESENT } - bool lvIsHfaRegArg() const + void SetLvHfaElemKind(CorInfoHFAElemType elemKind) { -#ifdef FEATURE_HFA - return lvIsRegArg && lvIsHfa(); +#ifdef FEATURE_HFA_FIELDS_PRESENT + _lvHfaElemKind = elemKind; #else - return false; -#endif + NOWAY_MSG("SetLvHfaElemKind"); +#endif // FEATURE_HFA_FIELDS_PRESENT + } + + bool lvIsHfa() const + { + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(GetLvHfaElemKind()); + } + else + { + return false; + } + } + + bool lvIsHfaRegArg() const + { + if (GlobalJitOptions::compFeatureHfa) + { + return lvIsRegArg && lvIsHfa(); + } + else + { + return false; + } } //------------------------------------------------------------------------------ @@ -595,7 +622,7 @@ class LclVarDsc slots = lvExactSize / sizeof(float); assert(slots <= 8); #elif defined(TARGET_ARM64) - switch (_lvHfaElemKind) + switch (GetLvHfaElemKind()) { case CORINFO_HFA_ELEM_NONE: assert(!"lvHfaSlots called for non-HFA"); @@ -921,22 +948,26 @@ class LclVarDsc var_types GetHfaType() const { -#ifdef FEATURE_HFA - assert(lvIsHfa()); - return HfaTypeFromElemKind(_lvHfaElemKind); -#else - return TYP_UNDEF; -#endif // FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { + assert(lvIsHfa()); + return HfaTypeFromElemKind(GetLvHfaElemKind()); + } + else + { + return TYP_UNDEF; + } } void SetHfaType(var_types type) { -#ifdef FEATURE_HFA - CorInfoHFAElemType elemKind = HfaElemKindFromType(type); - _lvHfaElemKind = elemKind; - // Ensure we've allocated enough bits. - assert(_lvHfaElemKind == elemKind); -#endif // FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { + CorInfoHFAElemType elemKind = HfaElemKindFromType(type); + SetLvHfaElemKind(elemKind); + // Ensure we've allocated enough bits. + assert(GetLvHfaElemKind() == elemKind); + } } var_types lvaArgType(); @@ -1292,6 +1323,7 @@ class JitTimer CompTimeInfo m_info; // The CompTimeInfo for this compilation. static CritSecObject s_csvLock; // Lock to protect the time log file. + static FILE* s_csvFile; // The time log file handle. void PrintCsvMethodStats(Compiler* comp); private: @@ -1336,6 +1368,8 @@ class JitTimer } return res; } + + static void Shutdown(); }; #endif // FEATURE_JIT_METHOD_PERF @@ -1481,9 +1515,27 @@ struct fgArgTabEntry #ifdef FEATURE_ARG_SPLIT bool _isSplit : 1; // True when this argument is split between the registers and OutArg area #endif // FEATURE_ARG_SPLIT -#ifdef FEATURE_HFA +#ifdef FEATURE_HFA_FIELDS_PRESENT CorInfoHFAElemType _hfaElemKind : 3; // What kind of an HFA this is (CORINFO_HFA_ELEM_NONE if it is not an HFA). #endif + CorInfoHFAElemType GetHfaElemKind() const + { +#ifdef FEATURE_HFA_FIELDS_PRESENT + return _hfaElemKind; +#else + NOWAY_MSG("GetHfaElemKind"); + return CORINFO_HFA_ELEM_NONE; +#endif + } + + void SetHfaElemKind(CorInfoHFAElemType elemKind) + { +#ifdef FEATURE_HFA_FIELDS_PRESENT + _hfaElemKind = elemKind; +#else + NOWAY_MSG("SetHfaElemKind"); +#endif + } bool isLateArg() const { @@ -1557,20 +1609,26 @@ struct fgArgTabEntry bool IsHfaArg() const { -#ifdef FEATURE_HFA - return IsHfa(_hfaElemKind); -#else - return false; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(GetHfaElemKind()); + } + else + { + return false; + } } bool IsHfaRegArg() const { -#ifdef FEATURE_HFA - return IsHfa(_hfaElemKind) && isPassedInRegisters(); -#else - return false; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(GetHfaElemKind()) && isPassedInRegisters(); + } + else + { + return false; + } } unsigned intRegCount() const @@ -1626,57 +1684,61 @@ struct fgArgTabEntry var_types GetHfaType() const { -#ifdef FEATURE_HFA - return HfaTypeFromElemKind(_hfaElemKind); -#else - return TYP_UNDEF; -#endif // FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { + return HfaTypeFromElemKind(GetHfaElemKind()); + } + else + { + return TYP_UNDEF; + } } void SetHfaType(var_types type, unsigned hfaSlots) { -#ifdef FEATURE_HFA - if (type != TYP_UNDEF) + if (GlobalJitOptions::compFeatureHfa) { - // We must already have set the passing mode. - assert(numRegs != 0 || GetStackByteSize() != 0); - // We originally set numRegs according to the size of the struct, but if the size of the - // hfaType is not the same as the pointer size, we need to correct it. - // Note that hfaSlots is the number of registers we will use. For ARM, that is twice - // the number of "double registers". - unsigned numHfaRegs = hfaSlots; -#ifdef TARGET_ARM - if (type == TYP_DOUBLE) + if (type != TYP_UNDEF) { - // Must be an even number of registers. - assert((numRegs & 1) == 0); - numHfaRegs = hfaSlots / 2; - } + // We must already have set the passing mode. + assert(numRegs != 0 || GetStackByteSize() != 0); + // We originally set numRegs according to the size of the struct, but if the size of the + // hfaType is not the same as the pointer size, we need to correct it. + // Note that hfaSlots is the number of registers we will use. For ARM, that is twice + // the number of "double registers". + unsigned numHfaRegs = hfaSlots; +#ifdef TARGET_ARM + if (type == TYP_DOUBLE) + { + // Must be an even number of registers. + assert((numRegs & 1) == 0); + numHfaRegs = hfaSlots / 2; + } #endif // TARGET_ARM - if (!IsHfaArg()) - { - // We haven't previously set this; do so now. - CorInfoHFAElemType elemKind = HfaElemKindFromType(type); - _hfaElemKind = elemKind; - // Ensure we've allocated enough bits. - assert(_hfaElemKind == elemKind); - if (isPassedInRegisters()) + if (!IsHfaArg()) { - numRegs = numHfaRegs; + // We haven't previously set this; do so now. + CorInfoHFAElemType elemKind = HfaElemKindFromType(type); + SetHfaElemKind(elemKind); + // Ensure we've allocated enough bits. + assert(GetHfaElemKind() == elemKind); + if (isPassedInRegisters()) + { + numRegs = numHfaRegs; + } } - } - else - { - // We've already set this; ensure that it's consistent. - if (isPassedInRegisters()) + else { - assert(numRegs == numHfaRegs); + // We've already set this; ensure that it's consistent. + if (isPassedInRegisters()) + { + assert(numRegs == numHfaRegs); + } + assert(type == HfaTypeFromElemKind(GetHfaElemKind())); } - assert(type == HfaTypeFromElemKind(_hfaElemKind)); } } -#endif // FEATURE_HFA } #ifdef TARGET_ARM @@ -1749,33 +1811,34 @@ struct fgArgTabEntry unsigned getSize() const { unsigned size = getSlotCount(); -#ifdef FEATURE_HFA - if (IsHfaRegArg()) + if (GlobalJitOptions::compFeatureHfa) { -#ifdef TARGET_ARM - // We counted the number of regs, but if they are DOUBLE hfa regs we have to double the size. - if (GetHfaType() == TYP_DOUBLE) + if (IsHfaRegArg()) { - assert(!IsSplit()); - size <<= 1; - } +#ifdef TARGET_ARM + // We counted the number of regs, but if they are DOUBLE hfa regs we have to double the size. + if (GetHfaType() == TYP_DOUBLE) + { + assert(!IsSplit()); + size <<= 1; + } #elif defined(TARGET_ARM64) - // We counted the number of regs, but if they are FLOAT hfa regs we have to halve the size, - // or if they are SIMD16 vector hfa regs we have to double the size. - if (GetHfaType() == TYP_FLOAT) - { - // Round up in case of odd HFA count. - size = (size + 1) >> 1; - } + // We counted the number of regs, but if they are FLOAT hfa regs we have to halve the size, + // or if they are SIMD16 vector hfa regs we have to double the size. + if (GetHfaType() == TYP_FLOAT) + { + // Round up in case of odd HFA count. + size = (size + 1) >> 1; + } #ifdef FEATURE_SIMD - else if (GetHfaType() == TYP_SIMD16) - { - size <<= 1; - } + else if (GetHfaType() == TYP_SIMD16) + { + size <<= 1; + } #endif // FEATURE_SIMD #endif // TARGET_ARM64 + } } -#endif // FEATURE_HFA return size; } @@ -2273,10 +2336,6 @@ class Compiler GenTree* impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); -#ifdef ARM_SOFTFP - bool isSingleFloat32Struct(CORINFO_CLASS_HANDLE hClass); -#endif // ARM_SOFTFP - #ifdef TARGET_X86 bool isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) const; #endif // TARGET_X86 @@ -3785,6 +3844,26 @@ class Compiler XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +private: + // For prefixFlags + enum + { + PREFIX_TAILCALL_EXPLICIT = 0x00000001, // call has "tail" IL prefix + PREFIX_TAILCALL_IMPLICIT = + 0x00000010, // call is treated as having "tail" prefix even though there is no "tail" IL prefix + PREFIX_TAILCALL_STRESS = + 0x00000100, // call doesn't "tail" IL prefix but is treated as explicit because of tail call stress + PREFIX_TAILCALL = (PREFIX_TAILCALL_EXPLICIT | PREFIX_TAILCALL_IMPLICIT | PREFIX_TAILCALL_STRESS), + PREFIX_VOLATILE = 0x00001000, + PREFIX_UNALIGNED = 0x00010000, + PREFIX_CONSTRAINED = 0x00100000, + PREFIX_READONLY = 0x01000000 + }; + + static void impValidateMemoryAccessOpcode(const BYTE* codeAddr, const BYTE* codeEndp, bool volatilePrefix); + static OPCODE impGetNonPrefixOpcode(const BYTE* codeAddr, const BYTE* codeEndp); + static bool impOpcodeIsCallOpcode(OPCODE opcode); + public: void impInit(); void impImport(); @@ -5584,7 +5663,8 @@ class Compiler void fgIncorporateEdgeCounts(); public: - bool fgProfileData_ILSizeMismatch; + const char* fgPgoFailReason; + bool fgPgoDisabled; ICorJitInfo::PgoInstrumentationSchema* fgPgoSchema; BYTE* fgPgoData; UINT32 fgPgoSchemaCount; @@ -5593,6 +5673,9 @@ class Compiler UINT32 fgPgoBlockCounts; UINT32 fgPgoEdgeCounts; UINT32 fgPgoClassProfiles; + unsigned fgPgoInlineePgo; + unsigned fgPgoInlineeNoPgo; + unsigned fgPgoInlineeNoPgoSingleBlock; void WalkSpanningTree(SpanningTreeVisitor* visitor); void fgSetProfileWeight(BasicBlock* block, BasicBlock::weight_t weight); @@ -5835,6 +5918,8 @@ class Compiler GenTree* fgMorphSmpOpOptional(GenTreeOp* tree); GenTree* fgMorphConst(GenTree* tree); + bool fgMorphCanUseLclFldForCopy(unsigned lclNum1, unsigned lclNum2); + GenTreeLclVar* fgMorphTryFoldObjAsLclVar(GenTreeObj* obj); GenTree* fgMorphCommutative(GenTreeOp* tree); @@ -6014,7 +6099,9 @@ class Compiler public: void optInit(); - void optRemoveRangeCheck(GenTree* tree, Statement* stmt); + GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt); + GenTree* Compiler::optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt); + void Compiler::optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt); bool optIsRangeCheckRemovable(GenTree* tree); protected: @@ -6134,20 +6221,10 @@ class Compiler PhaseStatus optOptimizeLayout(); // Optimize the BasicBlock layout of the method PhaseStatus optFindLoops(); // Finds loops and records them in the loop table - // Optionally clone loops in the loop table. void optCloneLoops(); - - // Clone loop "loopInd" in the loop table. void optCloneLoop(unsigned loopInd, LoopCloneContext* context); - - // Ensure that loop "loopInd" has a unique head block. (If the existing entry has - // non-loop predecessors other than the head entry, create a new, empty block that goes (only) to the entry, - // and redirects the preds of the entry to this new block.) Sets the weight of the newly created block to - // "ambientWeight". void optEnsureUniqueHead(unsigned loopInd, BasicBlock::weight_t ambientWeight); - void optUnrollLoops(); // Unrolls loops (needs to have cost info) - void optRemoveRedundantZeroInits(); protected: @@ -6261,13 +6338,13 @@ class Compiler /* The following values are set only for iterator loops, i.e. has the flag LPFLG_ITER set */ - GenTree* lpIterTree; // The "i = i const" tree - unsigned lpIterVar(); // iterator variable # - int lpIterConst(); // the constant with which the iterator is incremented - genTreeOps lpIterOper(); // the type of the operation on the iterator (ASG_ADD, ASG_SUB, etc.) - void VERIFY_lpIterTree(); + GenTree* lpIterTree; // The "i = i const" tree + unsigned lpIterVar() const; // iterator variable # + int lpIterConst() const; // the constant with which the iterator is incremented + genTreeOps lpIterOper() const; // the type of the operation on the iterator (ASG_ADD, ASG_SUB, etc.) + void VERIFY_lpIterTree() const; - var_types lpIterOperType(); // For overflow instructions + var_types lpIterOperType() const; // For overflow instructions union { int lpConstInit; // initial constant value of iterator : Valid if LPFLG_CONST_INIT @@ -6275,69 +6352,74 @@ class Compiler // LPFLG_VAR_INIT }; - /* The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var" */ + // The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var" + + GenTree* lpTestTree; // pointer to the node containing the loop test + genTreeOps lpTestOper() const; // the type of the comparison between the iterator and the limit (GT_LE, GT_GE, + // etc.) + void VERIFY_lpTestTree() const; - GenTree* lpTestTree; // pointer to the node containing the loop test - genTreeOps lpTestOper(); // the type of the comparison between the iterator and the limit (GT_LE, GT_GE, etc.) - void VERIFY_lpTestTree(); + bool lpIsReversed() const; // true if the iterator node is the second operand in the loop condition + GenTree* lpIterator() const; // the iterator node in the loop test + GenTree* lpLimit() const; // the limit node in the loop test - bool lpIsReversed(); // true if the iterator node is the second operand in the loop condition - GenTree* lpIterator(); // the iterator node in the loop test - GenTree* lpLimit(); // the limit node in the loop test + // Limit constant value of iterator - loop condition is "i RELOP const" + // : Valid if LPFLG_CONST_LIMIT + int lpConstLimit() const; - int lpConstLimit(); // limit constant value of iterator - loop condition is "i RELOP const" : Valid if - // LPFLG_CONST_LIMIT - unsigned lpVarLimit(); // the lclVar # in the loop condition ( "i RELOP lclVar" ) : Valid if - // LPFLG_VAR_LIMIT - bool lpArrLenLimit(Compiler* comp, ArrIndex* index); // The array length in the loop condition ( "i RELOP - // arr.len" or "i RELOP arr[i][j].len" ) : Valid if - // LPFLG_ARRLEN_LIMIT + // The lclVar # in the loop condition ( "i RELOP lclVar" ) + // : Valid if LPFLG_VAR_LIMIT + unsigned lpVarLimit() const; + + // The array length in the loop condition ( "i RELOP arr.len" or "i RELOP arr[i][j].len" ) + // : Valid if LPFLG_ARRLEN_LIMIT + bool lpArrLenLimit(Compiler* comp, ArrIndex* index) const; // Returns "true" iff "*this" contains the blk. - bool lpContains(BasicBlock* blk) + bool lpContains(BasicBlock* blk) const { return lpFirst->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum; } // Returns "true" iff "*this" (properly) contains the range [first, bottom] (allowing firsts // to be equal, but requiring bottoms to be different.) - bool lpContains(BasicBlock* first, BasicBlock* bottom) + bool lpContains(BasicBlock* first, BasicBlock* bottom) const { return lpFirst->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum; } // Returns "true" iff "*this" (properly) contains "lp2" (allowing firsts to be equal, but requiring // bottoms to be different.) - bool lpContains(const LoopDsc& lp2) + bool lpContains(const LoopDsc& lp2) const { return lpContains(lp2.lpFirst, lp2.lpBottom); } // Returns "true" iff "*this" is (properly) contained by the range [first, bottom] // (allowing firsts to be equal, but requiring bottoms to be different.) - bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) + bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) const { return first->bbNum <= lpFirst->bbNum && lpBottom->bbNum < bottom->bbNum; } // Returns "true" iff "*this" is (properly) contained by "lp2" // (allowing firsts to be equal, but requiring bottoms to be different.) - bool lpContainedBy(const LoopDsc& lp2) + bool lpContainedBy(const LoopDsc& lp2) const { return lpContains(lp2.lpFirst, lp2.lpBottom); } // Returns "true" iff "*this" is disjoint from the range [top, bottom]. - bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) + bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) const { return bottom->bbNum < lpFirst->bbNum || lpBottom->bbNum < first->bbNum; } // Returns "true" iff "*this" is disjoint from "lp2". - bool lpDisjoint(const LoopDsc& lp2) + bool lpDisjoint(const LoopDsc& lp2) const { return lpDisjoint(lp2.lpFirst, lp2.lpBottom); } // Returns "true" iff the loop is well-formed (see code for defn). - bool lpWellFormed() + bool lpWellFormed() const { return lpFirst->bbNum <= lpTop->bbNum && lpTop->bbNum <= lpEntry->bbNum && lpEntry->bbNum <= lpBottom->bbNum && @@ -6353,6 +6435,11 @@ class Compiler LoopDsc* optLoopTable; // loop descriptor table unsigned char optLoopCount; // number of tracked loops +#ifdef DEBUG + unsigned char loopAlignCandidates; // number of loops identified for alignment + unsigned char loopsAligned; // number of loops actually aligned +#endif // DEBUG + bool optRecordLoop(BasicBlock* head, BasicBlock* first, BasicBlock* top, @@ -6377,9 +6464,9 @@ class Compiler BasicBlock* lpBottom, unsigned char lpExitCnt, BasicBlock* lpExit, - unsigned parentLoop = BasicBlock::NOT_IN_LOOP); - void optPrintLoopInfo(unsigned lnum); - void optPrintLoopRecording(unsigned lnum); + unsigned parentLoop = BasicBlock::NOT_IN_LOOP) const; + void optPrintLoopInfo(unsigned lnum) const; + void optPrintLoopRecording(unsigned lnum) const; void optCheckPreds(); #endif @@ -6417,8 +6504,6 @@ class Compiler // iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2". bool optLoopContains(unsigned l1, unsigned l2); - // Requires "loopInd" to be a valid index into the loop table. - // Updates the loop table by changing loop "loopInd", whose head is required // to be "from", to be "to". Also performs this transformation for any // loop nested in "loopInd" that shares the same head as "loopInd". @@ -6430,10 +6515,13 @@ class Compiler // Marks the containsCall information to "lnum" and any parent loops. void AddContainsCallAllContainingLoops(unsigned lnum); + // Adds the variable liveness information from 'blk' to "lnum" and any parent loops. void AddVariableLivenessAllContainingLoops(unsigned lnum, BasicBlock* blk); + // Adds "fldHnd" to the set of modified fields of "lnum" and any parent loops. void AddModifiedFieldAllContainingLoops(unsigned lnum, CORINFO_FIELD_HANDLE fldHnd); + // Adds "elemType" to the set of modified array element types of "lnum" and any parent loops. void AddModifiedElemTypeAllContainingLoops(unsigned lnum, CORINFO_CLASS_HANDLE elemType); @@ -6442,7 +6530,7 @@ class Compiler void optCopyBlkDest(BasicBlock* from, BasicBlock* to); // Returns true if 'block' is an entry block for any loop in 'optLoopTable' - bool optIsLoopEntry(BasicBlock* block); + bool optIsLoopEntry(BasicBlock* block) const; // The depth of the loop described by "lnum" (an index into the loop table.) (0 == top level) unsigned optLoopDepth(unsigned lnum) @@ -7293,7 +7381,7 @@ class Compiler GenTree* optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCall* call, Statement* stmt); GenTree* optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt); GenTree* optAssertionProp_Comma(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt); - GenTree* optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree* tree); + GenTree* optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt); GenTree* optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt); GenTree* optAssertionPropLocal_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt); GenTree* optAssertionProp_Update(GenTree* newTree, GenTree* tree, Statement* stmt); @@ -7338,7 +7426,7 @@ class Compiler void optObtainLoopCloningOpts(LoopCloneContext* context); bool optIsLoopClonable(unsigned loopInd); - bool optCanCloneLoops(); + bool optLoopCloningEnabled(); #ifdef DEBUG void optDebugLogLoopCloning(BasicBlock* block, Statement* insertBefore); @@ -9169,11 +9257,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX int compJitSaveFpLrWithCalleeSavedRegisters; #endif // defined(TARGET_ARM64) +#ifdef CONFIGURABLE_ARM_ABI + bool compUseSoftFP = false; +#else #ifdef ARM_SOFTFP static const bool compUseSoftFP = true; -#else // !ARM_SOFTFP +#else // !ARM_SOFTFP static const bool compUseSoftFP = false; -#endif +#endif // ARM_SOFTFP +#endif // CONFIGURABLE_ARM_ABI } opts; static bool s_pAltJitExcludeAssembliesListInitialized; @@ -9267,6 +9359,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX STRESS_MODE(SWITCH_CMP_BR_EXPANSION) \ STRESS_MODE(GENERIC_VARN) \ STRESS_MODE(PROFILER_CALLBACKS) /* Will generate profiler hooks for ELT callbacks */ \ + STRESS_MODE(BYREF_PROMOTION) /* Change undoPromotion decisions for byrefs */ \ + STRESS_MODE(PROMOTE_FEWER_STRUCTS)/* Don't promote some structs that can be promoted */ \ \ /* After COUNT_VARN, stress level 2 does all of these all the time */ \ \ @@ -9313,6 +9407,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX return compStressCompile(STRESS_RANDOM_INLINE, 50); } + bool compPromoteFewerStructs(unsigned lclNum); + #endif // DEBUG bool compTailCallStress() @@ -9331,7 +9427,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX const char* compGetTieringName(bool wantShortName = false) const; const char* compGetStressMessage() const; - codeOptimize compCodeOpt() + codeOptimize compCodeOpt() const { #if 0 // Switching between size & speed has measurable throughput impact diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 1266978683adf..9afb2b8c5baa3 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -3388,7 +3388,7 @@ inline void Compiler::LoopDsc::AddModifiedElemType(Compiler* comp, CORINFO_CLASS lpArrayElemTypesModified->Set(structHnd, true, ClassHandleSet::Overwrite); } -inline void Compiler::LoopDsc::VERIFY_lpIterTree() +inline void Compiler::LoopDsc::VERIFY_lpIterTree() const { #ifdef DEBUG assert(lpFlags & LPFLG_ITER); @@ -3397,8 +3397,8 @@ inline void Compiler::LoopDsc::VERIFY_lpIterTree() assert(lpIterTree->OperIs(GT_ASG)); - GenTree* lhs = lpIterTree->AsOp()->gtOp1; - GenTree* rhs = lpIterTree->AsOp()->gtOp2; + const GenTree* lhs = lpIterTree->AsOp()->gtOp1; + const GenTree* rhs = lpIterTree->AsOp()->gtOp2; assert(lhs->OperGet() == GT_LCL_VAR); switch (rhs->gtOper) @@ -3420,7 +3420,7 @@ inline void Compiler::LoopDsc::VERIFY_lpIterTree() //----------------------------------------------------------------------------- -inline unsigned Compiler::LoopDsc::lpIterVar() +inline unsigned Compiler::LoopDsc::lpIterVar() const { VERIFY_lpIterTree(); return lpIterTree->AsOp()->gtOp1->AsLclVarCommon()->GetLclNum(); @@ -3428,7 +3428,7 @@ inline unsigned Compiler::LoopDsc::lpIterVar() //----------------------------------------------------------------------------- -inline int Compiler::LoopDsc::lpIterConst() +inline int Compiler::LoopDsc::lpIterConst() const { VERIFY_lpIterTree(); GenTree* rhs = lpIterTree->AsOp()->gtOp2; @@ -3437,14 +3437,14 @@ inline int Compiler::LoopDsc::lpIterConst() //----------------------------------------------------------------------------- -inline genTreeOps Compiler::LoopDsc::lpIterOper() +inline genTreeOps Compiler::LoopDsc::lpIterOper() const { VERIFY_lpIterTree(); GenTree* rhs = lpIterTree->AsOp()->gtOp2; return rhs->OperGet(); } -inline var_types Compiler::LoopDsc::lpIterOperType() +inline var_types Compiler::LoopDsc::lpIterOperType() const { VERIFY_lpIterTree(); @@ -3459,7 +3459,7 @@ inline var_types Compiler::LoopDsc::lpIterOperType() return type; } -inline void Compiler::LoopDsc::VERIFY_lpTestTree() +inline void Compiler::LoopDsc::VERIFY_lpTestTree() const { #ifdef DEBUG assert(lpFlags & LPFLG_ITER); @@ -3505,7 +3505,7 @@ inline void Compiler::LoopDsc::VERIFY_lpTestTree() //----------------------------------------------------------------------------- -inline bool Compiler::LoopDsc::lpIsReversed() +inline bool Compiler::LoopDsc::lpIsReversed() const { VERIFY_lpTestTree(); return ((lpTestTree->AsOp()->gtOp2->gtOper == GT_LCL_VAR) && @@ -3514,7 +3514,7 @@ inline bool Compiler::LoopDsc::lpIsReversed() //----------------------------------------------------------------------------- -inline genTreeOps Compiler::LoopDsc::lpTestOper() +inline genTreeOps Compiler::LoopDsc::lpTestOper() const { VERIFY_lpTestTree(); genTreeOps op = lpTestTree->OperGet(); @@ -3523,7 +3523,7 @@ inline genTreeOps Compiler::LoopDsc::lpTestOper() //----------------------------------------------------------------------------- -inline GenTree* Compiler::LoopDsc::lpIterator() +inline GenTree* Compiler::LoopDsc::lpIterator() const { VERIFY_lpTestTree(); @@ -3532,7 +3532,7 @@ inline GenTree* Compiler::LoopDsc::lpIterator() //----------------------------------------------------------------------------- -inline GenTree* Compiler::LoopDsc::lpLimit() +inline GenTree* Compiler::LoopDsc::lpLimit() const { VERIFY_lpTestTree(); @@ -3541,7 +3541,7 @@ inline GenTree* Compiler::LoopDsc::lpLimit() //----------------------------------------------------------------------------- -inline int Compiler::LoopDsc::lpConstLimit() +inline int Compiler::LoopDsc::lpConstLimit() const { VERIFY_lpTestTree(); assert(lpFlags & LPFLG_CONST_LIMIT); @@ -3553,7 +3553,7 @@ inline int Compiler::LoopDsc::lpConstLimit() //----------------------------------------------------------------------------- -inline unsigned Compiler::LoopDsc::lpVarLimit() +inline unsigned Compiler::LoopDsc::lpVarLimit() const { VERIFY_lpTestTree(); assert(lpFlags & LPFLG_VAR_LIMIT); @@ -3565,7 +3565,7 @@ inline unsigned Compiler::LoopDsc::lpVarLimit() //----------------------------------------------------------------------------- -inline bool Compiler::LoopDsc::lpArrLenLimit(Compiler* comp, ArrIndex* index) +inline bool Compiler::LoopDsc::lpArrLenLimit(Compiler* comp, ArrIndex* index) const { VERIFY_lpTestTree(); assert(lpFlags & LPFLG_ARRLEN_LIMIT); diff --git a/src/coreclr/jit/earlyprop.cpp b/src/coreclr/jit/earlyprop.cpp index adf669fe01486..fbdc6881ea015 100644 --- a/src/coreclr/jit/earlyprop.cpp +++ b/src/coreclr/jit/earlyprop.cpp @@ -388,12 +388,21 @@ GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree, LocalNumberToNullCheck if ((checkConstVal >= 0) && (checkConstVal < actualConstVal)) { GenTree* comma = check->gtGetParent(nullptr); - if ((comma != nullptr) && comma->OperIs(GT_COMMA) && (comma->gtGetOp1() == check)) + + // We should never see cases other than these in the IR, + // as the check node does not produce a value. + assert(((comma != nullptr) && comma->OperIs(GT_COMMA) && + (comma->gtGetOp1() == check || comma->TypeIs(TYP_VOID))) || + (check == compCurStmt->GetRootNode())); + + // Still, we guard here so that release builds do not try to optimize trees we don't understand. + if (((comma != nullptr) && comma->OperIs(GT_COMMA) && (comma->gtGetOp1() == check)) || + (check == compCurStmt->GetRootNode())) { - optRemoveRangeCheck(comma, compCurStmt); // Both `tree` and `check` have been removed from the statement. // 'tree' was replaced with 'nop' or side effect list under 'comma'. - return comma->gtGetOp1(); + // optRemoveRangeCheck returns this modified tree. + return optRemoveRangeCheck(check, comma, compCurStmt); } } } diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index ad044c9dbebc4..74b62dd4b208a 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -3460,6 +3460,10 @@ void emitter::emitDispIGflags(unsigned flags) { printf(", extend"); } + if (flags & IGF_LOOP_ALIGN) + { + printf(", align"); + } } void emitter::emitDispIG(insGroup* ig, insGroup* igPrev, bool verbose) @@ -3747,6 +3751,14 @@ size_t emitter::emitIssue1Instr(insGroup* ig, instrDesc* id, BYTE** dp) } #endif // DEBUG_EMIT + // Add the shrinkage to the ongoing offset adjustment. This needs to happen during the + // processing of an instruction group, and not only at the beginning of an instruction + // group, or else the difference of IG sizes between debug and release builds can cause + // debug/non-debug asm diffs. + int offsShrinkage = estimatedSize - actualSize; + JITDUMP("Increasing size adj %d by %d => %d\n", emitOffsAdj, offsShrinkage, emitOffsAdj + offsShrinkage); + emitOffsAdj += offsShrinkage; + /* The instruction size estimate wasn't accurate; remember this */ ig->igFlags |= IGF_UPD_ISZ; @@ -4619,21 +4631,29 @@ void emitter::emitJumpDistBind() // void emitter::emitLoopAlignment() { + unsigned short paddingBytes; + if ((emitComp->opts.compJitAlignLoopBoundary > 16) && (!emitComp->opts.compJitAlignLoopAdaptive)) { - emitLongLoopAlign(emitComp->opts.compJitAlignLoopBoundary); + paddingBytes = emitComp->opts.compJitAlignLoopBoundary; + emitLongLoopAlign(paddingBytes); } else { - emitLoopAlign(); + paddingBytes = MAX_ENCODED_SIZE; + emitLoopAlign(paddingBytes); } // Mark this IG as need alignment so during emitter we can check the instruction count heuristics of // all IGs that follows this IG and participate in a loop. emitCurIG->igFlags |= IGF_LOOP_ALIGN; - JITDUMP("Adding 'align' instruction of %d bytes in G_M%03u_IG%02u.\n", emitComp->opts.compJitAlignLoopBoundary, - emitComp->compMethodID, emitCurIG->igNum); + JITDUMP("Adding 'align' instruction of %d bytes in G_M%03u_IG%02u.\n", paddingBytes, emitComp->compMethodID, + emitCurIG->igNum); + +#ifdef DEBUG + emitComp->loopAlignCandidates++; +#endif // DEBUG } //----------------------------------------------------------------------------- @@ -4698,7 +4718,7 @@ unsigned emitter::getLoopSize(insGroup* igLoopHeader, unsigned maxLoopSize DEBUG } else { - instrDescAlign *alignInstrToAdj = alignInstr, *prevAlignInstr = nullptr; + instrDescAlign* alignInstrToAdj = alignInstr; for (; alignInstrToAdj != nullptr && alignInstrToAdj->idaIG == alignInstr->idaIG; alignInstrToAdj = alignInstrToAdj->idaNext) { @@ -4788,7 +4808,7 @@ void emitter::emitSetLoopBackEdge(BasicBlock* loopTopBlock) //----------------------------------------------------------------------------- // emitLoopAlignAdjustments: Walk all the align instructions and update them // with actual padding needed. - +// // Notes: // For IGs that have align instructions in the end, calculate the actual offset // of loop start and determine how much padding is needed. Based on that, update @@ -4803,18 +4823,16 @@ void emitter::emitLoopAlignAdjustments() } JITDUMP("*************** In emitLoopAlignAdjustments()\n"); + JITDUMP("compJitAlignLoopAdaptive = %s\n", dspBool(emitComp->opts.compJitAlignLoopAdaptive)); + JITDUMP("compJitAlignLoopBoundary = %u\n", emitComp->opts.compJitAlignLoopBoundary); + JITDUMP("compJitAlignLoopMinBlockWeight = %u\n", emitComp->opts.compJitAlignLoopMinBlockWeight); + JITDUMP("compJitAlignLoopForJcc = %s\n", dspBool(emitComp->opts.compJitAlignLoopForJcc)); + JITDUMP("compJitAlignLoopMaxCodeSize = %u\n", emitComp->opts.compJitAlignLoopMaxCodeSize); + JITDUMP("compJitAlignPaddingLimit = %u\n", emitComp->opts.compJitAlignPaddingLimit); - unsigned short estimatedPaddingNeeded = emitComp->opts.compJitAlignPaddingLimit; - unsigned short alignmentBoundary = emitComp->opts.compJitAlignLoopBoundary; - - if (emitComp->opts.compJitAlignLoopAdaptive) - { - // For adaptive, adjust the loop size depending on the alignment boundary - int maxBlocksAllowedForLoop = genLog2((unsigned)alignmentBoundary) - 1; - } + unsigned estimatedPaddingNeeded = emitComp->opts.compJitAlignPaddingLimit; unsigned alignBytesRemoved = 0; - unsigned loopSize = 0; unsigned loopIGOffset = 0; instrDescAlign* alignInstr = emitAlignList; @@ -4861,8 +4879,8 @@ void emitter::emitLoopAlignAdjustments() unsigned paddingToAdj = actualPaddingNeeded; #ifdef DEBUG - - int instrAdjusted = (alignmentBoundary + (MAX_ENCODED_SIZE - 1)) / MAX_ENCODED_SIZE; + int instrAdjusted = + (emitComp->opts.compJitAlignLoopBoundary + (MAX_ENCODED_SIZE - 1)) / MAX_ENCODED_SIZE; #endif // Adjust the padding amount in all align instructions in this IG instrDescAlign *alignInstrToAdj = alignInstr, *prevAlignInstr = nullptr; @@ -4884,9 +4902,9 @@ void emitter::emitLoopAlignAdjustments() alignInstr = prevAlignInstr; } - JITDUMP("Adjusted alignment of G_M%03u_IG%02u from %02d to %02d.\n", emitComp->compMethodID, alignIG->igNum, + JITDUMP("Adjusted alignment of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum, estimatedPaddingNeeded, actualPaddingNeeded); - JITDUMP("Adjusted size of G_M%03u_IG%02u from %04d to %04d.\n", emitComp->compMethodID, alignIG->igNum, + JITDUMP("Adjusted size of G_M%03u_IG%02u from %u to %u.\n", emitComp->compMethodID, alignIG->igNum, (alignIG->igSize + diff), alignIG->igSize); } @@ -4980,8 +4998,7 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs // No padding if loop is big if (loopSize > maxLoopSize) { - JITDUMP(";; Skip alignment: 'Loop is big. LoopSize= %d, MaxLoopSize= %d.'\n", alignmentBoundary, loopSize, - maxLoopSize); + JITDUMP(";; Skip alignment: 'Loop is big. LoopSize= %d, MaxLoopSize= %d.'\n", loopSize, maxLoopSize); return 0; } @@ -5007,7 +5024,7 @@ unsigned emitter::emitCalculatePaddingForLoopAlignment(insGroup* ig, size_t offs if (nPaddingBytes == 0) { skipPadding = true; - JITDUMP(";; Skip alignment: 'Loop already aligned at 16B boundary.'\n"); + JITDUMP(";; Skip alignment: 'Loop already aligned at %uB boundary.'\n", alignmentBoundary); } // Check if the alignment exceeds new maxPadding limit else if (nPaddingBytes > nMaxPaddingBytes) @@ -5272,6 +5289,8 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, emitCodeBlock = nullptr; emitConsBlock = nullptr; + emitOffsAdj = 0; + /* Tell everyone whether we have fully interruptible code or not */ emitFullyInt = fullyInt; @@ -5732,17 +5751,37 @@ unsigned emitter::emitEndCodeGen(Compiler* comp, /* Record the actual offset of the block, noting the difference */ - emitOffsAdj = ig->igOffs - emitCurCodeOffs(cp); - assert(emitOffsAdj >= 0); + int newOffsAdj = ig->igOffs - emitCurCodeOffs(cp); #if DEBUG_EMIT - if ((emitOffsAdj != 0) && emitComp->verbose) +#ifdef DEBUG + // Under DEBUG, only output under verbose flag. + if (emitComp->verbose) +#endif // DEBUG { - printf("Block predicted offs = %08X, actual = %08X -> size adj = %d\n", ig->igOffs, emitCurCodeOffs(cp), - emitOffsAdj); + if (newOffsAdj != 0) + { + printf("Block predicted offs = %08X, actual = %08X -> size adj = %d\n", ig->igOffs, emitCurCodeOffs(cp), + newOffsAdj); + } + if (emitOffsAdj != newOffsAdj) + { + printf("Block expected size adj %d not equal to actual size adj %d (probably some instruction size was " + "underestimated but not included in the running `emitOffsAdj` count)\n", + emitOffsAdj, newOffsAdj); + } } + // Make it noisy in DEBUG if these don't match. In release, the noway_assert below checks the + // fatal condition. + assert(emitOffsAdj == newOffsAdj); #endif // DEBUG_EMIT + // We can't have over-estimated the adjustment, or we might have underestimated a jump distance. + noway_assert(emitOffsAdj <= newOffsAdj); + + emitOffsAdj = newOffsAdj; + assert(emitOffsAdj >= 0); + ig->igOffs = emitCurCodeOffs(cp); assert(IsCodeAligned(ig->igOffs)); @@ -6555,7 +6594,6 @@ void emitter::emitOutputDataSec(dataSecDsc* sec, BYTE* dst) { JITDUMP(" section %u, size %u, block relative addr\n", secNum++, dscSize); - unsigned elemSize = 4; size_t numElems = dscSize / 4; unsigned* uDst = (unsigned*)dst; insGroup* labFirst = (insGroup*)emitCodeGetCookie(emitComp->fgFirstBB); @@ -7010,10 +7048,11 @@ void emitter::emitRecordGCcall(BYTE* codePos, unsigned char callInstrSize) assert(!emitFullGCinfo); unsigned offs = emitCurCodeOffs(codePos); - unsigned regs = (emitThisGCrefRegs | emitThisByrefRegs) & ~RBM_INTRET; callDsc* call; #ifdef JIT32_GCENCODER + unsigned regs = (emitThisGCrefRegs | emitThisByrefRegs) & ~RBM_INTRET; + // The JIT32 GCInfo encoder allows us to (as the comment previously here said): // "Bail if this is a totally boring call", but the GCInfoEncoder/Decoder interface // requires a definition for every call site, so we skip these "early outs" when we're diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 6353d06f9c5a6..fdf964c9b5c55 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -2709,7 +2709,7 @@ void emitter::emitLongLoopAlign(unsigned short alignmentBoundary) while (insAlignCount) { - emitLoopAlign(); + emitLoopAlign(MAX_ENCODED_SIZE); insAlignCount--; } emitLoopAlign(lastInsAlignSize); @@ -9434,6 +9434,8 @@ BYTE* emitter::emitOutputAlign(insGroup* ig, instrDesc* id, BYTE* dst) { assert(paddingToAdd == paddingNeeded); } + + emitComp->loopsAligned++; #endif return emitOutputNOP(dst, paddingToAdd); @@ -12532,13 +12534,12 @@ BYTE* emitter::emitOutputLJ(insGroup* ig, BYTE* dst, instrDesc* i) if (id->idCodeSize() != JMP_SIZE_SMALL) { - emitOffsAdj += id->idCodeSize() - JMP_SIZE_SMALL; - -#ifdef DEBUG - if (emitComp->verbose) +#if DEBUG_EMIT || defined(DEBUG) + int offsShrinkage = id->idCodeSize() - JMP_SIZE_SMALL; + if (INDEBUG(emitComp->verbose ||)(id->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || + INTERESTING_JUMP_NUM == 0)) { - printf("; NOTE: size of jump [%08X] mis-predicted by %d bytes\n", emitComp->dspPtr(id), - (id->idCodeSize() - JMP_SIZE_SMALL)); + printf("; NOTE: size of jump [%08p] mis-predicted by %d bytes\n", dspPtr(id), offsShrinkage); } #endif } @@ -12699,11 +12700,10 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) { assert(emitIssuing); - BYTE* dst = *dp; - size_t sz = sizeof(instrDesc); - instruction ins = id->idIns(); - unsigned char callInstrSize = 0; - int emitOffsAdjBefore = emitOffsAdj; + BYTE* dst = *dp; + size_t sz = sizeof(instrDesc); + instruction ins = id->idIns(); + unsigned char callInstrSize = 0; #ifdef DEBUG bool dspOffs = emitComp->opts.dspGCtbls; @@ -13882,18 +13882,6 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) #endif dst = emitOutputNOP(dst, diff); - - // since we compensated the over-estimation, revert the offsAdj that - // might have happened in the jump - if (emitOffsAdjBefore != emitOffsAdj) - { -#ifdef DEBUG - insFormat format = id->idInsFmt(); - assert((format == IF_LABEL) || (format == IF_RWR_LABEL) || (format == IF_SWR_LABEL)); - assert(diff == (emitOffsAdj - emitOffsAdjBefore)); -#endif - emitOffsAdj -= diff; - } } assert((id->idCodeSize() - ((UNATIVE_OFFSET)(dst - *dp))) == 0); } @@ -14299,7 +14287,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins result.insThroughput = PERFSCORE_THROUGHPUT_2X; // one or two components result.insLatency = PERFSCORE_LATENCY_1C; - if (id->idInsFmt() == IF_RWR_LABEL) + if (insFmt == IF_RWR_LABEL) { // RIP relative addressing // @@ -14307,7 +14295,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins // result.insThroughput = PERFSCORE_THROUGHPUT_1C; } - else if (id->idInsFmt() != IF_RWR_SRD) + else if (insFmt != IF_RWR_SRD) { if (id->idAddr()->iiaAddrMode.amIndxReg != REG_NA) { @@ -14355,11 +14343,17 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_imul_14: case INS_imul_15: #endif // TARGET_AMD64 + case INS_imul: + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency += PERFSCORE_LATENCY_3C; + break; + case INS_mulEAX: case INS_imulEAX: - case INS_imul: + // uops.info: mul/imul rdx:rax,reg latency is 3 only if the low half of the result is needed, but in that + // case codegen uses imul reg,reg instruction form (except for unsigned overflow checks, which are rare) result.insThroughput = PERFSCORE_THROUGHPUT_1C; - result.insLatency = PERFSCORE_LATENCY_3C; + result.insLatency += PERFSCORE_LATENCY_4C; break; case INS_div: @@ -14373,12 +14367,11 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins { assert(id->idOpSize() == EA_4BYTE); result.insThroughput = PERFSCORE_THROUGHPUT_6C; - result.insThroughput = PERFSCORE_LATENCY_26C; + result.insLatency = PERFSCORE_LATENCY_26C; } break; case INS_idiv: - result.insThroughput = PERFSCORE_THROUGHPUT_6C; // The integer divide instructions have long latenies if ((id->idOpSize() == EA_8BYTE)) { @@ -14389,7 +14382,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins { assert(id->idOpSize() == EA_4BYTE); result.insThroughput = PERFSCORE_THROUGHPUT_6C; - result.insThroughput = PERFSCORE_LATENCY_26C; + result.insLatency = PERFSCORE_LATENCY_26C; } break; @@ -14416,19 +14409,21 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case IF_ARW_CNS: // ins [mem], cns result.insThroughput = PERFSCORE_THROUGHPUT_2C; + result.insLatency += PERFSCORE_LATENCY_1C; break; - case IF_RRW: // probably should use INS_shl_N + case IF_RRW: // ins reg, cl result.insThroughput = PERFSCORE_THROUGHPUT_2C; result.insLatency = PERFSCORE_LATENCY_2C; break; - case IF_MRW: // probably should use INS_shr_N + case IF_MRW: case IF_SRW: case IF_ARW: // ins [mem], cl result.insThroughput = PERFSCORE_THROUGHPUT_4C; + result.insLatency += PERFSCORE_LATENCY_2C; break; default: @@ -14441,7 +14436,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_shl_1: case INS_shr_1: case INS_sar_1: - result.insLatency = PERFSCORE_LATENCY_1C; + result.insLatency += PERFSCORE_LATENCY_1C; switch (insFmt) { case IF_RRW: @@ -14466,7 +14461,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_ror_1: case INS_rol_1: result.insThroughput = PERFSCORE_THROUGHPUT_1C; - result.insLatency = PERFSCORE_LATENCY_1C; + result.insLatency += PERFSCORE_LATENCY_1C; break; case INS_shl_N: @@ -14474,19 +14469,19 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_sar_N: case INS_ror_N: case INS_rol_N: - result.insLatency = PERFSCORE_LATENCY_2C; + result.insLatency += PERFSCORE_LATENCY_1C; switch (insFmt) { case IF_RRW_SHF: - // ins reg, cl - result.insThroughput = PERFSCORE_THROUGHPUT_2C; + // ins reg, cns + result.insThroughput = PERFSCORE_THROUGHPUT_2X; break; case IF_MRW_SHF: case IF_SRW_SHF: case IF_ARW_SHF: - // ins [mem], cl - result.insThroughput = PERFSCORE_THROUGHPUT_4C; + // ins [mem], cns + result.insThroughput = PERFSCORE_THROUGHPUT_2C; break; default: @@ -14499,13 +14494,14 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_rcr: case INS_rcl: result.insThroughput = PERFSCORE_THROUGHPUT_6C; - result.insLatency = PERFSCORE_LATENCY_6C; + result.insLatency += PERFSCORE_LATENCY_6C; break; case INS_rcr_1: case INS_rcl_1: // uops.info result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency += PERFSCORE_LATENCY_2C; break; case INS_shld: diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index b0a8327acedb6..af8dffc301208 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -288,7 +288,7 @@ inline emitAttr emitDecodeScale(unsigned ensz) /************************************************************************/ public: -void emitLoopAlign(unsigned short paddingBytes = 15); +void emitLoopAlign(unsigned short paddingBytes); void emitLongLoopAlign(unsigned short alignmentBoundary); diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 8f07bfb324673..1f8bf814185eb 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -164,17 +164,21 @@ void Compiler::fgInit() fgPreviousCandidateSIMDFieldAsgStmt = nullptr; #endif - fgHasSwitch = false; - fgPgoSchema = nullptr; - fgPgoData = nullptr; - fgPgoSchemaCount = 0; - fgNumProfileRuns = 0; - fgPgoBlockCounts = 0; - fgPgoEdgeCounts = 0; - fgPgoClassProfiles = 0; - fgCountInstrumentor = nullptr; - fgClassInstrumentor = nullptr; - fgPredListSortVector = nullptr; + fgHasSwitch = false; + fgPgoDisabled = false; + fgPgoSchema = nullptr; + fgPgoData = nullptr; + fgPgoSchemaCount = 0; + fgNumProfileRuns = 0; + fgPgoBlockCounts = 0; + fgPgoEdgeCounts = 0; + fgPgoClassProfiles = 0; + fgPgoInlineePgo = 0; + fgPgoInlineeNoPgo = 0; + fgPgoInlineeNoPgoSingleBlock = 0; + fgCountInstrumentor = nullptr; + fgClassInstrumentor = nullptr; + fgPredListSortVector = nullptr; } /***************************************************************************** @@ -848,7 +852,13 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0; const bool makeInlineObservations = (compInlineResult != nullptr); const bool isInlining = compIsForInlining(); + const bool isTier1 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1); + const bool isPreJit = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT); + const bool resolveTokens = makeInlineObservations && (isTier1 || isPreJit); unsigned retBlocks = 0; + unsigned intrinsicCalls = 0; + int prefixFlags = 0; + int value = 0; if (makeInlineObservations) { @@ -882,12 +892,14 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed compInlineResult->Note(InlineObservation::CALLEE_BEGIN_OPCODE_SCAN); } + CORINFO_RESOLVED_TOKEN resolvedToken; + CORINFO_RESOLVED_TOKEN constrainedResolvedToken; + CORINFO_CALL_INFO callInfo; + while (codeAddr < codeEndp) { OPCODE opcode = (OPCODE)getU1LittleEndian(codeAddr); codeAddr += sizeof(__int8); - opts.instrCount++; - typeIsNormed = false; DECODE_OPCODE: @@ -938,19 +950,72 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed // There has to be code after the call, otherwise the inlinee is unverifiable. if (isInlining) { - noway_assert(codeAddr < codeEndp - sz); } - // If the method has a call followed by a ret, assume that - // it is a wrapper method. - if (makeInlineObservations) + if (!makeInlineObservations) { - if ((OPCODE)getU1LittleEndian(codeAddr + sz) == CEE_RET) + break; + } + + CORINFO_METHOD_HANDLE methodHnd = nullptr; + unsigned methodFlags = 0; + bool mustExpand = false; + CorInfoIntrinsics intrinsicID = CORINFO_INTRINSIC_Illegal; + NamedIntrinsic ni = NI_Illegal; + + if (resolveTokens) + { + impResolveToken(codeAddr, &resolvedToken, CORINFO_TOKENKIND_Method); + eeGetCallInfo(&resolvedToken, + (prefixFlags & PREFIX_CONSTRAINED) ? &constrainedResolvedToken : nullptr, + combine(CORINFO_CALLINFO_KINDONLY, + (opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE), + &callInfo); + + methodHnd = callInfo.hMethod; + methodFlags = callInfo.methodFlags; + } + + if ((methodFlags & (CORINFO_FLG_INTRINSIC | CORINFO_FLG_JIT_INTRINSIC)) != 0) + { + intrinsicCalls++; + + if ((methodFlags & CORINFO_FLG_INTRINSIC) != 0) + { + intrinsicID = info.compCompHnd->getIntrinsicID(methodHnd, &mustExpand); + } + + if ((methodFlags & CORINFO_FLG_JIT_INTRINSIC) != 0) { - compInlineResult->Note(InlineObservation::CALLEE_LOOKS_LIKE_WRAPPER); + if (intrinsicID == CORINFO_INTRINSIC_Illegal) + { + ni = lookupNamedIntrinsic(methodHnd); + + switch (ni) + { + case NI_IsSupported_True: + case NI_IsSupported_False: + { + pushedStack.PushConstant(); + break; + } + + default: + { + break; + } + } + } } } + + if ((OPCODE)getU1LittleEndian(codeAddr + sz) == CEE_RET) + { + // If the method has a call followed by a ret, assume that + // it is a wrapper method. + compInlineResult->Note(InlineObservation::CALLEE_LOOKS_LIKE_WRAPPER); + } } break; @@ -1081,17 +1146,79 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed break; case CEE_UNALIGNED: + { + noway_assert(sz == sizeof(__int8)); + prefixFlags |= PREFIX_UNALIGNED; + + value = getU1LittleEndian(codeAddr); + codeAddr += sizeof(__int8); + + impValidateMemoryAccessOpcode(codeAddr, codeEndp, false); + goto OBSERVE_OPCODE; + } + case CEE_CONSTRAINED: + { + noway_assert(sz == sizeof(unsigned)); + prefixFlags |= PREFIX_CONSTRAINED; + + if (resolveTokens) + { + impResolveToken(codeAddr, &constrainedResolvedToken, CORINFO_TOKENKIND_Constrained); + } + codeAddr += sizeof(unsigned); + + { + OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp); + + if (actualOpcode != CEE_CALLVIRT) + { + BADCODE("constrained. has to be followed by callvirt"); + } + } + goto OBSERVE_OPCODE; + } + case CEE_READONLY: + { + noway_assert(sz == 0); + prefixFlags |= PREFIX_READONLY; + + { + OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp); + + if ((actualOpcode != CEE_LDELEMA) && !impOpcodeIsCallOpcode(actualOpcode)) + { + BADCODE("readonly. has to be followed by ldelema or call"); + } + } + goto OBSERVE_OPCODE; + } + case CEE_VOLATILE: + { + noway_assert(sz == 0); + prefixFlags |= PREFIX_VOLATILE; + + impValidateMemoryAccessOpcode(codeAddr, codeEndp, true); + goto OBSERVE_OPCODE; + } + case CEE_TAILCALL: { - if (codeAddr >= codeEndp) + noway_assert(sz == 0); + prefixFlags |= PREFIX_TAILCALL_EXPLICIT; + { - goto TOO_FAR; + OPCODE actualOpcode = impGetNonPrefixOpcode(codeAddr, codeEndp); + + if (!impOpcodeIsCallOpcode(actualOpcode)) + { + BADCODE("tailcall. has to be followed by call, callvirt or calli"); + } } + goto OBSERVE_OPCODE; } - break; case CEE_STARG: case CEE_STARG_S: @@ -1308,6 +1435,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed // the list of other opcodes (for all platforms). FALLTHROUGH; + case CEE_MKREFANY: case CEE_RETHROW: if (makeInlineObservations) @@ -1382,6 +1510,7 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed fgObserveInlineConstants(opcode, pushedStack, isInlining); } break; + case CEE_RET: retBlocks++; break; @@ -1393,6 +1522,14 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed // Skip any remaining operands this opcode may have codeAddr += sz; + // Clear any prefix flags that may have been set + prefixFlags = 0; + + // Increment the number of observed instructions + opts.instrCount++; + + OBSERVE_OPCODE: + // Note the opcode we just saw if (makeInlineObservations) { @@ -1400,6 +1537,8 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed typeIsNormed ? InlineObservation::CALLEE_OPCODE_NORMED : InlineObservation::CALLEE_OPCODE; compInlineResult->NoteInt(obs, opcode); } + + typeIsNormed = false; } if (codeAddr != codeEndp) @@ -4219,12 +4358,12 @@ BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst) flowList* newEdge = fgGetPredForBlock(jmpBlk, bSrc); jmpBlk->bbWeight = (newEdge->edgeWeightMin() + newEdge->edgeWeightMax()) / 2; - if (bSrc->bbWeight == 0) + if (bSrc->bbWeight == BB_ZERO_WEIGHT) { - jmpBlk->bbWeight = 0; + jmpBlk->bbWeight = BB_ZERO_WEIGHT; } - if (jmpBlk->bbWeight == 0) + if (jmpBlk->bbWeight == BB_ZERO_WEIGHT) { jmpBlk->bbFlags |= BBF_RUN_RARELY; } diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index 07720a8b18713..1ae0151a9ba27 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -764,7 +764,7 @@ bool Compiler::fgDumpFlowGraph(Phases phase) { if (createDotFile) { - fprintf(fgxFile, " BB%02u [label = \"BB%02u\\n\\n", block->bbNum, block->bbNum); + fprintf(fgxFile, " " FMT_BB " [label = \"" FMT_BB "\\n\\n", block->bbNum, block->bbNum); // "Raw" Profile weight if (block->hasProfileWeight()) @@ -822,10 +822,18 @@ bool Compiler::fgDumpFlowGraph(Phases phase) { fprintf(fgxFile, "\n loopHead=\"true\""); } + + const char* rootTreeOpName = "n/a"; + if (block->lastNode() != nullptr) + { + rootTreeOpName = GenTree::OpName(block->lastNode()->OperGet()); + } + fprintf(fgxFile, "\n weight="); fprintfDouble(fgxFile, ((double)block->bbWeight) / weightDivisor); fprintf(fgxFile, "\n codeEstimate=\"%d\"", fgGetCodeEstimate(block)); fprintf(fgxFile, "\n startOffset=\"%d\"", block->bbCodeOffs); + fprintf(fgxFile, "\n rootTreeOp=\"%s\"", rootTreeOpName); fprintf(fgxFile, "\n endOffset=\"%d\"", block->bbCodeOffsEnd); fprintf(fgxFile, ">"); fprintf(fgxFile, "\n "); @@ -1952,8 +1960,6 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef return; } - DWORD startTickCount = GetTickCount(); - #if defined(FEATURE_EH_FUNCLETS) bool reachedFirstFunclet = false; if (fgFuncletsCreated) @@ -2610,6 +2616,7 @@ void Compiler::fgDebugCheckDispFlags(GenTree* tree, unsigned dispFlags, unsigned { printf("%c", (dispFlags & GTF_IND_INVARIANT) ? '#' : '-'); printf("%c", (dispFlags & GTF_IND_NONFAULTING) ? 'n' : '-'); + printf("%c", (dispFlags & GTF_IND_NONNULL) ? '@' : '-'); } GenTree::gtDispFlags(dispFlags, debugFlags); } diff --git a/src/coreclr/jit/fgehopt.cpp b/src/coreclr/jit/fgehopt.cpp index 7de6120fd9b4a..39bbec95a7c59 100644 --- a/src/coreclr/jit/fgehopt.cpp +++ b/src/coreclr/jit/fgehopt.cpp @@ -1323,7 +1323,6 @@ void Compiler::fgDebugCheckTryFinallyExits() { unsigned XTnum = 0; EHblkDsc* HBtab = compHndBBtab; - unsigned cloneCount = 0; bool allTryExitsValid = true; for (; XTnum < compHndBBtabCount; XTnum++, HBtab++) { @@ -2256,6 +2255,14 @@ void Compiler::fgTailMergeThrowsFallThroughHelper(BasicBlock* predBlock, // Note there is now a jump to the canonical block canonicalBlock->bbFlags |= (BBF_JMP_TARGET | BBF_HAS_LABEL); + + // If nonCanonicalBlock has only one pred, all its flow transfers. + // If it has multiple preds, then we need edge counts or likelihoods + // to figure things out. + // + // For now just do a minimal update. + // + newBlock->inheritWeight(nonCanonicalBlock); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/fgflow.cpp b/src/coreclr/jit/fgflow.cpp index 8c5cd174ca153..5a1ba9d687ccc 100644 --- a/src/coreclr/jit/fgflow.cpp +++ b/src/coreclr/jit/fgflow.cpp @@ -704,8 +704,6 @@ void Compiler::fgRemoveCheapPred(BasicBlock* block, BasicBlock* blockPred) assert(!fgComputePredsDone); assert(fgCheapPredsValid); - flowList* oldEdge = nullptr; - assert(block != nullptr); assert(blockPred != nullptr); assert(block->bbCheapPreds != nullptr); diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index a51ca24773da7..bcb03bc56e5fe 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -817,7 +817,7 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD if (condTree->OperGet() == GT_CNS_INT) { - JITDUMP(" ... found foldable jtrue at [%06u] in BB%02u\n", dspTreeID(tree), block->bbNum); + JITDUMP(" ... found foldable jtrue at [%06u] in " FMT_BB "\n", dspTreeID(tree), block->bbNum); noway_assert((block->bbNext->countOfInEdges() > 0) && (block->bbJumpDest->countOfInEdges() > 0)); // We have a constant operand, and should have the all clear to optimize. @@ -849,7 +849,7 @@ Compiler::fgWalkResult Compiler::fgLateDevirtualization(GenTree** pTree, fgWalkD // other transitively unreachable blocks. if (bNotTaken->bbRefs == 0) { - JITDUMP("... it looks like BB%02u is now unreachable!\n", bNotTaken->bbNum); + JITDUMP("... it looks like " FMT_BB " is now unreachable!\n", bNotTaken->bbNum); } } } @@ -1365,15 +1365,16 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) continue; } - if (inheritWeight) - { - block->inheritWeight(iciBlock); - inheritWeight = false; - } - else + // If we were unable to compute a scale for some reason, then + // try to do something plausible. Entry/exit blocks match call + // site, internal blocks scaled by half; all rare blocks left alone. + // + if (!block->isRunRarely()) { - block->modifyBBWeight(iciBlock->bbWeight / 2); + block->inheritWeightPercentage(iciBlock, inheritWeight ? 100 : 50); } + + inheritWeight = false; } // Insert inlinee's blocks into inliner's block list. @@ -1426,7 +1427,30 @@ void Compiler::fgInsertInlineeBlocks(InlineInfo* pInlineInfo) // Update unmanaged call details info.compUnmanagedCallCountWithGCTransition += InlineeCompiler->info.compUnmanagedCallCountWithGCTransition; -// Update optMethodFlags + // Update stats for inlinee PGO + // + if (InlineeCompiler->fgPgoSchema != nullptr) + { + fgPgoInlineePgo++; + } + else if (InlineeCompiler->fgPgoFailReason != nullptr) + { + // Single block inlinees may not have probes + // when we've ensabled minimal profiling (which + // is now the default). + // + if (InlineeCompiler->fgBBcount == 1) + { + fgPgoInlineeNoPgoSingleBlock++; + } + else + { + fgPgoInlineeNoPgo++; + } + } + + // Update optMethodFlags + CLANG_FORMAT_COMMENT_ANCHOR; #ifdef DEBUG unsigned optMethodFlagsBefore = optMethodFlags; @@ -1847,8 +1871,6 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) } else { - CORINFO_CLASS_HANDLE structType = - lclVarInfo[lclNum + inlineInfo->argCnt].lclVerTypeInfo.GetClassHandle(); tree = gtNewBlkOpNode(gtNewLclvNode(tmpNum, lclTyp), // Dest gtNewIconNode(0), // Value false, // isVolatile diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index cc6ff733f4bba..9b0eafce39cef 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -1732,7 +1732,7 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) block->bbWeight = bNext->bbWeight; block->bbFlags |= (bNext->bbFlags & BBF_PROF_WEIGHT); // Set the profile weight flag (if necessary) - assert(block->bbWeight != 0); + assert(block->bbWeight != BB_ZERO_WEIGHT); block->bbFlags &= ~BBF_RUN_RARELY; // Clear any RarelyRun flag } } @@ -1914,7 +1914,8 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext) // In this case, there's no need to update the preorder and postorder numbering // since we're changing the bbNum, this makes the basic block all set. // - JITDUMP("Renumbering BB%02u to be BB%02u to preserve dominator information\n", block->bbNum, bNext->bbNum); + JITDUMP("Renumbering " FMT_BB " to be " FMT_BB " to preserve dominator information\n", block->bbNum, + bNext->bbNum); block->bbNum = bNext->bbNum; @@ -2687,8 +2688,7 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block) // if (block->NumSucc(this) == 1) { - // Use BBJ_ALWAYS for a switch with only a default clause, or with only one unique successor. - BasicBlock* uniqueSucc = jmpTab[0]; +// Use BBJ_ALWAYS for a switch with only a default clause, or with only one unique successor. #ifdef DEBUG if (verbose) @@ -3657,6 +3657,333 @@ bool Compiler::fgOptimizeSwitchJumps() return result; } +//----------------------------------------------------------------------------- +// fgExpandRunRarelyBlocks: given the current set of run rarely blocks, +// see if we can deduce that some other blocks are run rarely. +// +// Returns: +// True if new block was marked as run rarely. +// +bool Compiler::fgExpandRarelyRunBlocks() +{ + bool result = false; + +#ifdef DEBUG + if (verbose) + { + printf("\n*************** In fgExpandRarelyRunBlocks()\n"); + } + + const char* reason = nullptr; +#endif + + // Helper routine to figure out the lexically earliest predecessor + // of bPrev that could become run rarely, given that bPrev + // has just become run rarely. + // + // Note this is potentially expensive for large flow graphs and blocks + // with lots of predecessors. + // + auto newRunRarely = [](BasicBlock* block, BasicBlock* bPrev) { + // Figure out earliest block that might be impacted + BasicBlock* bPrevPrev = nullptr; + BasicBlock* tmpbb; + + if ((bPrev->bbFlags & BBF_KEEP_BBJ_ALWAYS) != 0) + { + // If we've got a BBJ_CALLFINALLY/BBJ_ALWAYS pair, treat the BBJ_CALLFINALLY as an + // additional predecessor for the BBJ_ALWAYS block + tmpbb = bPrev->bbPrev; + noway_assert(tmpbb != nullptr); +#if defined(FEATURE_EH_FUNCLETS) + noway_assert(tmpbb->isBBCallAlwaysPair()); + bPrevPrev = tmpbb; +#else + if (tmpbb->bbJumpKind == BBJ_CALLFINALLY) + { + bPrevPrev = tmpbb; + } +#endif + } + + flowList* pred = bPrev->bbPreds; + + if (pred != nullptr) + { + // bPrevPrev will be set to the lexically + // earliest predecessor of bPrev. + + while (pred != nullptr) + { + if (bPrevPrev == nullptr) + { + // Initially we select the first block in the bbPreds list + bPrevPrev = pred->getBlock(); + continue; + } + + // Walk the flow graph lexically forward from pred->getBlock() + // if we find (block == bPrevPrev) then + // pred->getBlock() is an earlier predecessor. + for (tmpbb = pred->getBlock(); tmpbb != nullptr; tmpbb = tmpbb->bbNext) + { + if (tmpbb == bPrevPrev) + { + /* We found an ealier predecessor */ + bPrevPrev = pred->getBlock(); + break; + } + else if (tmpbb == bPrev) + { + // We have reached bPrev so stop walking + // as this cannot be an earlier predecessor + break; + } + } + + // Onto the next predecessor + pred = pred->flNext; + } + } + + if (bPrevPrev != nullptr) + { + // Walk the flow graph forward from bPrevPrev + // if we don't find (tmpbb == bPrev) then our candidate + // bPrevPrev is lexically after bPrev and we do not + // want to select it as our new block + + for (tmpbb = bPrevPrev; tmpbb != nullptr; tmpbb = tmpbb->bbNext) + { + if (tmpbb == bPrev) + { + // Set up block back to the lexically + // earliest predecessor of pPrev + + return bPrevPrev; + } + } + } + + // No reason to backtrack + // + return (BasicBlock*)nullptr; + }; + + // We expand the number of rarely run blocks by observing + // that a block that falls into or jumps to a rarely run block, + // must itself be rarely run and when we have a conditional + // jump in which both branches go to rarely run blocks then + // the block must itself be rarely run + + BasicBlock* block; + BasicBlock* bPrev; + + for (bPrev = fgFirstBB, block = bPrev->bbNext; block != nullptr; bPrev = block, block = block->bbNext) + { + if (bPrev->isRunRarely()) + { + continue; + } + + if (bPrev->hasProfileWeight()) + { + continue; + } + + const char* reason = nullptr; + + switch (bPrev->bbJumpKind) + { + case BBJ_ALWAYS: + + if (bPrev->bbJumpDest->isRunRarely()) + { + reason = "Unconditional jump to a rarely run block"; + } + break; + + case BBJ_CALLFINALLY: + + if (bPrev->isBBCallAlwaysPair() && block->isRunRarely()) + { + reason = "Call of finally followed by a rarely run block"; + } + break; + + case BBJ_NONE: + + if (block->isRunRarely()) + { + reason = "Falling into a rarely run block"; + } + break; + + case BBJ_COND: + + if (block->isRunRarely() && bPrev->bbJumpDest->isRunRarely()) + { + reason = "Both sides of a conditional jump are rarely run"; + } + break; + + default: + break; + } + + if (reason != nullptr) + { + JITDUMP("%s, marking " FMT_BB " as rarely run\n", reason, bPrev->bbNum); + + // Must not have previously been marked + noway_assert(!bPrev->isRunRarely()); + + // Mark bPrev as a new rarely run block + bPrev->bbSetRunRarely(); + + // We have marked at least one block. + // + result = true; + + // See if we should to backtrack. + // + BasicBlock* bContinue = newRunRarely(block, bPrev); + + // If so, reset block to the backtrack point. + // + if (bContinue != nullptr) + { + block = bContinue; + } + } + } + + // Now iterate over every block to see if we can prove that a block is rarely run + // (i.e. when all predecessors to the block are rarely run) + // + for (bPrev = fgFirstBB, block = bPrev->bbNext; block != nullptr; bPrev = block, block = block->bbNext) + { + // If block is not run rarely, then check to make sure that it has + // at least one non-rarely run block. + + if (!block->isRunRarely()) + { + bool rare = true; + + /* Make sure that block has at least one normal predecessor */ + for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) + { + /* Find the fall through predecessor, if any */ + if (!pred->getBlock()->isRunRarely()) + { + rare = false; + break; + } + } + + if (rare) + { + // If 'block' is the start of a handler or filter then we cannot make it + // rarely run because we may have an exceptional edge that + // branches here. + // + if (bbIsHandlerBeg(block)) + { + rare = false; + } + } + + if (rare) + { + block->bbSetRunRarely(); + result = true; + +#ifdef DEBUG + if (verbose) + { + printf("All branches to " FMT_BB " are from rarely run blocks, marking as rarely run\n", + block->bbNum); + } +#endif // DEBUG + + // When marking a BBJ_CALLFINALLY as rarely run we also mark + // the BBJ_ALWAYS that comes after it as rarely run + // + if (block->isBBCallAlwaysPair()) + { + BasicBlock* bNext = block->bbNext; + PREFIX_ASSUME(bNext != nullptr); + bNext->bbSetRunRarely(); +#ifdef DEBUG + if (verbose) + { + printf("Also marking the BBJ_ALWAYS at " FMT_BB " as rarely run\n", bNext->bbNum); + } +#endif // DEBUG + } + } + } + + /* COMPACT blocks if possible */ + if (bPrev->bbJumpKind == BBJ_NONE) + { + if (fgCanCompactBlocks(bPrev, block)) + { + fgCompactBlocks(bPrev, block); + + block = bPrev; + continue; + } + } + // + // if bPrev->bbWeight is not based upon profile data we can adjust + // the weights of bPrev and block + // + else if (bPrev->isBBCallAlwaysPair() && // we must have a BBJ_CALLFINALLY and BBK_ALWAYS pair + (bPrev->bbWeight != block->bbWeight) && // the weights are currently different + !bPrev->hasProfileWeight()) // and the BBJ_CALLFINALLY block is not using profiled + // weights + { + if (block->isRunRarely()) + { + bPrev->bbWeight = + block->bbWeight; // the BBJ_CALLFINALLY block now has the same weight as the BBJ_ALWAYS block + bPrev->bbFlags |= BBF_RUN_RARELY; // and is now rarely run +#ifdef DEBUG + if (verbose) + { + printf("Marking the BBJ_CALLFINALLY block at " FMT_BB " as rarely run because " FMT_BB + " is rarely run\n", + bPrev->bbNum, block->bbNum); + } +#endif // DEBUG + } + else if (bPrev->isRunRarely()) + { + block->bbWeight = + bPrev->bbWeight; // the BBJ_ALWAYS block now has the same weight as the BBJ_CALLFINALLY block + block->bbFlags |= BBF_RUN_RARELY; // and is now rarely run +#ifdef DEBUG + if (verbose) + { + printf("Marking the BBJ_ALWAYS block at " FMT_BB " as rarely run because " FMT_BB + " is rarely run\n", + block->bbNum, bPrev->bbNum); + } +#endif // DEBUG + } + else // Both blocks are hot, bPrev is known not to be using profiled weight + { + bPrev->bbWeight = + block->bbWeight; // the BBJ_CALLFINALLY block now has the same weight as the BBJ_ALWAYS block + } + noway_assert(block->bbWeight == bPrev->bbWeight); + } + } + + return result; +} + #ifdef _PREFAST_ #pragma warning(push) #pragma warning(disable : 21000) // Suppress PREFast warning about overly large function @@ -3790,7 +4117,7 @@ bool Compiler::fgReorderBlocks() // if the weight of bDest is greater or equal to the weight of block // also the weight of bDest can't be zero. // - if ((bDest->bbWeight < block->bbWeight) || (bDest->bbWeight == 0)) + if ((bDest->bbWeight < block->bbWeight) || (bDest->bbWeight == BB_ZERO_WEIGHT)) { reorderBlock = false; } @@ -4023,7 +4350,7 @@ bool Compiler::fgReorderBlocks() } } - if ((bTmp->bbFallsThrough() == false) || (bTmp->bbWeight == 0)) + if ((bTmp->bbFallsThrough() == false) || (bTmp->bbWeight == BB_ZERO_WEIGHT)) { lastNonFallThroughBlock = bTmp; } diff --git a/src/coreclr/jit/fgprofile.cpp b/src/coreclr/jit/fgprofile.cpp index f32137f1404e6..6f71c2ebacff4 100644 --- a/src/coreclr/jit/fgprofile.cpp +++ b/src/coreclr/jit/fgprofile.cpp @@ -69,59 +69,62 @@ void Compiler::fgComputeProfileScale() // No, not yet -- try and compute the scale. JITDUMP("Computing inlinee profile scale:\n"); - // Call site has profile weight? - // - // Todo: handle case of unprofiled caller invoking profiled callee. + // Callee has profile data? // - const BasicBlock* callSiteBlock = impInlineInfo->iciBlock; - if (!callSiteBlock->hasProfileWeight()) + if (!fgHaveProfileData()) { - JITDUMP(" ... call site not profiled\n"); - impInlineInfo->profileScaleState = InlineInfo::ProfileScaleState::UNAVAILABLE; - return; + // No; we will carry on nonetheless. + // + JITDUMP(" ... no callee profile data, will use non-pgo weight to scale\n"); } - const BasicBlock::weight_t callSiteWeight = callSiteBlock->bbWeight; + // Ostensibly this should be fgCalledCount for the callee, but that's not available + // as it requires some analysis. + // + // For most callees it will be the same as the entry block count. + // + // Note when/if we early do normalization this may need to change. + // + BasicBlock::weight_t const calleeWeight = fgFirstBB->bbWeight; - // Call site has zero count? + // Callee entry weight is nonzero? // - // Todo: perhaps retain some semblance of callee profile data, - // possibly scaled down severely. + // If callee entry profile count is zero, perhaps we should discard + // the profile data. // - if (callSiteWeight == 0) + if (calleeWeight == BB_ZERO_WEIGHT) { - JITDUMP(" ... zero call site count\n"); + JITDUMP(" ... callee entry count is zero\n"); impInlineInfo->profileScaleState = InlineInfo::ProfileScaleState::UNAVAILABLE; return; } - // Callee has profile data? + // Call site has profile weight? // - if (!fgHaveProfileData()) + const BasicBlock* callSiteBlock = impInlineInfo->iciBlock; + if (!callSiteBlock->hasProfileWeight()) { - JITDUMP(" ... no callee profile data\n"); - impInlineInfo->profileScaleState = InlineInfo::ProfileScaleState::UNAVAILABLE; - return; + // No? We will carry on nonetheless. + // + JITDUMP(" ... call site not profiled, will use non-pgo weight to scale\n"); } - // Find callee's unscaled entry weight. + const BasicBlock::weight_t callSiteWeight = callSiteBlock->bbWeight; + + // Call site has zero count? // - // Ostensibly this should be fgCalledCount for the callee, but that's not available - // as it requires some analysis. + // Todo: perhaps retain some semblance of callee profile data, + // possibly scaled down severely. // - // For most callees it will be the same as the entry block count. + // You might wonder why we bother to inline at cold sites. + // Recall ALWAYS and FORCE inlines bypass all profitability checks. + // And, there can be hot-path benefits to a cold-path inline. // - if (!fgFirstBB->hasProfileWeight()) + if (callSiteWeight == BB_ZERO_WEIGHT) { - JITDUMP(" ... no callee profile data for entry block\n"); - impInlineInfo->profileScaleState = InlineInfo::ProfileScaleState::UNAVAILABLE; - return; + JITDUMP(" ... zero call site count; scale will be 0.0\n"); } - // Note when/if we early do normalization this may need to change. - // - BasicBlock::weight_t const calleeWeight = fgFirstBB->bbWeight; - // If profile data reflects a complete single run we can expect // calleeWeight >= callSiteWeight. // @@ -131,13 +134,6 @@ void Compiler::fgComputeProfileScale() // So, we are willing to scale the callee counts down or up as // needed to match the call site. // - if (calleeWeight == BB_ZERO_WEIGHT) - { - JITDUMP(" ... callee entry count is zero\n"); - impInlineInfo->profileScaleState = InlineInfo::ProfileScaleState::UNAVAILABLE; - return; - } - // Hence, scale can be somewhat arbitrary... // const double scale = ((double)callSiteWeight) / calleeWeight; @@ -1680,6 +1676,7 @@ PhaseStatus Compiler::fgInstrumentMethod() //------------------------------------------------------------------------ // fgIncorporateProfileData: add block/edge profile data to the flowgraph +// and compute profile scale for inlinees // // Returns: // appropriate phase status @@ -1692,6 +1689,12 @@ PhaseStatus Compiler::fgIncorporateProfileData() { JITDUMP("JitStress -- incorporating random profile data\n"); fgIncorporateBlockCounts(); + + if (compIsForInlining()) + { + fgComputeProfileScale(); + } + return PhaseStatus::MODIFIED_EVERYTHING; } @@ -1707,6 +1710,12 @@ PhaseStatus Compiler::fgIncorporateProfileData() { JITDUMP("BBOPT not set\n"); } + + if (compIsForInlining()) + { + fgComputeProfileScale(); + } + return PhaseStatus::MODIFIED_NOTHING; } @@ -2445,7 +2454,10 @@ void EfficientEdgeCountReconstructor::Propagate() { // We don't expect mismatches or convergence failures. // - assert(!m_mismatch); + + // Mismatches are currently expected as the flow for static pgo doesn't prevent them now. + // assert(!m_mismatch); + assert(!m_failedToConverge); // If any issues arose during reconstruction, don't set weights. @@ -2457,7 +2469,8 @@ void EfficientEdgeCountReconstructor::Propagate() // Make sure nothing else in the jit looks at the profile data. // - m_comp->fgPgoSchema = nullptr; + m_comp->fgPgoSchema = nullptr; + m_comp->fgPgoFailReason = "PGO data available, but there was a reconstruction error"; return; } @@ -2518,6 +2531,11 @@ void Compiler::fgIncorporateEdgeCounts() // slop - profile slush fund // wbUsedSlop [out] - true if we tapped into the slush fund // +// Returns: +// true if the edge weight was adjusted +// false if the edge weight update was inconsistent with the +// edge's current [min,max} +// bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, BasicBlock* bDst, BasicBlock::weight_t slop, @@ -2553,10 +2571,8 @@ bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, } } } - else + else if (flEdgeWeightMin > newWeight) { - assert(flEdgeWeightMin > newWeight); - // We have already determined that this edge's weight // is more than newWeight, so we just allow for the slop if ((newWeight + slop) >= flEdgeWeightMin) @@ -2578,31 +2594,28 @@ bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, // If we are returning true then we should have adjusted the range so that // the newWeight is in new range [Min..Max] or fgEdgeWeightMax is zero. // Also we should have set wbUsedSlop to true. - if (result == true) + if (result) { assert((flEdgeWeightMax == BB_ZERO_WEIGHT) || ((newWeight <= flEdgeWeightMax) && (newWeight >= flEdgeWeightMin))); - if (wbUsedSlop != nullptr) - { - assert(*wbUsedSlop == true); - } + assert((wbUsedSlop == nullptr) || (*wbUsedSlop)); } } #if DEBUG - if (result == false) + if (result) + { + JITDUMP("Updated min weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, + bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); + } + else { JITDUMP("Not adjusting min weight of " FMT_BB " -> " FMT_BB "; new value " FMT_WT " not in range [" FMT_WT ".." FMT_WT "] (+/- " FMT_WT ")\n", getBlock()->bbNum, bDst->bbNum, newWeight, flEdgeWeightMin, flEdgeWeightMax, slop); result = false; // break here } - else - { - JITDUMP("Updated min weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, - bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); - } #endif // DEBUG return result; @@ -2617,6 +2630,11 @@ bool flowList::setEdgeWeightMinChecked(BasicBlock::weight_t newWeight, // slop - profile slush fund // wbUsedSlop [out] - true if we tapped into the slush fund // +// Returns: +// true if the edge weight was adjusted +// false if the edge weight update was inconsistent with the +// edge's current [min,max} +// bool flowList::setEdgeWeightMaxChecked(BasicBlock::weight_t newWeight, BasicBlock* bDst, BasicBlock::weight_t slop, @@ -2651,10 +2669,8 @@ bool flowList::setEdgeWeightMaxChecked(BasicBlock::weight_t newWeight, } } } - else + else if (flEdgeWeightMin > newWeight) { - assert(flEdgeWeightMin > newWeight); - // We have already determined that this edge's weight // is more than newWeight, so we just allow for the slop if ((newWeight + slop) >= flEdgeWeightMin) @@ -2677,28 +2693,28 @@ bool flowList::setEdgeWeightMaxChecked(BasicBlock::weight_t newWeight, // If we are returning true then we should have adjusted the range so that // the newWeight is in new range [Min..Max] or fgEdgeWeightMax is zero // Also we should have set wbUsedSlop to true, unless it is NULL - if (result == true) + if (result) { assert((flEdgeWeightMax == BB_ZERO_WEIGHT) || ((newWeight <= flEdgeWeightMax) && (newWeight >= flEdgeWeightMin))); - assert((wbUsedSlop == nullptr) || (*wbUsedSlop == true)); + assert((wbUsedSlop == nullptr) || (*wbUsedSlop)); } } #if DEBUG - if (result == false) + if (result) + { + JITDUMP("Updated max weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, + bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); + } + else { JITDUMP("Not adjusting max weight of " FMT_BB " -> " FMT_BB "; new value " FMT_WT " not in range [" FMT_WT ".." FMT_WT "] (+/- " FMT_WT ")\n", getBlock()->bbNum, bDst->bbNum, newWeight, flEdgeWeightMin, flEdgeWeightMax, slop); result = false; // break here } - else - { - JITDUMP("Updated max weight of " FMT_BB " -> " FMT_BB " to [" FMT_WT ".." FMT_WT "]\n", getBlock()->bbNum, - bDst->bbNum, flEdgeWeightMin, flEdgeWeightMax); - } #endif // DEBUG return result; @@ -3096,31 +3112,40 @@ void Compiler::fgComputeEdgeWeights() } otherEdge = fgGetPredForBlock(otherDst, bSrc); - noway_assert(edge->edgeWeightMin() <= edge->edgeWeightMax()); - noway_assert(otherEdge->edgeWeightMin() <= otherEdge->edgeWeightMax()); + // If we see min/max violations, just give up on the computations + // + const bool edgeWeightSensible = edge->edgeWeightMin() <= edge->edgeWeightMax(); + const bool otherEdgeWeightSensible = otherEdge->edgeWeightMin() <= otherEdge->edgeWeightMax(); - // Adjust edge->flEdgeWeightMin up or adjust otherEdge->flEdgeWeightMax down - diff = bSrc->bbWeight - (edge->edgeWeightMin() + otherEdge->edgeWeightMax()); - if (diff > 0) - { - assignOK &= edge->setEdgeWeightMinChecked(edge->edgeWeightMin() + diff, bDst, slop, &usedSlop); - } - else if (diff < 0) - { - assignOK &= otherEdge->setEdgeWeightMaxChecked(otherEdge->edgeWeightMax() + diff, otherDst, - slop, &usedSlop); - } + assignOK &= edgeWeightSensible && otherEdgeWeightSensible; - // Adjust otherEdge->flEdgeWeightMin up or adjust edge->flEdgeWeightMax down - diff = bSrc->bbWeight - (otherEdge->edgeWeightMin() + edge->edgeWeightMax()); - if (diff > 0) - { - assignOK &= otherEdge->setEdgeWeightMinChecked(otherEdge->edgeWeightMin() + diff, otherDst, - slop, &usedSlop); - } - else if (diff < 0) + if (assignOK) { - assignOK &= edge->setEdgeWeightMaxChecked(edge->edgeWeightMax() + diff, bDst, slop, &usedSlop); + // Adjust edge->flEdgeWeightMin up or adjust otherEdge->flEdgeWeightMax down + diff = bSrc->bbWeight - (edge->edgeWeightMin() + otherEdge->edgeWeightMax()); + if (diff > 0) + { + assignOK &= + edge->setEdgeWeightMinChecked(edge->edgeWeightMin() + diff, bDst, slop, &usedSlop); + } + else if (diff < 0) + { + assignOK &= otherEdge->setEdgeWeightMaxChecked(otherEdge->edgeWeightMax() + diff, otherDst, + slop, &usedSlop); + } + + // Adjust otherEdge->flEdgeWeightMin up or adjust edge->flEdgeWeightMax down + diff = bSrc->bbWeight - (otherEdge->edgeWeightMin() + edge->edgeWeightMax()); + if (diff > 0) + { + assignOK &= otherEdge->setEdgeWeightMinChecked(otherEdge->edgeWeightMin() + diff, otherDst, + slop, &usedSlop); + } + else if (diff < 0) + { + assignOK &= + edge->setEdgeWeightMaxChecked(edge->edgeWeightMax() + diff, bDst, slop, &usedSlop); + } } if (!assignOK) @@ -3191,33 +3216,41 @@ void Compiler::fgComputeEdgeWeights() // otherMaxEdgesWeightSum is the sum of all of the other edges flEdgeWeightMax values // This can be used to compute a lower bound for our minimum edge weight - noway_assert(maxEdgeWeightSum >= edge->edgeWeightMax()); - BasicBlock::weight_t otherMaxEdgesWeightSum = maxEdgeWeightSum - edge->edgeWeightMax(); - - // otherMinEdgesWeightSum is the sum of all of the other edges flEdgeWeightMin values - // This can be used to compute an upper bound for our maximum edge weight - noway_assert(minEdgeWeightSum >= edge->edgeWeightMin()); - BasicBlock::weight_t otherMinEdgesWeightSum = minEdgeWeightSum - edge->edgeWeightMin(); + // + BasicBlock::weight_t const otherMaxEdgesWeightSum = maxEdgeWeightSum - edge->edgeWeightMax(); - if (bDstWeight >= otherMaxEdgesWeightSum) + if (otherMaxEdgesWeightSum >= BB_ZERO_WEIGHT) { - // minWeightCalc is our minWeight when every other path to bDst takes it's flEdgeWeightMax value - BasicBlock::weight_t minWeightCalc = - (BasicBlock::weight_t)(bDstWeight - otherMaxEdgesWeightSum); - if (minWeightCalc > edge->edgeWeightMin()) + if (bDstWeight >= otherMaxEdgesWeightSum) { - assignOK &= edge->setEdgeWeightMinChecked(minWeightCalc, bDst, slop, &usedSlop); + // minWeightCalc is our minWeight when every other path to bDst takes it's flEdgeWeightMax + // value + BasicBlock::weight_t minWeightCalc = + (BasicBlock::weight_t)(bDstWeight - otherMaxEdgesWeightSum); + if (minWeightCalc > edge->edgeWeightMin()) + { + assignOK &= edge->setEdgeWeightMinChecked(minWeightCalc, bDst, slop, &usedSlop); + } } } - if (bDstWeight >= otherMinEdgesWeightSum) + // otherMinEdgesWeightSum is the sum of all of the other edges flEdgeWeightMin values + // This can be used to compute an upper bound for our maximum edge weight + // + BasicBlock::weight_t const otherMinEdgesWeightSum = minEdgeWeightSum - edge->edgeWeightMin(); + + if (otherMinEdgesWeightSum >= BB_ZERO_WEIGHT) { - // maxWeightCalc is our maxWeight when every other path to bDst takes it's flEdgeWeightMin value - BasicBlock::weight_t maxWeightCalc = - (BasicBlock::weight_t)(bDstWeight - otherMinEdgesWeightSum); - if (maxWeightCalc < edge->edgeWeightMax()) + if (bDstWeight >= otherMinEdgesWeightSum) { - assignOK &= edge->setEdgeWeightMaxChecked(maxWeightCalc, bDst, slop, &usedSlop); + // maxWeightCalc is our maxWeight when every other path to bDst takes it's flEdgeWeightMin + // value + BasicBlock::weight_t maxWeightCalc = + (BasicBlock::weight_t)(bDstWeight - otherMinEdgesWeightSum); + if (maxWeightCalc < edge->edgeWeightMax()) + { + assignOK &= edge->setEdgeWeightMaxChecked(maxWeightCalc, bDst, slop, &usedSlop); + } } } diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 9fb409929044d..578967bac5285 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3161,330 +3161,6 @@ BasicBlock* Compiler::fgEndBBAfterMainFunction() return nullptr; } -/***************************************************************************** - * - * Function called to expand the set of rarely run blocks - */ - -bool Compiler::fgExpandRarelyRunBlocks() -{ - bool result = false; - -#ifdef DEBUG - if (verbose) - { - printf("\n*************** In fgExpandRarelyRunBlocks()\n"); - } - - const char* reason = nullptr; -#endif - - // We expand the number of rarely run blocks by observing - // that a block that falls into or jumps to a rarely run block, - // must itself be rarely run and when we have a conditional - // jump in which both branches go to rarely run blocks then - // the block must itself be rarely run - - BasicBlock* block; - BasicBlock* bPrev; - - for (bPrev = fgFirstBB, block = bPrev->bbNext; block != nullptr; bPrev = block, block = block->bbNext) - { - if (bPrev->isRunRarely()) - { - continue; - } - - /* bPrev is known to be a normal block here */ - switch (bPrev->bbJumpKind) - { - case BBJ_ALWAYS: - - /* Is the jump target rarely run? */ - if (bPrev->bbJumpDest->isRunRarely()) - { - INDEBUG(reason = "Unconditional jump to a rarely run block";) - goto NEW_RARELY_RUN; - } - break; - - case BBJ_CALLFINALLY: - - // Check for a BBJ_CALLFINALLY followed by a rarely run paired BBJ_ALWAYS - // - // TODO-Cleanup: How can this be hit? If bbPrev starts a CallAlwaysPair, then this - // block must be BBJ_ALWAYS, not BBJ_CALLFINALLY. - if (bPrev->isBBCallAlwaysPair()) - { - /* Is the next block rarely run? */ - if (block->isRunRarely()) - { - INDEBUG(reason = "Call of finally followed by a rarely run block";) - goto NEW_RARELY_RUN; - } - } - break; - - case BBJ_NONE: - - /* is fall through target rarely run? */ - if (block->isRunRarely()) - { - INDEBUG(reason = "Falling into a rarely run block";) - goto NEW_RARELY_RUN; - } - break; - - case BBJ_COND: - - if (!block->isRunRarely()) - { - continue; - } - - /* If both targets of the BBJ_COND are run rarely then don't reorder */ - if (bPrev->bbJumpDest->isRunRarely()) - { - /* bPrev should also be marked as run rarely */ - if (!bPrev->isRunRarely()) - { - INDEBUG(reason = "Both sides of a conditional jump are rarely run";) - - NEW_RARELY_RUN: - /* If the weight of the block was obtained from a profile run, - than it's more accurate than our static analysis */ - if (bPrev->hasProfileWeight()) - { - continue; - } - result = true; - -#ifdef DEBUG - assert(reason != nullptr); - if (verbose) - { - printf("%s, marking " FMT_BB " as rarely run\n", reason, bPrev->bbNum); - } -#endif // DEBUG - - /* Must not have previously been marked */ - noway_assert(!bPrev->isRunRarely()); - - /* Mark bPrev as a new rarely run block */ - bPrev->bbSetRunRarely(); - - BasicBlock* bPrevPrev = nullptr; - BasicBlock* tmpbb; - - if ((bPrev->bbFlags & BBF_KEEP_BBJ_ALWAYS) != 0) - { - // If we've got a BBJ_CALLFINALLY/BBJ_ALWAYS pair, treat the BBJ_CALLFINALLY as an - // additional predecessor for the BBJ_ALWAYS block - tmpbb = bPrev->bbPrev; - noway_assert(tmpbb != nullptr); -#if defined(FEATURE_EH_FUNCLETS) - noway_assert(tmpbb->isBBCallAlwaysPair()); - bPrevPrev = tmpbb; -#else - if (tmpbb->bbJumpKind == BBJ_CALLFINALLY) - { - bPrevPrev = tmpbb; - } -#endif - } - - /* Now go back to it's earliest predecessor to see */ - /* if it too should now be marked as rarely run */ - flowList* pred = bPrev->bbPreds; - - if ((pred != nullptr) || (bPrevPrev != nullptr)) - { - // bPrevPrev will be set to the lexically - // earliest predecessor of bPrev. - - while (pred != nullptr) - { - if (bPrevPrev == nullptr) - { - // Initially we select the first block in the bbPreds list - bPrevPrev = pred->getBlock(); - continue; - } - - // Walk the flow graph lexically forward from pred->getBlock() - // if we find (block == bPrevPrev) then - // pred->getBlock() is an earlier predecessor. - for (tmpbb = pred->getBlock(); tmpbb != nullptr; tmpbb = tmpbb->bbNext) - { - if (tmpbb == bPrevPrev) - { - /* We found an ealier predecessor */ - bPrevPrev = pred->getBlock(); - break; - } - else if (tmpbb == bPrev) - { - // We have reached bPrev so stop walking - // as this cannot be an earlier predecessor - break; - } - } - - // Onto the next predecessor - pred = pred->flNext; - } - - // Walk the flow graph forward from bPrevPrev - // if we don't find (tmpbb == bPrev) then our candidate - // bPrevPrev is lexically after bPrev and we do not - // want to select it as our new block - - for (tmpbb = bPrevPrev; tmpbb != nullptr; tmpbb = tmpbb->bbNext) - { - if (tmpbb == bPrev) - { - // Set up block back to the lexically - // earliest predecessor of pPrev - - block = bPrevPrev; - } - } - } - } - break; - - default: - break; - } - } - } - - // Now iterate over every block to see if we can prove that a block is rarely run - // (i.e. when all predecessors to the block are rarely run) - // - for (bPrev = fgFirstBB, block = bPrev->bbNext; block != nullptr; bPrev = block, block = block->bbNext) - { - // If block is not run rarely, then check to make sure that it has - // at least one non-rarely run block. - - if (!block->isRunRarely()) - { - bool rare = true; - - /* Make sure that block has at least one normal predecessor */ - for (flowList* pred = block->bbPreds; pred != nullptr; pred = pred->flNext) - { - /* Find the fall through predecessor, if any */ - if (!pred->getBlock()->isRunRarely()) - { - rare = false; - break; - } - } - - if (rare) - { - // If 'block' is the start of a handler or filter then we cannot make it - // rarely run because we may have an exceptional edge that - // branches here. - // - if (bbIsHandlerBeg(block)) - { - rare = false; - } - } - - if (rare) - { - block->bbSetRunRarely(); - result = true; - -#ifdef DEBUG - if (verbose) - { - printf("All branches to " FMT_BB " are from rarely run blocks, marking as rarely run\n", - block->bbNum); - } -#endif // DEBUG - - // When marking a BBJ_CALLFINALLY as rarely run we also mark - // the BBJ_ALWAYS that comes after it as rarely run - // - if (block->isBBCallAlwaysPair()) - { - BasicBlock* bNext = block->bbNext; - PREFIX_ASSUME(bNext != nullptr); - bNext->bbSetRunRarely(); -#ifdef DEBUG - if (verbose) - { - printf("Also marking the BBJ_ALWAYS at " FMT_BB " as rarely run\n", bNext->bbNum); - } -#endif // DEBUG - } - } - } - - /* COMPACT blocks if possible */ - if (bPrev->bbJumpKind == BBJ_NONE) - { - if (fgCanCompactBlocks(bPrev, block)) - { - fgCompactBlocks(bPrev, block); - - block = bPrev; - continue; - } - } - // - // if bPrev->bbWeight is not based upon profile data we can adjust - // the weights of bPrev and block - // - else if (bPrev->isBBCallAlwaysPair() && // we must have a BBJ_CALLFINALLY and BBK_ALWAYS pair - (bPrev->bbWeight != block->bbWeight) && // the weights are currently different - !bPrev->hasProfileWeight()) // and the BBJ_CALLFINALLY block is not using profiled - // weights - { - if (block->isRunRarely()) - { - bPrev->bbWeight = - block->bbWeight; // the BBJ_CALLFINALLY block now has the same weight as the BBJ_ALWAYS block - bPrev->bbFlags |= BBF_RUN_RARELY; // and is now rarely run -#ifdef DEBUG - if (verbose) - { - printf("Marking the BBJ_CALLFINALLY block at " FMT_BB " as rarely run because " FMT_BB - " is rarely run\n", - bPrev->bbNum, block->bbNum); - } -#endif // DEBUG - } - else if (bPrev->isRunRarely()) - { - block->bbWeight = - bPrev->bbWeight; // the BBJ_ALWAYS block now has the same weight as the BBJ_CALLFINALLY block - block->bbFlags |= BBF_RUN_RARELY; // and is now rarely run -#ifdef DEBUG - if (verbose) - { - printf("Marking the BBJ_ALWAYS block at " FMT_BB " as rarely run because " FMT_BB - " is rarely run\n", - block->bbNum, bPrev->bbNum); - } -#endif // DEBUG - } - else // Both blocks are hot, bPrev is known not to be using profiled weight - { - bPrev->bbWeight = - block->bbWeight; // the BBJ_CALLFINALLY block now has the same weight as the BBJ_ALWAYS block - } - noway_assert(block->bbWeight == bPrev->bbWeight); - } - } - - return result; -} - #if defined(FEATURE_EH_FUNCLETS) /***************************************************************************** diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index 9ce6456a391cc..c20f7d6bd7416 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -4094,10 +4094,6 @@ void GCInfo::gcMakeRegPtrTable( ************************************************************************** */ - unsigned count = 0; - - int lastoffset = 0; - /* Count&Write untracked locals and non-enregistered args */ unsigned varNum; @@ -4324,8 +4320,6 @@ void GCInfo::gcMakeRegPtrTable( for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr; genRegPtrTemp = genRegPtrTemp->rpdNext) { - int nextOffset = genRegPtrTemp->rpdOffs; - if (genRegPtrTemp->rpdArg) { if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ec5352b1338f1..ad09a52f0e157 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -230,14 +230,19 @@ void GenTree::InitNodeSize() // Now set all of the appropriate entries to 'large' CLANG_FORMAT_COMMENT_ANCHOR; -// clang-format off -#if defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) - // On ARM32, ARM64 and System V for struct returning - // there is code that does GT_ASG-tree.CopyObj call. - // CopyObj is a large node and the GT_ASG is small, which triggers an exception. - GenTree::s_gtNodeSizes[GT_ASG] = TREE_NODE_SZ_LARGE; - GenTree::s_gtNodeSizes[GT_RETURN] = TREE_NODE_SZ_LARGE; -#endif // defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) + // clang-format off + if (GlobalJitOptions::compFeatureHfa +#if defined(UNIX_AMD64_ABI) + || true +#endif // defined(UNIX_AMD64_ABI) + ) + { + // On ARM32, ARM64 and System V for struct returning + // there is code that does GT_ASG-tree.CopyObj call. + // CopyObj is a large node and the GT_ASG is small, which triggers an exception. + GenTree::s_gtNodeSizes[GT_ASG] = TREE_NODE_SZ_LARGE; + GenTree::s_gtNodeSizes[GT_RETURN] = TREE_NODE_SZ_LARGE; + } GenTree::s_gtNodeSizes[GT_CALL] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_CAST] = TREE_NODE_SZ_LARGE; @@ -3516,8 +3521,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case GT_CNS_DBL: { - var_types targetType = tree->TypeGet(); - level = 0; + level = 0; #if defined(TARGET_XARCH) /* We use fldz and fld1 to load 0.0 and 1.0, but all other */ /* floating point constants are loaded using an indirection */ @@ -3533,6 +3537,7 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) costSz = 4; } #elif defined(TARGET_ARM) + var_types targetType = tree->TypeGet(); if (targetType == TYP_FLOAT) { costEx = 1 + 2; @@ -6102,6 +6107,12 @@ GenTree* Compiler::gtNewIndOfIconHandleNode(var_types indType, size_t addr, unsi // This indirection also is invariant. indNode->gtFlags |= GTF_IND_INVARIANT; + + if (iconFlags == GTF_ICON_STR_HDL) + { + // String literals are never null + indNode->gtFlags |= GTF_IND_NONNULL; + } } return indNode; } @@ -10005,8 +10016,15 @@ void Compiler::gtDispNodeName(GenTree* tree) switch (tree->AsBoundsChk()->gtThrowKind) { case SCK_RNGCHK_FAIL: - sprintf_s(bufp, sizeof(buf), " %s_Rng", name); + { + bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), " %s_Rng", name); + if (tree->AsBoundsChk()->gtIndRngFailBB != nullptr) + { + bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), " -> " FMT_BB, + tree->AsBoundsChk()->gtIndRngFailBB->bbNum); + } break; + } case SCK_ARG_EXCPN: sprintf_s(bufp, sizeof(buf), " %s_Arg", name); break; @@ -10102,9 +10120,7 @@ void Compiler::gtDispCommonEndLine(GenTree* tree) void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z __in_opt const char* msg, bool isLIR) { - bool printPointer = true; // always true.. - bool printFlags = true; // always true.. - bool printCost = true; // always true.. + bool printFlags = true; // always true.. int msgLength = 25; @@ -10249,6 +10265,12 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ --msgLength; break; } + if (tree->gtFlags & GTF_IND_NONNULL) + { + printf("@"); + --msgLength; + break; + } } FALLTHROUGH; @@ -10588,7 +10610,6 @@ void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z _ { // Promoted implicit by-refs can have this state during // global morph while they are being rewritten - assert(fgGlobalMorph); printf("(P?!)"); // Promoted struct } } @@ -11288,7 +11309,6 @@ void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) { // Promoted implicit byrefs can get in this state while they are being rewritten // in global morph. - assert(fgGlobalMorph); } else { @@ -16964,8 +16984,6 @@ bool GenTree::isContained() const // They can only produce a result if the child is a SIMD equality comparison. else if (OperKind() & GTK_RELOP) { - // We have to cast away const-ness since AsOp() method is non-const. - const GenTree* childNode = AsOp()->gtGetOp1(); assert(isMarkedContained == false); } @@ -17257,8 +17275,7 @@ bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pObj, GenTree** pStatic, Fie // // The CSE could be a pointer to a boxed struct // - GenTreeLclVarCommon* lclVar = AsLclVarCommon(); - ValueNum vn = gtVNPair.GetLiberal(); + ValueNum vn = gtVNPair.GetLiberal(); if (vn != ValueNumStore::NoVN) { // Is the ValueNum a MapSelect involving a SharedStatic helper? @@ -17853,7 +17870,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, b case GT_CALL: { GenTreeCall* call = tree->AsCall(); - if (call->gtFlags & CORINFO_FLG_JIT_INTRINSIC) + if (call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) { NamedIntrinsic ni = lookupNamedIntrinsic(call->gtCallMethHnd); if ((ni == NI_System_Array_Clone) || (ni == NI_System_Object_MemberwiseClone)) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index e4ed857e1322e..6b3ff34518c0f 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -866,10 +866,11 @@ struct GenTree // alignment of 1 byte) #define GTF_IND_INVARIANT 0x01000000 // GT_IND -- the target is invariant (a prejit indirection) #define GTF_IND_ARR_INDEX 0x00800000 // GT_IND -- the indirection represents an (SZ) array index +#define GTF_IND_NONNULL 0x00400000 // GT_IND -- the indirection never returns null (zero) #define GTF_IND_FLAGS \ (GTF_IND_VOLATILE | GTF_IND_TGTANYWHERE | GTF_IND_NONFAULTING | GTF_IND_TLS_REF | \ - GTF_IND_UNALIGNED | GTF_IND_INVARIANT | GTF_IND_ARR_INDEX | GTF_IND_TGT_NOT_HEAP) + GTF_IND_UNALIGNED | GTF_IND_INVARIANT | GTF_IND_NONNULL | GTF_IND_ARR_INDEX | GTF_IND_TGT_NOT_HEAP) #define GTF_CLS_VAR_VOLATILE 0x40000000 // GT_FIELD/GT_CLS_VAR -- same as GTF_IND_VOLATILE #define GTF_CLS_VAR_INITCLASS 0x20000000 // GT_FIELD/GT_CLS_VAR -- same as GTF_FLD_INITCLASS @@ -1087,6 +1088,17 @@ struct GenTree return TypeIs(type) || TypeIs(rest...); } + static bool StaticOperIs(genTreeOps operCompare, genTreeOps oper) + { + return operCompare == oper; + } + + template + static bool StaticOperIs(genTreeOps operCompare, genTreeOps oper, T... rest) + { + return StaticOperIs(operCompare, oper) || StaticOperIs(operCompare, rest...); + } + bool OperIs(genTreeOps oper) const { return OperGet() == oper; diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 2f589a4db9c83..dee63b2c99d69 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -416,8 +416,7 @@ GenTree* HWIntrinsicInfo::lookupLastOp(const GenTreeHWIntrinsic* node) { assert(node != nullptr); - NamedIntrinsic id = node->gtHWIntrinsicId; - GenTree* op1 = node->gtGetOp1(); + GenTree* op1 = node->gtGetOp1(); if (op1 == nullptr) { @@ -778,11 +777,10 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_SIG_INFO* sig, bool mustExpand) { - CORINFO_InstructionSet isa = HWIntrinsicInfo::lookupIsa(intrinsic); - HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic); - int numArgs = sig->numArgs; - var_types retType = JITtype2varType(sig->retType); - var_types baseType = TYP_UNKNOWN; + HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsic); + int numArgs = sig->numArgs; + var_types retType = JITtype2varType(sig->retType); + var_types baseType = TYP_UNKNOWN; if ((retType == TYP_STRUCT) && featureSIMD) { diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index 49858f9aa2d0b..e5ae7fa13b445 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -89,11 +89,10 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) if (genIsTableDrivenHWIntrinsic(intrinsicId, category)) { - GenTree* op1 = node->gtGetOp1(); - GenTree* op2 = node->gtGetOp2(); - regNumber targetReg = node->GetRegNum(); - var_types targetType = node->TypeGet(); - var_types baseType = node->gtSIMDBaseType; + GenTree* op1 = node->gtGetOp1(); + GenTree* op2 = node->gtGetOp2(); + regNumber targetReg = node->GetRegNum(); + var_types baseType = node->gtSIMDBaseType; regNumber op1Reg = REG_NA; regNumber op2Reg = REG_NA; @@ -549,11 +548,9 @@ void CodeGen::genHWIntrinsic_R_RM( // void CodeGen::genHWIntrinsic_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, int8_t ival) { - var_types targetType = node->TypeGet(); - regNumber targetReg = node->GetRegNum(); - GenTree* op1 = node->gtGetOp1(); - emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->gtSIMDSize)); - emitter* emit = GetEmitter(); + regNumber targetReg = node->GetRegNum(); + GenTree* op1 = node->gtGetOp1(); + emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->gtSIMDSize)); // TODO-XArch-CQ: Commutative operations can have op1 be contained // TODO-XArch-CQ: Non-VEX encoded instructions can have both ops contained @@ -630,12 +627,11 @@ void CodeGen::genHWIntrinsic_R_R_RM( // void CodeGen::genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, int8_t ival) { - var_types targetType = node->TypeGet(); - regNumber targetReg = node->GetRegNum(); - GenTree* op1 = node->gtGetOp1(); - GenTree* op2 = node->gtGetOp2(); - emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->gtSIMDSize)); - emitter* emit = GetEmitter(); + regNumber targetReg = node->GetRegNum(); + GenTree* op1 = node->gtGetOp1(); + GenTree* op2 = node->gtGetOp2(); + emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->gtSIMDSize)); + emitter* emit = GetEmitter(); // TODO-XArch-CQ: Commutative operations can have op1 be contained // TODO-XArch-CQ: Non-VEX encoded instructions can have both ops contained @@ -795,13 +791,12 @@ void CodeGen::genHWIntrinsic_R_R_RM_I(GenTreeHWIntrinsic* node, instruction ins, // void CodeGen::genHWIntrinsic_R_R_RM_R(GenTreeHWIntrinsic* node, instruction ins) { - var_types targetType = node->TypeGet(); - regNumber targetReg = node->GetRegNum(); - GenTree* op1 = node->gtGetOp1(); - GenTree* op2 = node->gtGetOp2(); - GenTree* op3 = nullptr; - emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->gtSIMDSize)); - emitter* emit = GetEmitter(); + regNumber targetReg = node->GetRegNum(); + GenTree* op1 = node->gtGetOp1(); + GenTree* op2 = node->gtGetOp2(); + GenTree* op3 = nullptr; + emitAttr simdSize = emitActualTypeSize(Compiler::getSIMDTypeForSize(node->gtSIMDSize)); + emitter* emit = GetEmitter(); assert(op1->OperIsList()); assert(op2 == nullptr); @@ -1095,7 +1090,6 @@ void CodeGen::genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsi BasicBlock* jmpTable[256]; unsigned jmpTableBase = emit->emitBBTableDataGenBeg(maxByte, true); - unsigned jmpTableOffs = 0; // Emit the jump table for (unsigned i = 0; i < maxByte; i++) @@ -1144,7 +1138,6 @@ void CodeGen::genBaseIntrinsic(GenTreeHWIntrinsic* node) { NamedIntrinsic intrinsicId = node->gtHWIntrinsicId; regNumber targetReg = node->GetRegNum(); - var_types targetType = node->TypeGet(); var_types baseType = node->gtSIMDBaseType; assert(compiler->compIsaSupportedDebugOnly(InstructionSet_SSE)); @@ -1333,16 +1326,11 @@ void CodeGen::genSSEIntrinsic(GenTreeHWIntrinsic* node) NamedIntrinsic intrinsicId = node->gtHWIntrinsicId; GenTree* op1 = node->gtGetOp1(); GenTree* op2 = node->gtGetOp2(); - GenTree* op3 = nullptr; - GenTree* op4 = nullptr; regNumber targetReg = node->GetRegNum(); var_types targetType = node->TypeGet(); var_types baseType = node->gtSIMDBaseType; regNumber op1Reg = REG_NA; - regNumber op2Reg = REG_NA; - regNumber op3Reg = REG_NA; - regNumber op4Reg = REG_NA; emitter* emit = GetEmitter(); genConsumeHWIntrinsicOperands(node); @@ -1418,7 +1406,6 @@ void CodeGen::genSSE2Intrinsic(GenTreeHWIntrinsic* node) var_types targetType = node->TypeGet(); var_types baseType = node->gtSIMDBaseType; regNumber op1Reg = REG_NA; - regNumber op2Reg = REG_NA; emitter* emit = GetEmitter(); genConsumeHWIntrinsicOperands(node); @@ -1520,17 +1507,10 @@ void CodeGen::genSSE41Intrinsic(GenTreeHWIntrinsic* node) NamedIntrinsic intrinsicId = node->gtHWIntrinsicId; GenTree* op1 = node->gtGetOp1(); GenTree* op2 = node->gtGetOp2(); - GenTree* op3 = nullptr; - GenTree* op4 = nullptr; regNumber targetReg = node->GetRegNum(); - var_types targetType = node->TypeGet(); var_types baseType = node->gtSIMDBaseType; - regNumber op1Reg = REG_NA; - regNumber op2Reg = REG_NA; - regNumber op3Reg = REG_NA; - regNumber op4Reg = REG_NA; - emitter* emit = GetEmitter(); + emitter* emit = GetEmitter(); genConsumeHWIntrinsicOperands(node); diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index f6558fbb96708..02e0e17548a76 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -1459,17 +1459,12 @@ GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HAN GenTree* retNode = nullptr; GenTree* op1 = nullptr; GenTree* op2 = nullptr; - int ival = -1; int simdSize = HWIntrinsicInfo::lookupSimdSize(this, intrinsic, sig); var_types baseType = getBaseTypeOfSIMDType(sig->retTypeSigClass); - var_types retType = TYP_UNKNOWN; // The fencing intrinsics don't take any operands and simdSize is 0 assert((simdSize == 16) || (simdSize == 0)); - CORINFO_ARG_LIST_HANDLE argList = sig->args; - var_types argType = TYP_UNKNOWN; - switch (intrinsic) { case NI_SSE2_CompareScalarGreaterThan: diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 0b083746808c4..1a1224d096d62 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1311,7 +1311,41 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, { if (callConvIsInstanceMethodCallConv(srcCall->GetUnmanagedCallConv())) { +#ifdef TARGET_X86 + // The argument list has already been reversed. + // Insert the return buffer as the second-to-last node + // so it will be pushed on to the stack after the user args but before the native this arg + // as required by the native ABI. + GenTreeCall::Use* lastArg = srcCall->gtCallArgs; + if (lastArg == nullptr) + { + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs); + } + else if (srcCall->GetUnmanagedCallConv() == CorInfoCallConvExtension::Thiscall) + { + // For thiscall, the "this" parameter is not included in the argument list reversal, + // so we need to put the return buffer as the last parameter. + for (; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext()) + ; + gtInsertNewCallArgAfter(destAddr, lastArg); + } + else if (lastArg->GetNext() == nullptr) + { + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, lastArg); + } + else + { + assert(lastArg != nullptr && lastArg->GetNext() != nullptr); + GenTreeCall::Use* secondLastArg = lastArg; + lastArg = lastArg->GetNext(); + for (; lastArg->GetNext() != nullptr; secondLastArg = lastArg, lastArg = lastArg->GetNext()) + ; + assert(secondLastArg->GetNext() != nullptr); + gtInsertNewCallArgAfter(destAddr, secondLastArg); + } +#else GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); +#endif } else { @@ -2800,7 +2834,7 @@ BasicBlock* Compiler::impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_H /* Create extra basic block for the spill */ BasicBlock* newBlk = fgNewBBbefore(BBJ_NONE, hndBlk, /* extendRegion */ true); newBlk->bbFlags |= BBF_IMPORTED | BBF_DONT_REMOVE | BBF_HAS_LABEL | BBF_JMP_TARGET; - newBlk->setBBWeight(hndBlk->bbWeight); + newBlk->inheritWeight(hndBlk); newBlk->bbCodeOffs = hndBlk->bbCodeOffs; /* Account for the new link we are about to create */ @@ -3075,7 +3109,7 @@ unsigned Compiler::impInitBlockLineInfo() /*****************************************************************************/ -static inline bool impOpcodeIsCallOpcode(OPCODE opcode) +bool Compiler::impOpcodeIsCallOpcode(OPCODE opcode) { switch (opcode) { @@ -4323,7 +4357,7 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, if (opts.OptimizationEnabled() && impStackTop().val->OperIs(GT_RET_EXPR)) { GenTreeCall* call = impStackTop().val->AsRetExpr()->gtInlineCandidate->AsCall(); - if (call->gtFlags & CORINFO_FLG_JIT_INTRINSIC) + if (call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) { if (lookupNamedIntrinsic(call->gtCallMethHnd) == NI_System_Threading_Thread_get_CurrentThread) { @@ -4458,10 +4492,13 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, break; } + case NI_System_Array_Clone: case NI_System_Collections_Generic_Comparer_get_Default: case NI_System_Collections_Generic_EqualityComparer_get_Default: + case NI_System_Object_MemberwiseClone: + case NI_System_Threading_Thread_get_CurrentThread: { - // Flag for later handling during devirtualization. + // Flag for later handling. isSpecial = true; break; } @@ -5321,22 +5358,20 @@ bool Compiler::verMergeEntryStates(BasicBlock* block, bool* changed) /***************************************************************************** * 'logMsg' is true if a log message needs to be logged. false if the caller has * already logged it (presumably in a more detailed fashion than done here) - * 'bVerificationException' is true for a verification exception, false for a - * "call unauthorized by host" exception. */ void Compiler::verConvertBBToThrowVerificationException(BasicBlock* block DEBUGARG(bool logMsg)) { block->bbJumpKind = BBJ_THROW; block->bbFlags |= BBF_FAILED_VERIFICATION; + block->bbFlags &= ~BBF_IMPORTED; impCurStmtOffsSet(block->bbCodeOffs); -#ifdef DEBUG - // we need this since BeginTreeList asserts otherwise + // Clear the statement list as it exists so far; we're only going to have a verification exception. impStmtList = impLastStmt = nullptr; - block->bbFlags &= ~BBF_IMPORTED; +#ifdef DEBUG if (logMsg) { JITLOG((LL_ERROR, "Verification failure: while compiling %s near IL offset %x..%xh \n", info.compFullName, @@ -6889,8 +6924,7 @@ void Compiler::impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORI // pass number of arguments to the helper args = gtPrependNewCallArg(gtNewIconNode(pCallInfo->sig.numArgs), args); - unsigned argFlags = 0; - args = impPopCallArgs(pCallInfo->sig.numArgs, &pCallInfo->sig, args); + args = impPopCallArgs(pCallInfo->sig.numArgs, &pCallInfo->sig, args); node = gtNewHelperCallNode(CORINFO_HELP_NEW_MDARR, TYP_REF, args); @@ -6957,8 +6991,7 @@ GenTree* Compiler::impTransformThis(GenTree* thisPtr, obj = gtNewObjNode(pConstrainedResolvedToken->hClass, obj); obj->gtFlags |= GTF_EXCEPT; - CorInfoType jitTyp = info.compCompHnd->asCorInfoType(pConstrainedResolvedToken->hClass); - var_types objType = JITtype2varType(jitTyp); + CorInfoType jitTyp = info.compCompHnd->asCorInfoType(pConstrainedResolvedToken->hClass); if (impIsPrimitive(jitTyp)) { if (obj->OperIsBlk()) @@ -7152,8 +7185,11 @@ void Compiler::impCheckForPInvokeCall( call->gtCallMoreFlags |= GTF_CALL_M_SUPPRESS_GC_TRANSITION; } - if (unmanagedCallConv != CorInfoCallConvExtension::C && unmanagedCallConv != CorInfoCallConvExtension::Stdcall && - unmanagedCallConv != CorInfoCallConvExtension::Thiscall) + // If we can't get the unmanaged calling convention or the calling convention is unsupported in the JIT, + // return here without inlining the native call. + if (unmanagedCallConv == CorInfoCallConvExtension::Managed || + unmanagedCallConv == CorInfoCallConvExtension::Fastcall || + unmanagedCallConv == CorInfoCallConvExtension::FastcallMemberFunction) { return; } @@ -7217,7 +7253,8 @@ void Compiler::impCheckForPInvokeCall( } // AMD64 convention is same for native and managed - if (unmanagedCallConv == CorInfoCallConvExtension::C) + if (unmanagedCallConv == CorInfoCallConvExtension::C || + unmanagedCallConv == CorInfoCallConvExtension::CMemberFunction) { call->gtFlags |= GTF_CALL_POP_ARGS; } @@ -7812,21 +7849,6 @@ bool Compiler::impTailCallRetTypeCompatible(var_types callerRetTy return false; } -// For prefixFlags -enum -{ - PREFIX_TAILCALL_EXPLICIT = 0x00000001, // call has "tail" IL prefix - PREFIX_TAILCALL_IMPLICIT = - 0x00000010, // call is treated as having "tail" prefix even though there is no "tail" IL prefix - PREFIX_TAILCALL_STRESS = - 0x00000100, // call doesn't "tail" IL prefix but is treated as explicit because of tail call stress - PREFIX_TAILCALL = (PREFIX_TAILCALL_EXPLICIT | PREFIX_TAILCALL_IMPLICIT | PREFIX_TAILCALL_STRESS), - PREFIX_VOLATILE = 0x00001000, - PREFIX_UNALIGNED = 0x00010000, - PREFIX_CONSTRAINED = 0x00100000, - PREFIX_READONLY = 0x01000000 -}; - /******************************************************************************** * * Returns true if the current opcode and and the opcodes following it correspond @@ -7956,7 +7978,6 @@ var_types Compiler::impImportCall(OPCODE opcode, CORINFO_CLASS_HANDLE clsHnd = nullptr; unsigned clsFlags = 0; unsigned mflags = 0; - unsigned argFlags = 0; GenTree* call = nullptr; GenTreeCall::Use* args = nullptr; CORINFO_THIS_TRANSFORM constraintCallThisTransform = CORINFO_NO_THIS_TRANSFORM; @@ -9410,7 +9431,6 @@ var_types Compiler::impImportJitTestLabelMark(int numArgs) // a GT_IND of a static field address, which should be the sum of a (hoistable) helper call and possibly some // offset within the the static field block whose address is returned by the helper call. // The annotation is saying that this address calculation, but not the entire access, should be hoisted. - GenTree* helperCall = nullptr; assert(node->OperGet() == GT_IND); tlAndN.m_num -= 100; GetNodeTestData()->Set(node->AsOp()->gtOp1, tlAndN); @@ -9719,7 +9739,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } -#endif // FEATURE_MULTIREG_RET && FEATURE_HFA +#endif // FEATURE_MULTIREG_RET && TARGET_ARM64 if (!compDoOldStructRetyping() && (!op->IsCall() || !op->AsCall()->TreatAsHasRetBufArg(this))) { @@ -10298,7 +10318,7 @@ void Compiler::impImportLeave(BasicBlock* block) BasicBlock* step2 = fgNewBBinRegion(BBJ_ALWAYS, XTnum + 1, 0, step); step->bbJumpDest = step2; step->bbJumpDest->bbRefs++; - step2->setBBWeight(block->bbWeight); + step2->inheritWeight(block); step2->bbFlags |= (block->bbFlags & BBF_RUN_RARELY) | BBF_IMPORTED; #ifdef DEBUG @@ -10577,7 +10597,7 @@ void Compiler::impResetLeaveBlock(BasicBlock* block, unsigned jmpAddr) // d) as internal // e) as rarely run dupBlock->bbRefs = 0; - dupBlock->bbWeight = 0; + dupBlock->bbWeight = BB_ZERO_WEIGHT; dupBlock->bbFlags |= BBF_IMPORTED | BBF_INTERNAL | BBF_RUN_RARELY; // Insert the block right after the block which is getting reset so that BBJ_CALLFINALLY and BBJ_ALWAYS @@ -10608,7 +10628,7 @@ void Compiler::impResetLeaveBlock(BasicBlock* block, unsigned jmpAddr) // Get the first non-prefix opcode. Used for verification of valid combinations // of prefixes and actual opcodes. -static OPCODE impGetNonPrefixOpcode(const BYTE* codeAddr, const BYTE* codeEndp) +OPCODE Compiler::impGetNonPrefixOpcode(const BYTE* codeAddr, const BYTE* codeEndp) { while (codeAddr < codeEndp) { @@ -10646,7 +10666,7 @@ static OPCODE impGetNonPrefixOpcode(const BYTE* codeAddr, const BYTE* codeEndp) /*****************************************************************************/ // Checks whether the opcode is a valid opcode for volatile. and unaligned. prefixes -static void impValidateMemoryAccessOpcode(const BYTE* codeAddr, const BYTE* codeEndp, bool volatilePrefix) +void Compiler::impValidateMemoryAccessOpcode(const BYTE* codeAddr, const BYTE* codeEndp, bool volatilePrefix) { OPCODE opcode = impGetNonPrefixOpcode(codeAddr, codeEndp); @@ -10866,7 +10886,6 @@ GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_T bool isExact = false; bool isNonNull = false; CORINFO_CLASS_HANDLE fromClass = gtGetClassHandle(op1, &isExact, &isNonNull); - GenTree* optResult = nullptr; if (fromClass != nullptr) { @@ -11267,7 +11286,9 @@ void Compiler::impImportBlockCode(BasicBlock* block) while (codeAddr < codeEndp) { - bool usingReadyToRunHelper = false; +#ifdef FEATURE_READYTORUN_COMPILER + bool usingReadyToRunHelper = false; +#endif CORINFO_RESOLVED_TOKEN resolvedToken; CORINFO_RESOLVED_TOKEN constrainedResolvedToken; CORINFO_CALL_INFO callInfo; @@ -19775,7 +19796,7 @@ void Compiler::impInlineInitVars(InlineInfo* pInlineInfo) return; } } - else if (genTypeSize(sigType) < EA_PTRSIZE) + else if (genTypeSize(sigType) < TARGET_POINTER_SIZE) { // Narrowing cast. if (inlArgNode->OperIs(GT_LCL_VAR)) @@ -20911,11 +20932,10 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, } // See what we know about the type of 'this' in the call. - GenTree* thisObj = call->gtCallThisArg->GetNode()->gtEffectiveVal(false); - GenTree* actualThisObj = nullptr; - bool isExact = false; - bool objIsNonNull = false; - CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); + GenTree* thisObj = call->gtCallThisArg->GetNode()->gtEffectiveVal(false); + bool isExact = false; + bool objIsNonNull = false; + CORINFO_CLASS_HANDLE objClass = gtGetClassHandle(thisObj, &isExact, &objIsNonNull); // Bail if we know nothing. if (objClass == nullptr) @@ -21281,7 +21301,7 @@ void Compiler::impDevirtualizeCall(GenTreeCall* call, derivedResolvedToken.tokenScope = info.compCompHnd->getMethodModule(derivedMethod); derivedResolvedToken.tokenContext = *pContextHandle; derivedResolvedToken.token = info.compCompHnd->getMethodDefFromMethod(derivedMethod); - derivedResolvedToken.tokenType = CORINFO_TOKENKIND_Method; + derivedResolvedToken.tokenType = CORINFO_TOKENKIND_DevirtualizedMethod; derivedResolvedToken.hClass = derivedClass; derivedResolvedToken.hMethod = derivedMethod; @@ -21504,6 +21524,15 @@ void Compiler::considerGuardedDevirtualization( JITDUMP("Considering guarded devirtualization\n"); + // We currently only get likely class guesses when there is PGO data + // with class profiles. + // + if (fgPgoClassProfiles == 0) + { + JITDUMP("Not guessing for class: no class profile pgo data, or pgo disabled\n"); + return; + } + // See if there's a likely guess for the class. // const unsigned likelihoodThreshold = isInterface ? 25 : 30; diff --git a/src/coreclr/jit/indirectcalltransformer.cpp b/src/coreclr/jit/indirectcalltransformer.cpp index a26db2c4531c7..8699d898cf368 100644 --- a/src/coreclr/jit/indirectcalltransformer.cpp +++ b/src/coreclr/jit/indirectcalltransformer.cpp @@ -436,7 +436,6 @@ class IndirectCallTransformer Statement* CreateFatCallStmt(GenTree* actualCallAddress, GenTree* hiddenArgument) { Statement* fatStmt = compiler->gtCloneStmt(stmt); - GenTree* fatTree = fatStmt->GetRootNode(); GenTreeCall* fatCall = GetCall(fatStmt); fatCall->gtCallAddr = actualCallAddress; AddHiddenArgument(fatCall, hiddenArgument); @@ -702,7 +701,7 @@ class IndirectCallTransformer call->gtCallThisArg = compiler->gtNewCallArgs(compiler->gtNewLclvNode(thisTemp, TYP_REF)); call->SetIsGuarded(); - JITDUMP("Direct call [%06u] in block BB%02u\n", compiler->dspTreeID(call), thenBlock->bbNum); + JITDUMP("Direct call [%06u] in block " FMT_BB "\n", compiler->dspTreeID(call), thenBlock->bbNum); // Then invoke impDevirtualizeCall to actually // transform the call for us. It should succeed.... as we have @@ -767,7 +766,7 @@ class IndirectCallTransformer call->gtFlags &= ~GTF_CALL_INLINE_CANDIDATE; call->SetIsGuarded(); - JITDUMP("Residual call [%06u] moved to block BB%02u\n", compiler->dspTreeID(call), elseBlock->bbNum); + JITDUMP("Residual call [%06u] moved to block " FMT_BB "\n", compiler->dspTreeID(call), elseBlock->bbNum); if (returnTemp != BAD_VAR_NUM) { @@ -867,7 +866,6 @@ class IndirectCallTransformer { GenTree* tree = callStmt->GetRootNode(); assert(tree->OperIs(GT_ASG)); - GenTreeOp* asg = tree->AsOp(); GenTreeCall* call = tree->gtGetOp2()->AsCall(); return call; } diff --git a/src/coreclr/jit/inline.cpp b/src/coreclr/jit/inline.cpp index a2eee7dce1afd..828ad60767660 100644 --- a/src/coreclr/jit/inline.cpp +++ b/src/coreclr/jit/inline.cpp @@ -1456,8 +1456,7 @@ void InlineStrategy::DumpDataContents(FILE* file) DumpDataEnsurePolicyIsSet(); // Cache references to compiler substructures. - const Compiler::Info& info = m_Compiler->info; - const Compiler::Options& opts = m_Compiler->opts; + const Compiler::Info& info = m_Compiler->info; // We'd really like the method identifier to be unique and // durable across crossgen invocations. Not clear how to @@ -1472,7 +1471,7 @@ void InlineStrategy::DumpDataContents(FILE* file) unsigned __int64 compCycles = m_Compiler->getInlineCycleCount(); if (compCycles > 0) { - double countsPerSec = CycleTimer::CyclesPerSecond(); + double countsPerSec = CachedCyclesPerSecond(); double counts = (double)compCycles; microsecondsSpentJitting = (unsigned)((counts / countsPerSec) * 1000 * 1000); } @@ -1567,7 +1566,7 @@ void InlineStrategy::DumpXml(FILE* file, unsigned indent) unsigned __int64 compCycles = m_Compiler->getInlineCycleCount(); if (compCycles > 0) { - double countsPerSec = CycleTimer::CyclesPerSecond(); + double countsPerSec = CachedCyclesPerSecond(); double counts = (double)compCycles; microsecondsSpentJitting = (unsigned)((counts / countsPerSec) * 1000 * 1000); } diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 4c0a5bf63487d..24fac11b945d0 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -25,25 +25,6 @@ // break. Replacing those instances with ZERO avoids this change #define ZERO 0 -#ifdef _MSC_VER -// These don't seem useful, so turning them off is no big deal -#pragma warning(disable : 4065) // "switch statement contains 'default' but no 'case' labels" (happens due to #ifdefs) -#pragma warning(disable : 4510) // can't generate default constructor -#pragma warning(disable : 4511) // can't generate copy constructor -#pragma warning(disable : 4512) // can't generate assignment constructor -#pragma warning(disable : 4610) // user defined constructor required -#pragma warning(disable : 4211) // nonstandard extension used (char name[0] in structs) -#pragma warning(disable : 4127) // conditional expression constant -#pragma warning(disable : 4201) // "nonstandard extension used : nameless struct/union" - -// Depending on the code base, you may want to not disable these -#pragma warning(disable : 4245) // assigning signed / unsigned -#pragma warning(disable : 4146) // unary minus applied to unsigned - -#pragma warning(disable : 4100) // unreferenced formal parameter -#pragma warning(disable : 4291) // new operator without delete (only in emitX86.cpp) -#endif - #ifdef _MSC_VER #define CHECK_STRUCT_PADDING 0 // Set this to '1' to enable warning C4820 "'bytes' bytes padding added after // construct 'member_name'" on interesting structs/classes @@ -386,6 +367,27 @@ typedef ptrdiff_t ssize_t; #define FLD_GLOBAL_DS ((CORINFO_FIELD_HANDLE)-4) #define FLD_GLOBAL_FS ((CORINFO_FIELD_HANDLE)-8) +class GlobalJitOptions +{ +public: +#ifdef FEATURE_HFA +#define FEATURE_HFA_FIELDS_PRESENT +#ifdef CONFIGURABLE_ARM_ABI + // These are safe to have globals as they cannot change once initialized within the process. + static LONG compUseSoftFPConfigured; + static bool compFeatureHfa; +#else // !CONFIGURABLE_ARM_ABI + static const bool compFeatureHfa = true; +#endif // CONFIGURABLE_ARM_ABI +#else // !FEATURE_HFA + static const bool compFeatureHfa = false; +#endif // FEATURE_HFA + +#ifdef FEATURE_HFA +#undef FEATURE_HFA +#endif +}; + /*****************************************************************************/ #include "vartype.h" @@ -508,7 +510,7 @@ typedef ptrdiff_t ssize_t; #if DUMP_GC_TABLES #pragma message("NOTE: this non-debug build has GC ptr table dumping always enabled!") -const bool dspGCtbls = true; +const bool dspGCtbls = true; #endif /*****************************************************************************/ @@ -753,6 +755,8 @@ class Histogram #ifdef TARGET_XARCH #define FEATURE_LOOP_ALIGN 1 +#else +#define FEATURE_LOOP_ALIGN 0 #endif #define CLFLG_MAXOPT \ diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 478aa4d8d0bd8..055750272e335 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -306,6 +306,10 @@ CONFIG_INTEGER(EnableArm64Sm4, W("EnableArm64Sm4"), 1) CONFIG_INTEGER(EnableArm64Sve, W("EnableArm64Sve"), 1) #endif // defined(TARGET_ARM64) +#if defined(CONFIGURABLE_ARM_ABI) +CONFIG_INTEGER(JitSoftFP, W("JitSoftFP"), 0) +#endif // defined(CONFIGURABLE_ARM_ABI) + // clang-format on #ifdef FEATURE_SIMD @@ -458,6 +462,12 @@ CONFIG_INTEGER(JitMinimalPrejitProfiling, W("JitMinimalPrejitProfiling"), 0) CONFIG_INTEGER(JitClassProfiling, W("JitClassProfiling"), 1) CONFIG_INTEGER(JitEdgeProfiling, W("JitEdgeProfiling"), 1) +// Profile consumption options +CONFIG_INTEGER(JitDisablePgo, W("JitDisablePgo"), 0) // Ignore pgo data for all methods +#if defined(DEBUG) +CONFIG_STRING(JitEnablePgoRange, W("JitEnablePgoRange")) // Enable pgo data for only some methods +#endif // debug + // Control when Virtual Calls are expanded CONFIG_INTEGER(JitExpandCallsEarly, W("JitExpandCallsEarly"), 1) // Expand Call targets early (in the global morph // phase) diff --git a/src/coreclr/jit/jitee.h b/src/coreclr/jit/jitee.h index 496da9edfb1cb..27149c824348a 100644 --- a/src/coreclr/jit/jitee.h +++ b/src/coreclr/jit/jitee.h @@ -81,7 +81,12 @@ class JitFlags JIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method +#if defined(TARGET_ARM) + JIT_FLAG_SOFTFP_ABI = 43, // On ARM should enable armel calling convention +#else // !defined(TARGET_ARM) JIT_FLAG_UNUSED16 = 43, +#endif // !defined(TARGET_ARM) + JIT_FLAG_UNUSED17 = 44, JIT_FLAG_UNUSED18 = 45, JIT_FLAG_UNUSED19 = 46, @@ -215,6 +220,13 @@ class JitFlags #endif // TARGET_ARM FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING, JIT_FLAG_NO_INLINING); + +#if defined(TARGET_ARM) + + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_SOFTFP_ABI, JIT_FLAG_SOFTFP_ABI); + +#endif // TARGET_ARM + #undef FLAGS_EQUAL } diff --git a/src/coreclr/jit/jitexpandarray.h b/src/coreclr/jit/jitexpandarray.h index 999fcdb683c25..a81b1caeae625 100644 --- a/src/coreclr/jit/jitexpandarray.h +++ b/src/coreclr/jit/jitexpandarray.h @@ -292,7 +292,7 @@ class JitExpandArrayStack : public JitExpandArray // Assumptions: // The stack must not be empty. // - T Top() + T Top() const { assert(Size() > 0); return this->m_members[m_used - 1]; @@ -328,7 +328,7 @@ class JitExpandArrayStack : public JitExpandArray // Assumptions: // The element index does not exceed the current stack depth. // - T GetNoExpand(unsigned idx) + T GetNoExpand(unsigned idx) const { assert(idx < m_used); return this->m_members[idx]; @@ -364,7 +364,7 @@ class JitExpandArrayStack : public JitExpandArray // Return Value: // The stack depth. // - unsigned Size() + unsigned Size() const { return m_used; } diff --git a/src/coreclr/jit/layout.cpp b/src/coreclr/jit/layout.cpp index 6318b56e195a4..3b07a5bada350 100644 --- a/src/coreclr/jit/layout.cpp +++ b/src/coreclr/jit/layout.cpp @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + #include "jitpch.h" #include "layout.h" #include "compiler.h" diff --git a/src/coreclr/jit/layout.h b/src/coreclr/jit/layout.h index 6a74949cfd214..cef2fe6ded3ab 100644 --- a/src/coreclr/jit/layout.h +++ b/src/coreclr/jit/layout.h @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + #ifndef LAYOUT_H #define LAYOUT_H diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index ca6e34a083e1c..f3c12658ee456 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -247,10 +247,13 @@ void Compiler::lvaInitTypeRef() break; case CorInfoCallConvExtension::C: case CorInfoCallConvExtension::Stdcall: + case CorInfoCallConvExtension::CMemberFunction: + case CorInfoCallConvExtension::StdcallMemberFunction: varDscInfo.Init(lvaTable, hasRetBuffArg, 0, 0); break; case CorInfoCallConvExtension::Managed: case CorInfoCallConvExtension::Fastcall: + case CorInfoCallConvExtension::FastcallMemberFunction: default: varDscInfo.Init(lvaTable, hasRetBuffArg, MAX_REG_ARG, MAX_FLOAT_REG_ARG); break; @@ -1400,11 +1403,12 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, varDsc->lvIsImplicitByRef = 0; #endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) -// Set the lvType (before this point it is TYP_UNDEF). + // Set the lvType (before this point it is TYP_UNDEF). -#ifdef FEATURE_HFA - varDsc->SetHfaType(TYP_UNDEF); -#endif + if (GlobalJitOptions::compFeatureHfa) + { + varDsc->SetHfaType(TYP_UNDEF); + } if ((varTypeIsStruct(type))) { lvaSetStruct(varNum, typeHnd, typeHnd != nullptr, !tiVerificationNeeded); @@ -2104,6 +2108,14 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) // TODO-1stClassStructs: a temporary solution to keep diffs small, it will be fixed later. shouldPromote = false; } +#if defined(DEBUG) + else if (compiler->compPromoteFewerStructs(lclNum)) + { + // Do not promote some structs, that can be promoted, to stress promoted/unpromoted moves. + JITDUMP("Not promoting promotable struct local V%02u, because of STRESS_PROMOTE_FEWER_STRUCTS\n", lclNum); + shouldPromote = false; + } +#endif // // If the lvRefCnt is zero and we have a struct promoted parameter we can end up with an extra store of @@ -2804,25 +2816,26 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool varDsc->lvBaseType = simdBaseType; } #endif // FEATURE_SIMD -#ifdef FEATURE_HFA - // For structs that are small enough, we check and set HFA element type - if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES) + if (GlobalJitOptions::compFeatureHfa) { - // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF - var_types hfaType = GetHfaType(typeHnd); - if (varTypeIsValidHfaType(hfaType)) + // For structs that are small enough, we check and set HFA element type + if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES) { - varDsc->SetHfaType(hfaType); - - // hfa variables can never contain GC pointers - assert(!layout->HasGCPtr()); - // The size of this struct should be evenly divisible by 4 or 8 - assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0); - // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit - assert((varDsc->lvExactSize / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT); + // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF + var_types hfaType = GetHfaType(typeHnd); + if (varTypeIsValidHfaType(hfaType)) + { + varDsc->SetHfaType(hfaType); + + // hfa variables can never contain GC pointers + assert(!layout->HasGCPtr()); + // The size of this struct should be evenly divisible by 4 or 8 + assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0); + // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit + assert((varDsc->lvExactSize / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT); + } } } -#endif // FEATURE_HFA } } else @@ -2936,14 +2949,15 @@ void Compiler::makeExtraStructQueries(CORINFO_CLASS_HANDLE structHandle, int lev void Compiler::lvaSetStructUsedAsVarArg(unsigned varNum) { -#ifdef FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - LclVarDsc* varDsc = &lvaTable[varNum]; - // For varargs methods incoming and outgoing arguments should not be treated - // as HFA. - varDsc->SetHfaType(TYP_UNDEF); + LclVarDsc* varDsc = &lvaTable[varNum]; + // For varargs methods incoming and outgoing arguments should not be treated + // as HFA. + varDsc->SetHfaType(TYP_UNDEF); #endif // defined(TARGET_WINDOWS) && defined(TARGET_ARM64) -#endif // FEATURE_HFA + } } //------------------------------------------------------------------------ @@ -3257,9 +3271,9 @@ BasicBlock::weight_t BasicBlock::getCalledCount(Compiler* comp) // getBBWeight -- get the normalized weight of this block BasicBlock::weight_t BasicBlock::getBBWeight(Compiler* comp) { - if (this->bbWeight == 0) + if (this->bbWeight == BB_ZERO_WEIGHT) { - return 0; + return BB_ZERO_WEIGHT; } else { @@ -4088,6 +4102,21 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, if (varDsc->lvEhWriteThruCandidate || needsExplicitZeroInit) { +#ifdef DEBUG + if (needsExplicitZeroInit) + { + varDsc->lvDisqualifyEHVarReason = 'Z'; + JITDUMP("EH Var V%02u needs explicit zero init. Disqualified as a register candidate.\n", + lclNum); + } + else + { + varDsc->lvDisqualifyEHVarReason = 'M'; + JITDUMP("EH Var V%02u has multiple definitions. Disqualified as a register candidate.\n", + lclNum); + } + +#endif // DEBUG varDsc->lvEhWriteThruCandidate = false; varDsc->lvDisqualifyForEhWriteThru = true; } @@ -4100,6 +4129,7 @@ void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, #endif { varDsc->lvEhWriteThruCandidate = true; + JITDUMP("Marking EH Var V%02u as a register candidate.\n", lclNum); } } } @@ -5455,9 +5485,13 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() // the native return buffer parameter. if (callConvIsInstanceMethodCallConv(info.compCallConv)) { - noway_assert(lvaTable[lclNum].lvIsRegArg); -#ifndef TARGET_X86 - argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); +#ifdef TARGET_X86 + if (!lvaTable[lclNum].lvIsRegArg) + { + argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); + } +#else + argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); #endif // TARGET_X86 lclNum++; userArgsToSkip++; @@ -7354,7 +7388,7 @@ void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t r } if (lvaEnregEHVars && varDsc->lvLiveInOutOfHndlr) { - printf("H"); + printf("%c", varDsc->lvDisqualifyEHVarReason); } if (varDsc->lvLclFieldExpr) { diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 51c0040a1f927..442e11b8fc6cd 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -139,11 +139,12 @@ GenTree* LC_Condition::ToGenTree(Compiler* comp, BasicBlock* bb) // Evaluates - Evaluate a given loop cloning condition if it can be statically evaluated. // // Arguments: -// pResult The evaluation result +// pResult OUT parameter. The evaluation result // // Return Values: // Returns true if the condition can be statically evaluated. If the condition's result -// is statically unknown then return false. In other words, true if "pResult" is valid. +// is statically unknown then return false. In other words, `*pResult` is valid only if the +// function returns true. // bool LC_Condition::Evaluates(bool* pResult) { @@ -240,7 +241,7 @@ JitExpandArrayStack* LoopCloneContext::GetLoopOptInfo(unsigned loopN // void LoopCloneContext::CancelLoopOptInfo(unsigned loopNum) { - JITDUMP("Cancelling loop cloning for loop L_%02u\n", loopNum); + JITDUMP("Cancelling loop cloning for loop " FMT_LP "\n", loopNum); optInfo[loopNum] = nullptr; if (conditions[loopNum] != nullptr) { @@ -392,36 +393,40 @@ JitExpandArrayStack*>* LoopCloneContext::Ensur #ifdef DEBUG void LoopCloneContext::PrintBlockConditions(unsigned loopNum) { + printf("Block conditions:\n"); + JitExpandArrayStack*>* levelCond = blockConditions[loopNum]; if (levelCond == nullptr || levelCond->Size() == 0) { - JITDUMP("No block conditions\n"); + printf("No block conditions\n"); return; } for (unsigned i = 0; i < levelCond->Size(); ++i) { - JITDUMP("%d = {", i); + printf("%d = {", i); for (unsigned j = 0; j < ((*levelCond)[i])->Size(); ++j) { if (j != 0) { - JITDUMP(" & "); + printf(" & "); } (*((*levelCond)[i]))[j].Print(); } - JITDUMP("}\n"); + printf("}\n"); } } #endif //-------------------------------------------------------------------------------------------------- -// EvaluateConditions - Evaluate the loop cloning conditions statically, if it can be evaluated. +// EvaluateConditions - Evaluate the loop cloning conditions statically, if they can be evaluated. // // Arguments: // loopNum the loop index. -// pAllTrue all the cloning conditions evaluated to "true" statically. -// pAnyFalse some cloning condition evaluated to "false" statically. +// pAllTrue OUT parameter. `*pAllTrue` is set to `true` if all the cloning conditions statically +// evaluate to true. +// pAnyFalse OUT parameter. `*pAnyFalse` is set to `true` if some cloning condition statically +// evaluate to false. // verbose verbose logging required. // // Return Values: @@ -436,8 +441,12 @@ void LoopCloneContext::PrintBlockConditions(unsigned loopNum) // all be "AND"ed, so statically we know we will never take the fast path. // // Sometimes we simply can't say statically whether "V02 > V01.length" is true or false. -// In that case, the "pAllTrue" will be false because this condition doesn't evaluate to "true" and -// "pAnyFalse" could be false if no other condition statically evaluates to "false". +// In that case, `*pAllTrue` will be false because this condition doesn't evaluate to "true" and +// `*pAnyFalse` could be false if no other condition statically evaluates to "false". +// +// If `*pAnyFalse` is true, we set that and return, and `*pAllTrue` is not accurate, since the loop cloning +// needs to be aborted. +// void LoopCloneContext::EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool* pAnyFalse DEBUGARG(bool verbose)) { bool allTrue = true; @@ -445,7 +454,7 @@ void LoopCloneContext::EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool JitExpandArrayStack& conds = *conditions[loopNum]; - JITDUMP("Evaluating %d loop cloning conditions for loop %d\n", conds.Size(), loopNum); + JITDUMP("Evaluating %d loop cloning conditions for loop " FMT_LP "\n", conds.Size(), loopNum); assert(conds.Size() > 0); for (unsigned i = 0; i < conds.Size(); ++i) @@ -462,11 +471,15 @@ void LoopCloneContext::EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool // Check if this condition evaluates to true or false. if (conds[i].Evaluates(&res)) { - JITDUMP(") evaluates to %d\n", res); + JITDUMP(") evaluates to %s\n", dspBool(res)); if (!res) { anyFalse = true; - return; + + // Since this will force us to abort loop cloning, there is no need compute an accurate `allTrue`, + // so we can break out of the loop now. + // REVIEW: it appears we never hit this condition in any test. + break; } } else @@ -476,7 +489,7 @@ void LoopCloneContext::EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool } } - JITDUMP("Evaluation result allTrue = %d, anyFalse = %d\n", allTrue, anyFalse); + JITDUMP("Evaluation result allTrue = %s, anyFalse = %s\n", dspBool(allTrue), dspBool(anyFalse)); *pAllTrue = allTrue; *pAnyFalse = anyFalse; } @@ -643,6 +656,7 @@ void LoopCloneContext::PrintConditions(unsigned loopNum) if (conditions[loopNum]->Size() == 0) { JITDUMP("Conditions were optimized away! Will always take cloned path."); + return; } for (unsigned i = 0; i < conditions[loopNum]->Size(); ++i) { diff --git a/src/coreclr/jit/loopcloning.h b/src/coreclr/jit/loopcloning.h index 37d1b7466bdcc..6cc921c520db1 100644 --- a/src/coreclr/jit/loopcloning.h +++ b/src/coreclr/jit/loopcloning.h @@ -4,34 +4,108 @@ /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX XX -XX LoopCloning XX +XX Loop Cloning XX XX XX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - Loop cloning optimizations comprise of the following steps: - - Loop detection logic which is existing logic in the JIT that records - loop information with loop flags. - - The next step is to identify loop optimization candidates. This is done - by optObtainLoopCloningOpts. The loop context variable is updated with - all the necessary information (for ex: block, stmt, tree information) - to do the optimization later. + Loop cloning is an optimization which duplicates a loop to create two versions. + One copy is optimized by hoisting out various dynamic checks, such as array bounds + checks that can't be statically eliminated. The checks are dynamically run. If + they fail, the original copy of the loop is executed. If they pass, the + optimized copy of the loop is executed, knowing that the bounds checks are + dynamically unnecessary. + + The optimization can reduce the amount of code executed within a loop body. + + For example: + + public static int f(int[] a, int l) + { + int sum = 0; + for (int i = 0; i < l; i++) + { + sum += a[i]; // This array bounds check must be executed in the loop + } + } + + This can be transformed to (in pseudo-code): + + public static int f(int[] a, int l) + { + int sum = 0; + if (a != null && l <= a.Length) + { + for (int i = 0; i < l; i++) + { + sum += a[i]; // no bounds check needed + } + } + else + { + for (int i = 0; i < l; i++) + { + // bounds check needed. We need to do the normal computation (esp., side effects) before the +exception occurs. + sum += a[i]; + } + } + } + + One generalization of this is "loop unswitching". + + Because code is duplicated, this is a code size expanding optimization, and + therefore we need to be careful to avoid duplicating too much code unnecessarily. + + Also, there is a risk that we can duplicate the loops and later, downstream + phases optimize away the bounds checks even on the un-optimized copy of the loop. + + Loop cloning is implemented with the following steps: + + 1. Loop detection logic, which is existing logic in the JIT that records + loop information with loop flags. + + 2. Identify loop optimization candidates. This is done by optObtainLoopCloningOpts. + The loop context variable is updated with all the necessary information (for example: + block, stmt, tree information) to do the optimization later. a) This involves checking if the loop is well-formed with respect to the optimization being performed. b) In array bounds check case, reconstructing the morphed GT_INDEX nodes back to their array representation. i) The array index is stored in the "context" variable with additional block, tree, stmt info. - - Once the optimization candidates are identified, we derive cloning conditions - For ex: to clone a simple "for (i=0; i= 0) && (n <= a.length) && (stride > 0) + Note that "&&" implies a short-circuiting operator. This requires each condition + to be in its own block with its own comparison and branch instruction. This can + be optimized if there are no dependent conditions in a block by using a bitwise + AND instead of a short-circuit AND. The (a != null) condition needs to occur before + "a.length" is checked. But otherwise, the last three conditions can be computed in + the same block, as: (a != null) && ((n >= 0) & (n <= a.length) & (stride > 0)) - a) Note the short circuit AND for (a != null). These are called block - conditions or deref-conditions since these conditions need to be in their - own blocks to be able to short-circuit. - i) For a doubly nested loop on i, j, we would then have - conditions like - (a != null) && (i < a.len) && (a[i] != null) && (j < a[i].len) + Since we're optimizing for the expected fast path case, where all the conditions + are true, we expect all the conditions to be executed most of the time. Thus, it + is advantageous to make as many as possible non-short-circuiting to reduce the + number of compare/branch/blocks needed. + + In the above case, stride == 1, so we statically know stride > 0. + + If we had "for (i=0; i<=n; ++i) { a[i] }", we would need: + (a != null) && (n >= 0) && (a.length >= 1) && (n <= a.length - 1) && (stride > 0) + This is more complicated. The loop is equivalent (except for possible overflow) to: + for (i=0; i= 0) condition? We do need to know + "array index var initialization value >= array lower bound (0)". + + a) Conditions that need to be in their own blocks to enable short-circuit are called block + conditions or deref-conditions. + i) For a doubly nested loop on i, j, we would then have conditions like + (a != null) && (i < a.len) && (a[i] != null) && (j < a[i].len) all short-circuiting creating blocks. Advantage: @@ -43,6 +117,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Heuristic: Therefore we will not clone if we exceed creating 4 blocks. + Note: this means we never clone more than 2-dimension a[i][j] expressions + (see optComputeDerefConditions()). + REVIEW: make this heuristic defined by a COMPlus variable, for easier + experimentation, and make it more dynamic and based on potential benefit? b) The other conditions called cloning conditions are transformed into LC_Condition structs which are then optimized. @@ -50,38 +128,71 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ii) If some conditions evaluate to true statically, then they are removed. iii) If any condition evaluates to false statically, then loop cloning is aborted for that loop. - - Then the block splitting occurs and loop cloning conditions is transformed into - GenTree and added to the loop cloning choice block. + + 4. Then the block splitting occurs and loop cloning conditions are transformed into + GenTree and added to the loop cloning choice block (the block that determines which + copy of the loop is executed). Preconditions - - Loop detection should have completed and the loop table should be - populated with the loop dscs. - - The loops that will be considered are the ones with the LPFLG_ITER - marked on them. + + 1. Loop detection has completed and the loop table is populated. + + 2. The loops that will be considered are the ones with the LPFLG_ITER flag: + "for (i = icon or lclVar; test_condition(); i++)" Limitations - - For array based optimizations the loop choice condition is checked - before the loop body. This implies that the loop initializer statement - has not executed at the time of the check. So any loop cloning condition - involving the initial value of the loop counter cannot be condition checked - as it hasn't been assigned yet at the time of condition checking. Therefore - the initial value has to be statically known. This can be fixed with further - effort. - - Assumption - - The assumption is that the optimization candidates collected during the - identification phase will be the ones that will be optimized. In other words, - the loop that is present originally will be the fast path. Explicitly, the cloned - path will be the slow path and will be unoptimized. This allows us to - collect additional information at the same time of identifying the optimization - candidates. This later helps us to perform the optimizations during actual cloning. - - All loop cloning choice conditions will automatically be "AND"-ed. These are - bitwise AND operations. - - Perform short circuit AND for (array != null) side effect check - before hoisting (limit <= a.length) check. - For ex: to clone a simple "for (i=0; i= 0) & (n <= a.length) & (stride > 0)) + + 1. For array based optimizations the loop choice condition is checked + before the loop body. This implies that the loop initializer statement + has not executed at the time of the check. So any loop cloning condition + involving the initial value of the loop counter cannot be condition checked + as it hasn't been assigned yet at the time of condition checking. Therefore + the initial value has to be statically known. This can be fixed with further + effort. + + 2. Loops containing nested exception handling regions are not cloned. (Cloning them + would require creating new exception handling regions for the cloned loop, which + is "hard".) There are a few other EH-related edge conditions that also cause us to + reject cloning. + + 3. If the loop contains RETURN blocks, and cloning those would push us over the maximum + number of allowed RETURN blocks in the function (either due to GC info encoding limitations + or otherwise), we reject cloning. + + 4. Loop increment must be `i += 1` + + 5. Loop test must be `i < x` where `x` is a constant, a variable, or `a.Length` for array `a` + + (There is some implementation support for decrementing loops, but it is incomplete. + There is some implementation support for `i <= x` conditions, but it is incomplete + (Compiler::optDeriveLoopCloningConditions() only handles GT_LT conditions)) + + 6. Loop must have been converted to a do-while form. + + 7. There are a few other loop well-formedness conditions. + + 8. Multi-dimensional (non-jagged) loop index checking is only partially implemented. + + 9. Constant initializations and constant limits must be non-negative (REVIEW: why? The + implementation does use `unsigned` to represent them.) + + 10. The cloned loop (the slow path) is not added to the loop table, meaning certain + downstream optimization passes do not see them. See + https://github.com/dotnet/runtime/issues/43713. + + Assumptions + + 1. The assumption is that the optimization candidates collected during the + identification phase will be the ones that will be optimized. In other words, + the loop that is present originally will be the fast path. The cloned + path will be the slow path and will be unoptimized. This allows us to + collect additional information at the same time as identifying the optimization + candidates. This later helps us to perform the optimizations during actual cloning. + + 2. All loop cloning choice conditions will automatically be "AND"-ed. These are bitwise AND operations. + + 3. Perform short circuit AND for (array != null) side effect check + before hoisting (limit <= a.length) check. */ #pragma once @@ -91,7 +202,7 @@ class Compiler; /** * * Represents an array access and associated bounds checks. - * Array access is required have the array and indices in local variables. + * Array access is required to have the array and indices in local variables. * This struct is constructed using a GT_INDEX node that is broken into * its sub trees. * @@ -130,8 +241,10 @@ struct ArrIndex * other classes are supposed to derive from this base class. * * Example usage: + * * LcMdArrayOptInfo is multi-dimensional array optimization for which the * loop can be cloned. + * * LcArrIndexOptInfo is a jagged array optimization for which the loop * can be cloned. * @@ -143,7 +256,6 @@ struct LcOptInfo { enum OptType { -#undef LC_OPT #define LC_OPT(en) en, #include "loopcloningopts.h" }; @@ -158,7 +270,6 @@ struct LcOptInfo { return optType; } -#undef LC_OPT #define LC_OPT(en) \ en##OptInfo* As##en##OptInfo() \ { \ @@ -175,7 +286,7 @@ struct LcOptInfo struct LcMdArrayOptInfo : public LcOptInfo { GenTreeArrElem* arrElem; // "arrElem" node of an MD array. - unsigned dim; // "dim" represents upto what level of the rank this optimization applies to. + unsigned dim; // "dim" represents up to what level of the rank this optimization applies to. // For example, a[i,j,k] could be the MD array "arrElem" but if "dim" is 2, // then this node is treated as though it were a[i,j] ArrIndex* index; // "index" cached computation in the form of an ArrIndex representation. @@ -207,7 +318,7 @@ struct LcMdArrayOptInfo : public LcOptInfo */ struct LcJaggedArrayOptInfo : public LcOptInfo { - unsigned dim; // "dim" represents upto what level of the rank this optimization applies to. + unsigned dim; // "dim" represents up to what level of the rank this optimization applies to. // For example, a[i][j][k] could be the jagged array but if "dim" is 2, // then this node is treated as though it were a[i][j] ArrIndex arrIndex; // ArrIndex representation of the array. @@ -312,8 +423,8 @@ struct LC_Array /** * - * Symbolic representation of either a constant like 1, 2 or a variable V02, V03 etc. or an "LC_Array" or the null - * constant. + * Symbolic representation of either a constant like 1 or 2, or a variable like V02 or V03, or an "LC_Array", + * or the null constant. */ struct LC_Ident { @@ -337,7 +448,7 @@ struct LC_Ident { case Const: case Var: - return (type == that.type) && constant == that.constant; + return (type == that.type) && (constant == that.constant); case ArrLen: return (type == that.type) && (arrLen == that.arrLen); case Null: @@ -366,7 +477,7 @@ struct LC_Ident printf("null"); break; default: - assert(false); + printf("INVALID"); break; } } @@ -426,6 +537,10 @@ struct LC_Expr { ident.Print(); } + else + { + printf("INVALID"); + } } #endif @@ -516,11 +631,12 @@ struct LC_Deref static LC_Deref* Find(JitExpandArrayStack* children, unsigned lcl); void DeriveLevelConditions(JitExpandArrayStack*>* len); + #ifdef DEBUG void Print(unsigned indent = 0) { unsigned tab = 4 * indent; - printf("%*s%d,%d => {", tab, "", Lcl(), level); + printf("%*sV%02d, level %d => {", tab, "", Lcl(), level); if (children != nullptr) { for (unsigned i = 0; i < children->Size(); ++i) @@ -553,23 +669,25 @@ struct LC_Deref * LC_Condition : LC_Expr genTreeOps LC_Expr * LC_Expr : LC_Ident | LC_Ident + Constant * LC_Ident : Constant | Var | LC_Array - * LC_Array : . + * LC_Array : . * genTreeOps : GT_GE | GT_LE | GT_GT | GT_LT * */ struct LoopCloneContext { - CompAllocator alloc; // The allocator - JitExpandArrayStack** optInfo; // The array of optimization opportunities found in each loop. (loop x - // optimization-opportunities) - JitExpandArrayStack** conditions; // The array of conditions that influence which path to take for - // each - // loop. (loop x cloning-conditions) - JitExpandArrayStack** derefs; // The array of dereference conditions found in each loop. (loop x - // deref-conditions) - JitExpandArrayStack*>** blockConditions; // The array of block levels of - // conditions for - // each loop. (loop x level x conditions) + CompAllocator alloc; // The allocator + + // The array of optimization opportunities found in each loop. (loop x optimization-opportunities) + JitExpandArrayStack** optInfo; + + // The array of conditions that influence which path to take for each loop. (loop x cloning-conditions) + JitExpandArrayStack** conditions; + + // The array of dereference conditions found in each loop. (loop x deref-conditions) + JitExpandArrayStack** derefs; + + // The array of block levels of conditions for each loop. (loop x level x conditions) + JitExpandArrayStack*>** blockConditions; LoopCloneContext(unsigned loopCount, CompAllocator alloc) : alloc(alloc) { @@ -589,16 +707,16 @@ struct LoopCloneContext // Evaluate conditions into a JTRUE stmt and put it in the block. Reverse condition if 'reverse' is true. void CondToStmtInBlock(Compiler* comp, JitExpandArrayStack& conds, BasicBlock* block, bool reverse); - // Get all the optimization information for loop "loopNum"; This information is held in "optInfo" array. - // If NULL this allocates the optInfo[loopNum] array for "loopNum" + // Get all the optimization information for loop "loopNum"; this information is held in "optInfo" array. + // If NULL this allocates the optInfo[loopNum] array for "loopNum". JitExpandArrayStack* EnsureLoopOptInfo(unsigned loopNum); - // Get all the optimization information for loop "loopNum"; This information is held in "optInfo" array. - // If NULL this does not allocate the optInfo[loopNum] array for "loopNum" + // Get all the optimization information for loop "loopNum"; this information is held in "optInfo" array. + // If NULL this does not allocate the optInfo[loopNum] array for "loopNum". JitExpandArrayStack* GetLoopOptInfo(unsigned loopNum); // Cancel all optimizations for loop "loopNum" by clearing out the "conditions" member if non-null - // and setting the optInfo to "null.", If "null", then the user of this class is not supposed to + // and setting the optInfo to "null". If "null", then the user of this class is not supposed to // clone this loop. void CancelLoopOptInfo(unsigned loopNum); @@ -618,21 +736,25 @@ struct LoopCloneContext JitExpandArrayStack*>* EnsureBlockConditions(unsigned loopNum, unsigned totalBlocks); +#ifdef DEBUG // Print the block conditions for the loop. void PrintBlockConditions(unsigned loopNum); +#endif // Does the loop have block conditions? bool HasBlockConditions(unsigned loopNum); // Evaluate the conditions for "loopNum" and indicate if they are either all true or any of them are false. - // "pAllTrue" implies all the conditions are statically known to be true. - // "pAnyFalse" implies at least one condition is statically known to be false. - // If neither of them are true, then some conditions' evaluations are statically unknown. // - // If all conditions yield true, then the caller doesn't need to clone the loop, but it can perform - // fast path optimizations. - // If any condition yields false, then the caller needs to abort cloning the loop (neither clone nor - // fast path optimizations.) + // `pAllTrue` and `pAnyFalse` are OUT parameters. + // + // If `*pAllTrue` is `true`, then all the conditions are statically known to be true. + // The caller doesn't need to clone the loop, but it can perform fast path optimizations. + // + // If `*pAnyFalse` is `true`, then at least one condition is statically known to be false. + // The caller needs to abort cloning the loop (neither clone nor fast path optimizations.) + // + // If neither `*pAllTrue` nor `*pAnyFalse` is true, then the evaluation of some conditions are statically unknown. // // Assumes the conditions involve an AND join operator. void EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool* pAnyFalse DEBUGARG(bool verbose)); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 6641f412a417c..3eaaac1408bc4 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3243,38 +3243,41 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) // void Lowering::LowerRetStruct(GenTreeUnOp* ret) { -#if defined(FEATURE_HFA) && defined(TARGET_ARM64) - if (varTypeIsSIMD(ret)) +#ifdef TARGET_ARM64 + if (GlobalJitOptions::compFeatureHfa) { - if (comp->info.compRetNativeType == TYP_STRUCT) + if (varTypeIsSIMD(ret)) { - assert(varTypeIsSIMD(ret->gtGetOp1())); - assert(comp->compMethodReturnsMultiRegRegTypeAlternate()); - if (!comp->compDoOldStructRetyping()) + if (comp->info.compRetNativeType == TYP_STRUCT) { - ret->ChangeType(comp->info.compRetNativeType); + assert(varTypeIsSIMD(ret->gtGetOp1())); + assert(comp->compMethodReturnsMultiRegRegTypeAlternate()); + if (!comp->compDoOldStructRetyping()) + { + ret->ChangeType(comp->info.compRetNativeType); + } + else + { + // With old struct retyping a value that is returned as HFA + // could have both SIMD* or STRUCT types, keep it as it. + return; + } } else { - // With old struct retyping a value that is returned as HFA - // could have both SIMD* or STRUCT types, keep it as it. + assert(comp->info.compRetNativeType == ret->TypeGet()); + GenTree* retVal = ret->gtGetOp1(); + if (retVal->TypeGet() != ret->TypeGet()) + { + assert(retVal->OperIs(GT_LCL_VAR)); + assert(!comp->compDoOldStructRetyping()); + LowerRetSingleRegStructLclVar(ret); + } return; } } - else - { - assert(comp->info.compRetNativeType == ret->TypeGet()); - GenTree* retVal = ret->gtGetOp1(); - if (retVal->TypeGet() != ret->TypeGet()) - { - assert(retVal->OperIs(GT_LCL_VAR)); - assert(!comp->compDoOldStructRetyping()); - LowerRetSingleRegStructLclVar(ret); - } - return; - } } -#endif +#endif // TARGET_ARM64 if (comp->compMethodReturnsMultiRegRegTypeAlternate()) { @@ -3461,25 +3464,26 @@ void Lowering::LowerCallStruct(GenTreeCall* call) return; } -#if defined(FEATURE_HFA) - if (comp->IsHfa(call)) + if (GlobalJitOptions::compFeatureHfa) { + if (comp->IsHfa(call)) + { #if defined(TARGET_ARM64) - assert(comp->GetHfaCount(call) == 1); + assert(comp->GetHfaCount(call) == 1); #elif defined(TARGET_ARM) - // ARM returns double in 2 float registers, but - // `call->HasMultiRegRetVal()` count double registers. - assert(comp->GetHfaCount(call) <= 2); -#elif // !TARGET_ARM64 && !TARGET_ARM - unreached(); + // ARM returns double in 2 float registers, but + // `call->HasMultiRegRetVal()` count double registers. + assert(comp->GetHfaCount(call) <= 2); +#else // !TARGET_ARM64 && !TARGET_ARM + NYI("Unknown architecture"); #endif // !TARGET_ARM64 && !TARGET_ARM - var_types hfaType = comp->GetHfaType(call); - if (call->TypeIs(hfaType)) - { - return; + var_types hfaType = comp->GetHfaType(call); + if (call->TypeIs(hfaType)) + { + return; + } } } -#endif // FEATURE_HFA assert(!comp->compDoOldStructRetyping()); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; @@ -3551,7 +3555,6 @@ void Lowering::LowerStoreSingleRegCallStruct(GenTreeBlk* store) const ClassLayout* layout = store->GetLayout(); const var_types regType = layout->GetRegisterType(); - unsigned storeSize = store->GetLayout()->GetSize(); if (regType != TYP_UNDEF) { store->ChangeType(regType); @@ -4503,7 +4506,12 @@ GenTree* Lowering::LowerNonvirtPinvokeCall(GenTreeCall* call) switch (lookup.accessType) { case IAT_VALUE: - if (!IsCallTargetInRange(addr)) + // IsCallTargetInRange always return true on x64. It wants to use rip-based addressing + // for this call. Unfortunately, in case of pinvokes (+suppressgctransition) to external libs + // (e.g. kernel32.dll) the relative offset is unlikely to fit into int32 and we will have to + // turn fAllowRel32 off globally. + if ((call->IsSuppressGCTransition() && !comp->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)) || + !IsCallTargetInRange(addr)) { result = AddrGen(addr); } @@ -5383,10 +5391,9 @@ GenTree* Lowering::LowerConstIntDivOrMod(GenTree* node) // For -3 we need: // mulhi -= dividend ; requires sub adjust // div = signbit(mulhi) + sar(mulhi, 1) ; requires shift adjust - bool requiresAddSubAdjust = signum(divisorValue) != signum(magic); - bool requiresShiftAdjust = shift != 0; - bool requiresDividendMultiuse = requiresAddSubAdjust || !isDiv; - BasicBlock::weight_t curBBWeight = comp->compCurBB->getBBWeight(comp); + bool requiresAddSubAdjust = signum(divisorValue) != signum(magic); + bool requiresShiftAdjust = shift != 0; + bool requiresDividendMultiuse = requiresAddSubAdjust || !isDiv; if (requiresDividendMultiuse) { diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 98aa897960009..dfa6fe68ff421 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -5158,7 +5158,6 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node) HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId); int numArgs = HWIntrinsicInfo::lookupNumArgs(node); var_types baseType = node->gtSIMDBaseType; - unsigned simdSize = node->gtSIMDSize; GenTree* op1 = node->gtGetOp1(); GenTree* op2 = node->gtGetOp2(); diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index bbcc44a281404..609d8eaf0b9e1 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -816,9 +816,8 @@ void LinearScan::setBlockSequence() assert(blockSequenceWorkList == nullptr); - bool addedInternalBlocks = false; - verifiedAllBBs = false; - hasCriticalEdges = false; + verifiedAllBBs = false; + hasCriticalEdges = false; BasicBlock* nextBlock; // We use a bbNum of 0 for entry RefPositions. // The other information in blockInfo[0] will never be used. @@ -1333,10 +1332,9 @@ void LinearScan::recordVarLocationsAtStartOfBB(BasicBlock* bb) { unsigned varNum = compiler->lvaTrackedIndexToLclNum(varIndex); LclVarDsc* varDsc = compiler->lvaGetDesc(varNum); - regNumber regNum = getVarReg(map, varIndex); regNumber oldRegNum = varDsc->GetRegNum(); - regNumber newRegNum = regNum; + regNumber newRegNum = getVarReg(map, varIndex); if (oldRegNum != newRegNum) { @@ -2309,12 +2307,41 @@ BasicBlock* LinearScan::findPredBlockForLiveIn(BasicBlock* block, if (block->bbPreds == nullptr) { + assert((block != compiler->fgFirstBB) || (prevBlock != nullptr)); + JITDUMP("\n\nNo predecessor; "); + + // Some throw blocks do not have predecessor. For such blocks, we want to return the fact + // that predecessor is indeed null instead of returning the prevBlock. Returning prevBlock + // will be wrong, because LSRA would think that the variable is live in registers based on + // the lexical flow, but that won't be true according to the control flow. + // Example: + // + // IG05: + // ... ; V01 is in 'rdi' + // JNE IG07 + // ... + // IG06: + // ... + // ... ; V01 is in 'rbx' + // JMP IG08 + // IG07: + // ... ; LSRA thinks V01 is in 'rbx' if IG06 is set as previous block of IG07. + // .... + // CALL CORINFO_HELP_RNGCHKFAIL + // ... + // IG08: + // ... + // ... + if (block->bbJumpKind == BBJ_THROW) + { + JITDUMP(" - throw block; "); + return nullptr; + } + // We may have unreachable blocks, due to optimization. // We don't want to set the predecessor as null in this case, since that will result in // unnecessary DummyDefs, and possibly result in inconsistencies requiring resolution // (since these unreachable blocks can have reachable successors). - assert((block != compiler->fgFirstBB) || (prevBlock != nullptr)); - JITDUMP("\n\nNo predecessor; "); return prevBlock; } @@ -2943,17 +2970,11 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos REG_NUM = 0x0001, // It has a lower register number. }; - LsraLocation bestLocation = MinLocation; - LsraLocation farRefLocation = MinLocation; - // These are used in the post-selection updates, and must be set for any selection. regMaskTP freeCandidates = RBM_NONE; regMaskTP matchingConstants = RBM_NONE; regMaskTP unassignedSet = RBM_NONE; - // These must be set prior to their use in the associated heuristics. - unsigned int thisSpillWeight = 0; - // We'll set this to short-circuit remaining heuristics when we have a single candidate. bool found = false; @@ -3295,8 +3316,6 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos float thisSpillWeight = getWeight(refPosition); // The spill weight for the best candidate we've found so far. float bestSpillWeight = FloatingPointUtils::infinite_float(); - // True if we found registers with lower spill weight than this refPosition. - bool foundLowerSpillWeight = false; for (regMaskTP spillCandidates = selector.candidates; spillCandidates != RBM_NONE;) { @@ -3503,9 +3522,8 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos // the current interval to a previous assignment, we don't remember the previous interval. // Note that we need to compute this condition before calling unassignPhysReg, which wil reset // assignedInterval->physReg. - regMaskTP freePrefCandidates = (selector.candidates & preferences & freeCandidates); - bool wasThisAssigned = ((prevRegBit & preferences) == foundRegBit); - bool wasAssigned = (((foundRegBit & unassignedSet) != RBM_NONE) && !wasThisAssigned && + bool wasThisAssigned = ((prevRegBit & preferences) == foundRegBit); + bool wasAssigned = (((foundRegBit & unassignedSet) != RBM_NONE) && !wasThisAssigned && (assignedInterval != nullptr) && (assignedInterval->physReg == foundReg)); unassignPhysReg(availablePhysRegRecord ARM_ARG(currentInterval->registerType)); if ((matchingConstants & foundRegBit) != RBM_NONE) @@ -3741,11 +3759,6 @@ regNumber LinearScan::assignCopyReg(RefPosition* refPosition) assert(currentInterval != nullptr); assert(currentInterval->isActive); - bool foundFreeReg = false; - RegRecord* bestPhysReg = nullptr; - LsraLocation bestLocation = MinLocation; - regMaskTP candidates = refPosition->registerAssignment; - // Save the relatedInterval, if any, so that it doesn't get modified during allocation. Interval* savedRelatedInterval = currentInterval->relatedInterval; currentInterval->relatedInterval = nullptr; @@ -4217,8 +4230,8 @@ void LinearScan::unassignPhysReg(RegRecord* regRec, RefPosition* spillRefPositio return; } - regNumber victimAssignedReg = assignedInterval->physReg; - assignedInterval->physReg = REG_NA; + // regNumber victimAssignedReg = assignedInterval->physReg; + assignedInterval->physReg = REG_NA; bool spill = assignedInterval->isActive && nextRefPosition != nullptr; if (spill) @@ -4706,10 +4719,9 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock) return; } - unsigned predBBNum = blockInfo[currentBlock->bbNum].predBBNum; - VarToRegMap predVarToRegMap = getOutVarToRegMap(predBBNum); - VarToRegMap inVarToRegMap = getInVarToRegMap(currentBlock->bbNum); - bool hasCriticalInEdge = blockInfo[currentBlock->bbNum].hasCriticalInEdge; + unsigned predBBNum = blockInfo[currentBlock->bbNum].predBBNum; + VarToRegMap predVarToRegMap = getOutVarToRegMap(predBBNum); + VarToRegMap inVarToRegMap = getInVarToRegMap(currentBlock->bbNum); // If this block enters an exception region, all incoming vars are on the stack. if (predBBNum == 0) @@ -5033,7 +5045,6 @@ void LinearScan::processBlockEndLocations(BasicBlock* currentBlock) VarSetOps::Assign(compiler, currentLiveVars, registerCandidateVars); } #endif // DEBUG - regMaskTP liveRegs = RBM_NONE; VarSetOps::Iter iter(compiler, currentLiveVars); unsigned varIndex = 0; while (iter.NextElem(&varIndex)) @@ -5180,7 +5191,7 @@ void LinearScan::freeRegisters(regMaskTP regsToFree) regsToFree &= ~(nextRegBit << 1); } #endif - freeRegister(getRegisterRecord(nextReg)); + freeRegister(regRecord); } } @@ -5252,7 +5263,6 @@ void LinearScan::allocateRegisters() } #ifdef DEBUG - regNumber lastAllocatedReg = REG_NA; if (VERBOSE) { dumpRefPositions("BEFORE ALLOCATION"); @@ -5331,7 +5341,6 @@ void LinearScan::allocateRegisters() Interval* currentInterval = nullptr; Referenceable* currentReferent = nullptr; - bool isInternalRef = false; RefType refType = currentRefPosition->refType; currentReferent = currentRefPosition->referent; @@ -5939,9 +5948,8 @@ void LinearScan::allocateRegisters() regNumber copyReg = assignCopyReg(currentRefPosition); assert(copyReg != REG_NA); INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_COPY_REG, currentInterval, copyReg)); - lastAllocatedRefPosition = currentRefPosition; - bool unassign = false; - RefPosition* nextRefPosition = currentRefPosition->nextRefPosition; + lastAllocatedRefPosition = currentRefPosition; + bool unassign = false; if (currentInterval->isWriteThru) { if (currentRefPosition->refType == RefTypeDef) @@ -6126,8 +6134,7 @@ void LinearScan::allocateRegisters() // is not used, etc. // If this is an UpperVector we'll neither free it nor preference it // (it will be freed when it is used). - bool stillInReg = true; - bool unassign = false; + bool unassign = false; if (!currentInterval->IsUpperVector()) { if (currentInterval->isWriteThru) @@ -7055,7 +7062,7 @@ void LinearScan::insertUpperVectorRestore(GenTree* tree, } else { - JITDUMP("at end of BB%02u:\n", block->bbNum); + JITDUMP("at end of " FMT_BB ":\n", block->bbNum); if (block->bbJumpKind == BBJ_COND || block->bbJumpKind == BBJ_SWITCH) { noway_assert(!blockRange.IsEmpty()); @@ -7349,9 +7356,6 @@ void LinearScan::resolveRegisters() (refPosIterator->refType != RefTypeParamDef && refPosIterator->refType != RefTypeZeroInit)); } - BasicBlock* insertionBlock = compiler->fgFirstBB; - GenTree* insertionPoint = LIR::AsRange(insertionBlock).FirstNonPhiNode(); - // write back assignments for (block = startBlockSequence(); block != nullptr; block = moveToNextBlock()) { @@ -7718,7 +7722,6 @@ void LinearScan::resolveRegisters() regNumber initialReg = (initialRegMask == RBM_NONE || interval->firstRefPosition->spillAfter) ? REG_STK : genRegNumFromMask(initialRegMask); - regNumber sourceReg = (varDsc->lvIsRegArg) ? varDsc->GetArgReg() : REG_STK; #ifdef TARGET_ARM if (varTypeIsMultiReg(varDsc)) @@ -8259,16 +8262,12 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) return; } VARSET_TP sameResolutionSet(VarSetOps::MakeEmpty(compiler)); - VARSET_TP sameLivePathsSet(VarSetOps::MakeEmpty(compiler)); - VARSET_TP singleTargetSet(VarSetOps::MakeEmpty(compiler)); VARSET_TP diffResolutionSet(VarSetOps::MakeEmpty(compiler)); // Get the outVarToRegMap for this block VarToRegMap outVarToRegMap = getOutVarToRegMap(block->bbNum); unsigned succCount = block->NumSucc(compiler); assert(succCount > 1); - VarToRegMap firstSuccInVarToRegMap = nullptr; - BasicBlock* firstSucc = nullptr; // First, determine the live regs at the end of this block so that we know what regs are // available to copy into. @@ -8370,9 +8369,6 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) while (outResolutionSetIter.NextElem(&outResolutionSetVarIndex)) { regNumber fromReg = getVarReg(outVarToRegMap, outResolutionSetVarIndex); - bool isMatch = true; - bool isSame = false; - bool maybeSingleTarget = false; bool maybeSameLivePaths = false; bool liveOnlyAtSplitEdge = true; regNumber sameToReg = REG_NA; @@ -8499,7 +8495,6 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block) // Now collect the resolution set for just this edge, if any. // Check only the vars in diffResolutionSet that are live-in to this successor. - bool needsResolution = false; VarToRegMap succInVarToRegMap = getInVarToRegMap(succBlock->bbNum); VARSET_TP edgeResolutionSet(VarSetOps::Intersection(compiler, diffResolutionSet, succBlock->bbLiveIn)); VarSetOps::Iter iter(compiler, edgeResolutionSet); @@ -8840,7 +8835,9 @@ void LinearScan::resolveEdge(BasicBlock* fromBlock, (resolveType == ResolveSharedCritical) ? REG_NA : getTempRegForResolution(fromBlock, toBlock, TYP_INT); #endif // !TARGET_XARCH regNumber tempRegFlt = REG_NA; - regNumber tempRegDbl = REG_NA; // Used only for ARM +#ifdef TARGET_ARM + regNumber tempRegDbl = REG_NA; +#endif if ((compiler->compFloatingPointUsed) && (resolveType != ResolveSharedCritical)) { #ifdef TARGET_ARM @@ -9985,11 +9982,8 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode) { GenTree* tree = node; - genTreeOps oper = tree->OperGet(); - int produce = tree->IsValue() ? ComputeOperandDstCount(tree) : 0; - int consume = ComputeAvailableSrcCount(tree); - regMaskTP killMask = RBM_NONE; - regMaskTP fixedMask = RBM_NONE; + int produce = tree->IsValue() ? ComputeOperandDstCount(tree) : 0; + int consume = ComputeAvailableSrcCount(tree); lsraDispNode(tree, mode, produce != 0 && mode != LSRA_DUMP_REFPOS); @@ -10402,8 +10396,7 @@ void LinearScan::dumpRegRecordHeader() regTableIndent = shortRefPositionDumpWidth + allocationInfoWidth; // BBnn printed left-justified in the NAME Typeld and allocationInfo space. - int bbDumpWidth = regColumnWidth + 1 + refTypeInfoWidth + allocationInfoWidth; - int bbNumWidth = (int)log10((double)compiler->fgBBNumMax) + 1; + int bbNumWidth = (int)log10((double)compiler->fgBBNumMax) + 1; // In the unlikely event that BB numbers overflow the space, we'll simply omit the predBB int predBBNumDumpSpace = regTableIndent - locationAndRPNumWidth - bbNumWidth - 9; // 'BB' + ' PredBB' if (predBBNumDumpSpace < bbNumWidth) @@ -10615,7 +10608,6 @@ void LinearScan::dumpNewBlock(BasicBlock* currentBlock, LsraLocation location) // void LinearScan::dumpRefPositionShort(RefPosition* refPosition, BasicBlock* currentBlock) { - BasicBlock* block = currentBlock; static RefPosition* lastPrintedRefPosition = nullptr; if (refPosition == lastPrintedRefPosition) { diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 32441ad422aa3..61bb8ca4420e7 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -286,9 +286,8 @@ void LinearScan::resolveConflictingDefAndUse(Interval* interval, RefPosition* de } if (useRefPosition->isFixedRegRef && !useRegConflict) { - useReg = useRefPosition->assignedReg(); - useRegRecord = getRegisterRecord(useReg); - RefPosition* currFixedRegRefPosition = useRegRecord->recentRefPosition; + useReg = useRefPosition->assignedReg(); + useRegRecord = getRegisterRecord(useReg); // We know that useRefPosition is a fixed use, so the nextRefPosition must not be null. RefPosition* nextFixedRegRefPosition = useRegRecord->getNextRefPosition(); @@ -3802,7 +3801,6 @@ void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* c // int LinearScan::BuildGCWriteBarrier(GenTree* tree) { - GenTree* dst = tree; GenTree* addr = tree->gtGetOp1(); GenTree* src = tree->gtGetOp2(); @@ -3810,7 +3808,6 @@ int LinearScan::BuildGCWriteBarrier(GenTree* tree) // is an indir through an lea, we need to actually instantiate the // lea in a register assert(!addr->isContained() && !src->isContained()); - int srcCount = 2; regMaskTP addrCandidates = RBM_ARG_0; regMaskTP srcCandidates = RBM_ARG_1; diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 3872e794aa884..910b121ce05ee 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -45,10 +45,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX int LinearScan::BuildNode(GenTree* tree) { assert(!tree->isContained()); - Interval* prefSrcInterval = nullptr; int srcCount; int dstCount = 0; - regMaskTP dstCandidates = RBM_NONE; regMaskTP killMask = RBM_NONE; bool isLocalDefUse = false; @@ -1088,7 +1086,6 @@ int LinearScan::BuildCall(GenTreeCall* call) // there is an explicit thisPtr but it is redundant bool callHasFloatRegArgs = false; - bool isVarArgs = call->IsVarargs(); // First, determine internal registers. // We will need one for any float arguments to a varArgs call. @@ -1723,11 +1720,10 @@ int LinearScan::BuildLclHeap(GenTree* tree) // int LinearScan::BuildModDiv(GenTree* tree) { - GenTree* op1 = tree->gtGetOp1(); - GenTree* op2 = tree->gtGetOp2(); - regMaskTP dstCandidates = RBM_NONE; - RefPosition* internalDef = nullptr; - int srcCount = 0; + GenTree* op1 = tree->gtGetOp1(); + GenTree* op2 = tree->gtGetOp2(); + regMaskTP dstCandidates = RBM_NONE; + int srcCount = 0; if (varTypeIsFloating(tree->TypeGet())) { @@ -2161,11 +2157,10 @@ int LinearScan::BuildSIMD(GenTreeSIMD* simdTree) // int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree) { - NamedIntrinsic intrinsicId = intrinsicTree->gtHWIntrinsicId; - var_types baseType = intrinsicTree->gtSIMDBaseType; - CORINFO_InstructionSet isa = HWIntrinsicInfo::lookupIsa(intrinsicId); - HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId); - int numArgs = HWIntrinsicInfo::lookupNumArgs(intrinsicTree); + NamedIntrinsic intrinsicId = intrinsicTree->gtHWIntrinsicId; + var_types baseType = intrinsicTree->gtSIMDBaseType; + HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(intrinsicId); + int numArgs = HWIntrinsicInfo::lookupNumArgs(intrinsicTree); // Set the AVX Flags if this instruction may use VEX encoding for SIMD operations. // Note that this may be true even if the ISA is not AVX (e.g. for platform-agnostic intrinsics diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index c2ee204766075..8fbce8a586e79 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1056,9 +1056,10 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum, curArgTabEntry->needTmp = false; curArgTabEntry->needPlace = false; curArgTabEntry->processed = false; -#ifdef FEATURE_HFA - curArgTabEntry->_hfaElemKind = CORINFO_HFA_ELEM_NONE; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + curArgTabEntry->SetHfaElemKind(CORINFO_HFA_ELEM_NONE); + } curArgTabEntry->isBackFilled = false; curArgTabEntry->isNonStandard = false; curArgTabEntry->isStruct = isStruct; @@ -1153,9 +1154,10 @@ fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum, curArgTabEntry->needTmp = false; curArgTabEntry->needPlace = false; curArgTabEntry->processed = false; -#ifdef FEATURE_HFA - curArgTabEntry->_hfaElemKind = CORINFO_HFA_ELEM_NONE; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + curArgTabEntry->SetHfaElemKind(CORINFO_HFA_ELEM_NONE); + } curArgTabEntry->isBackFilled = false; curArgTabEntry->isNonStandard = false; curArgTabEntry->isStruct = isStruct; @@ -2986,8 +2988,6 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) { argx = args->GetNode()->gtSkipPutArgType(); - fgArgTabEntry* argEntry = nullptr; - // Change the node to TYP_I_IMPL so we don't report GC info // NOTE: We deferred this from the importer because of the inliner. @@ -3008,35 +3008,35 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) #if !defined(OSX_ARM64_ABI) unsigned argAlignBytes = TARGET_POINTER_SIZE; #endif - unsigned size = 0; - unsigned byteSize = 0; - CORINFO_CLASS_HANDLE copyBlkClass = nullptr; - bool isRegArg = false; - bool isNonStandard = false; - regNumber nonStdRegNum = REG_NA; + unsigned size = 0; + unsigned byteSize = 0; + bool isRegArg = false; + bool isNonStandard = false; + regNumber nonStdRegNum = REG_NA; -#ifdef FEATURE_HFA - hfaType = GetHfaType(argx); - isHfaArg = varTypeIsValidHfaType(hfaType); + if (GlobalJitOptions::compFeatureHfa) + { + hfaType = GetHfaType(argx); + isHfaArg = varTypeIsValidHfaType(hfaType); #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - // Make sure for vararg methods isHfaArg is not true. - isHfaArg = callIsVararg ? false : isHfaArg; + // Make sure for vararg methods isHfaArg is not true. + isHfaArg = callIsVararg ? false : isHfaArg; #endif // defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - if (isHfaArg) - { - isHfaArg = true; - hfaSlots = GetHfaCount(argx); + if (isHfaArg) + { + isHfaArg = true; + hfaSlots = GetHfaCount(argx); - // If we have a HFA struct it's possible we transition from a method that originally - // only had integer types to now start having FP types. We have to communicate this - // through this flag since LSRA later on will use this flag to determine whether - // or not to track the FP register set. - // - compFloatingPointUsed = true; + // If we have a HFA struct it's possible we transition from a method that originally + // only had integer types to now start having FP types. We have to communicate this + // through this flag since LSRA later on will use this flag to determine whether + // or not to track the FP register set. + // + compFloatingPointUsed = true; + } } -#endif // FEATURE_HFA const bool isFloatHfa = (hfaType == TYP_FLOAT); @@ -3211,9 +3211,6 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) if (isStructArg) { - // We have an argument with a struct type, but it may be be a child of a GT_COMMA - GenTree* argObj = argx->gtEffectiveVal(true /*commaOnly*/); - assert(argx == args->GetNode()); assert(structSize != 0); @@ -3586,12 +3583,14 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) #endif } -#ifdef FEATURE_HFA - if (isHfaArg) + if (GlobalJitOptions::compFeatureHfa) { - newArgEntry->SetHfaType(hfaType, hfaSlots); + if (isHfaArg) + { + newArgEntry->SetHfaType(hfaType, hfaSlots); + } } -#endif // FEATURE_HFA + newArgEntry->SetMultiRegNums(); noway_assert(newArgEntry != nullptr); @@ -3674,7 +3673,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // Set up the fgArgInfo. fgInitArgInfo(call); - unsigned numArgs = call->fgArgInfo->ArgCount(); JITDUMP("%sMorphing args for %d.%s:\n", (reMorphing) ? "Re" : "", call->gtTreeID, GenTree::OpName(call->gtOper)); // If we are remorphing, process the late arguments (which were determined by a previous caller). @@ -3764,9 +3762,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // Get information about this argument. var_types hfaType = argEntry->GetHfaType(); bool isHfaArg = (hfaType != TYP_UNDEF); - unsigned hfaSlots = argEntry->numRegs; bool passUsingFloatRegs = argEntry->isPassedInFloatRegisters(); - bool isBackFilled = argEntry->IsBackFilled(); unsigned structSize = 0; // Struct arguments may be morphed into a node that is not a struct type. @@ -4271,10 +4267,8 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // void Compiler::fgMorphMultiregStructArgs(GenTreeCall* call) { - bool foundStructArg = false; - unsigned initialFlags = call->gtFlags; - unsigned flagsSummary = 0; - fgArgInfo* allArgInfo = call->fgArgInfo; + bool foundStructArg = false; + unsigned flagsSummary = 0; #ifdef TARGET_X86 assert(!"Logic error: no MultiregStructArgs for X86"); @@ -6526,7 +6520,9 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac) if (isStaticReadOnlyInited) { JITDUMP("Marking initialized static read-only field '%s' as invariant.\n", eeGetFieldName(symHnd)); - tree->gtFlags |= GTF_IND_INVARIANT; + + // Static readonly field is not null at this point (see getStaticFieldCurrentClass impl). + tree->gtFlags |= (GTF_IND_INVARIANT | GTF_IND_NONFAULTING | GTF_IND_NONNULL); tree->gtFlags &= ~GTF_ICON_INITCLASS; addr->gtFlags = GTF_ICON_CONST_PTR; } @@ -9594,7 +9590,19 @@ GenTree* Compiler::fgMorphConst(GenTree* tree) // guarantee slow performance for that block. Instead cache the return value // of CORINFO_HELP_STRCNS and go to cache first giving reasonable perf. + bool useLazyStrCns = false; if (compCurBB->bbJumpKind == BBJ_THROW) + { + useLazyStrCns = true; + } + else if (fgGlobalMorph && compCurStmt->GetRootNode()->IsCall()) + { + // Quick check: if the root node of the current statement happens to be a noreturn call. + GenTreeCall* call = compCurStmt->GetRootNode()->AsCall(); + useLazyStrCns = call->IsNoReturn() || fgIsThrow(call); + } + + if (useLazyStrCns) { CorInfoHelpFunc helper = info.compCompHnd->getLazyStringLiteralHelper(tree->AsStrCon()->gtScpHnd); if (helper != CORINFO_HELP_UNDEF) @@ -10993,6 +11001,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) unsigned modifiedLclNum = BAD_VAR_NUM; LclVarDsc* destLclVar = nullptr; FieldSeqNode* destFldSeq = nullptr; + unsigned destLclOffset = 0; bool destDoFldAsg = false; GenTree* destAddr = nullptr; GenTree* srcAddr = nullptr; @@ -11030,9 +11039,11 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) { assert(dest->TypeGet() != TYP_STRUCT); assert(dest->gtOper == GT_LCL_FLD); - blockWidth = genTypeSize(dest->TypeGet()); - destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest); - destFldSeq = dest->AsLclFld()->GetFieldSeq(); + GenTreeLclFld* destFld = dest->AsLclFld(); + blockWidth = genTypeSize(destFld->TypeGet()); + destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, destFld); + destFldSeq = destFld->GetFieldSeq(); + destLclOffset = destFld->GetLclOffs(); } } else @@ -11069,6 +11080,7 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) destLclNum = lclVarTree->GetLclNum(); modifiedLclNum = destLclNum; destLclVar = &lvaTable[destLclNum]; + destLclOffset = lclVarTree->GetLclOffs(); } } } @@ -11104,10 +11116,14 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) } } - FieldSeqNode* srcFldSeq = nullptr; - unsigned srcLclNum = BAD_VAR_NUM; - LclVarDsc* srcLclVar = nullptr; - bool srcDoFldAsg = false; + FieldSeqNode* srcFldSeq = nullptr; + unsigned srcLclNum = BAD_VAR_NUM; + LclVarDsc* srcLclVar = nullptr; + unsigned srcLclOffset = 0; + bool srcDoFldAsg = false; + + bool srcUseLclFld = false; + bool destUseLclFld = false; if (src->IsLocal()) { @@ -11132,7 +11148,8 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) if (srcLclNum != BAD_VAR_NUM) { - srcLclVar = &lvaTable[srcLclNum]; + srcLclOffset = srcLclVarTree->GetLclOffs(); + srcLclVar = &lvaTable[srcLclNum]; if (srcLclVar->lvPromoted && blockWidthIsConst) { @@ -11206,11 +11223,9 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) } #endif // TARGET_ARM - // Don't use field by field assignment if the src is a call - // - // TODO: Document why do we have this restriction? - // Maybe it isn't needed, or maybe it is only needed - // for calls that return multiple registers + // Don't use field by field assignment if the src is a call, + // lowering will handle it without spilling the call result into memory + // to access the individual fields. // if (src->OperGet() == GT_CALL) { @@ -11409,8 +11424,9 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) if (destDoFldAsg && srcDoFldAsg) { - // To do fieldwise assignments for both sides, they'd better be the same struct type! - // All of these conditions were checked above... + // To do fieldwise assignments for both sides. + // The structs do not have to be the same exact types but have to have same field types + // at the same offsets. assert(destLclNum != BAD_VAR_NUM && srcLclNum != BAD_VAR_NUM); assert(destLclVar != nullptr && srcLclVar != nullptr && destLclVar->lvFieldCnt == srcLclVar->lvFieldCnt); @@ -11422,7 +11438,10 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) { fieldCnt = destLclVar->lvFieldCnt; src = fgMorphBlockOperand(src, asgType, blockWidth, false /*isBlkReqd*/); - if (srcAddr == nullptr) + + srcUseLclFld = fgMorphCanUseLclFldForCopy(destLclNum, srcLclNum); + + if (!srcUseLclFld && srcAddr == nullptr) { srcAddr = fgMorphGetStructAddr(&src, destLclVar->GetStructHnd(), true /* rValue */); } @@ -11437,28 +11456,35 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) dest->SetOper(GT_IND); dest->gtType = TYP_STRUCT; } - destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest); + destUseLclFld = fgMorphCanUseLclFldForCopy(srcLclNum, destLclNum); + if (!destUseLclFld) + { + destAddr = gtNewOperNode(GT_ADDR, TYP_BYREF, dest); + } } if (destDoFldAsg) { noway_assert(!srcDoFldAsg); - if (gtClone(srcAddr)) + if (!srcUseLclFld) { - // srcAddr is simple expression. No need to spill. - noway_assert((srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - } - else - { - // srcAddr is complex expression. Clone and spill it (unless the destination is - // a struct local that only has one field, in which case we'd only use the - // address value once...) - if (destLclVar->lvFieldCnt > 1) + if (gtClone(srcAddr)) { - // We will spill srcAddr (i.e. assign to a temp "BlockOp address local") - // no need to clone a new copy as it is only used once - // - addrSpill = srcAddr; // addrSpill represents the 'srcAddr' + // srcAddr is simple expression. No need to spill. + noway_assert((srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + } + else + { + // srcAddr is complex expression. Clone and spill it (unless the destination is + // a struct local that only has one field, in which case we'd only use the + // address value once...) + if (destLclVar->lvFieldCnt > 1) + { + // We will spill srcAddr (i.e. assign to a temp "BlockOp address local") + // no need to clone a new copy as it is only used once + // + addrSpill = srcAddr; // addrSpill represents the 'srcAddr' + } } } } @@ -11478,22 +11504,25 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) lclVarTree->gtFlags &= ~(GTF_VAR_DEF | GTF_VAR_USEASG); } - if (gtClone(destAddr)) - { - // destAddr is simple expression. No need to spill - noway_assert((destAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - } - else + if (!destUseLclFld) { - // destAddr is complex expression. Clone and spill it (unless - // the source is a struct local that only has one field, in which case we'd only - // use the address value once...) - if (srcLclVar->lvFieldCnt > 1) + if (gtClone(destAddr)) { - // We will spill destAddr (i.e. assign to a temp "BlockOp address local") - // no need to clone a new copy as it is only used once - // - addrSpill = destAddr; // addrSpill represents the 'destAddr' + // destAddr is simple expression. No need to spill + noway_assert((destAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + } + else + { + // destAddr is complex expression. Clone and spill it (unless + // the source is a struct local that only has one field, in which case we'd only + // use the address value once...) + if (srcLclVar->lvFieldCnt > 1) + { + // We will spill destAddr (i.e. assign to a temp "BlockOp address local") + // no need to clone a new copy as it is only used once + // + addrSpill = destAddr; // addrSpill represents the 'destAddr' + } } } } @@ -11591,43 +11620,48 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) } else { - if (addrSpill) + GenTree* dstAddrClone = nullptr; + if (!destUseLclFld) { - assert(addrSpillTemp != BAD_VAR_NUM); - dstFld = gtNewLclvNode(addrSpillTemp, TYP_BYREF); - } - else - { - if (i == 0) + // Need address of the destination. + if (addrSpill) { - // Use the orginal destAddr tree when i == 0 - dstFld = destAddr; + assert(addrSpillTemp != BAD_VAR_NUM); + dstAddrClone = gtNewLclvNode(addrSpillTemp, TYP_BYREF); } else { - // We can't clone multiple copies of a tree with persistent side effects - noway_assert((destAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + if (i == 0) + { + // Use the orginal destAddr tree when i == 0 + dstAddrClone = destAddr; + } + else + { + // We can't clone multiple copies of a tree with persistent side effects + noway_assert((destAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - dstFld = gtCloneExpr(destAddr); - noway_assert(dstFld != nullptr); + dstAddrClone = gtCloneExpr(destAddr); + noway_assert(dstAddrClone != nullptr); - JITDUMP("dstFld - Multiple Fields Clone created:\n"); - DISPTREE(dstFld); + JITDUMP("dstAddr - Multiple Fields Clone created:\n"); + DISPTREE(dstAddrClone); - // Morph the newly created tree - dstFld = fgMorphTree(dstFld); - } + // Morph the newly created tree + dstAddrClone = fgMorphTree(dstAddrClone); + } - // Is the address of a local? - GenTreeLclVarCommon* lclVarTree = nullptr; - bool isEntire = false; - bool* pIsEntire = (blockWidthIsConst ? &isEntire : nullptr); - if (dstFld->DefinesLocalAddr(this, blockWidth, &lclVarTree, pIsEntire)) - { - lclVarTree->gtFlags |= GTF_VAR_DEF; - if (!isEntire) + // Is the address of a local? + GenTreeLclVarCommon* lclVarTree = nullptr; + bool isEntire = false; + bool* pIsEntire = (blockWidthIsConst ? &isEntire : nullptr); + if (dstAddrClone->DefinesLocalAddr(this, blockWidth, &lclVarTree, pIsEntire)) { - lclVarTree->gtFlags |= GTF_VAR_USEASG; + lclVarTree->gtFlags |= GTF_VAR_DEF; + if (!isEntire) + { + lclVarTree->gtFlags |= GTF_VAR_USEASG; + } } } } @@ -11642,27 +11676,46 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) info.compCompHnd->getFieldInClass(classHnd, srcFieldVarDsc->lvFldOrdinal); FieldSeqNode* curFieldSeq = GetFieldSeqStore()->CreateSingleton(fieldHnd); - unsigned srcFieldOffset = lvaGetDesc(srcFieldLclNum)->lvFldOffset; + unsigned srcFieldOffset = lvaGetDesc(srcFieldLclNum)->lvFldOffset; + var_types srcType = srcFieldVarDsc->TypeGet(); - if (srcFieldOffset == 0) + if (!destUseLclFld) { - fgAddFieldSeqForZeroOffset(dstFld, curFieldSeq); + + if (srcFieldOffset == 0) + { + fgAddFieldSeqForZeroOffset(dstAddrClone, curFieldSeq); + } + else + { + GenTree* fieldOffsetNode = gtNewIconNode(srcFieldVarDsc->lvFldOffset, curFieldSeq); + dstAddrClone = gtNewOperNode(GT_ADD, TYP_BYREF, dstAddrClone, fieldOffsetNode); + } + + dstFld = gtNewIndir(srcType, dstAddrClone); } else { - GenTree* fieldOffsetNode = gtNewIconNode(srcFieldVarDsc->lvFldOffset, curFieldSeq); - dstFld = gtNewOperNode(GT_ADD, TYP_BYREF, dstFld, fieldOffsetNode); + assert(dstAddrClone == nullptr); + assert((destLclOffset == 0) || (destFldSeq != nullptr)); + // If the dst was a struct type field "B" in a struct "A" then we add + // add offset of ("B" in "A") + current offset in "B". + unsigned summOffset = destLclOffset + srcFieldOffset; + dstFld = gtNewLclFldNode(destLclNum, srcType, summOffset); + FieldSeqNode* dstFldFldSeq = GetFieldSeqStore()->Append(destFldSeq, curFieldSeq); + dstFld->AsLclFld()->SetFieldSeq(dstFldFldSeq); + + // TODO-1stClassStructs: remove this and implement storing to a field in a struct in a reg. + lvaSetVarDoNotEnregister(destLclNum DEBUGARG(DNER_LocalField)); } - dstFld = gtNewIndir(srcFieldVarDsc->TypeGet(), dstFld); - // !!! The destination could be on stack. !!! // This flag will let us choose the correct write barrier. dstFld->gtFlags |= GTF_IND_TGTANYWHERE; } } - GenTree* srcFld; + GenTree* srcFld = nullptr; if (srcDoFldAsg) { noway_assert(srcLclNum != BAD_VAR_NUM); @@ -11688,31 +11741,36 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) } else { - if (addrSpill) - { - assert(addrSpillTemp != BAD_VAR_NUM); - srcFld = gtNewLclvNode(addrSpillTemp, TYP_BYREF); - } - else + GenTree* srcAddrClone = nullptr; + if (!srcUseLclFld) { - if (i == 0) + // Need address of the source. + if (addrSpill) { - // Use the orginal srcAddr tree when i == 0 - srcFld = srcAddr; + assert(addrSpillTemp != BAD_VAR_NUM); + srcAddrClone = gtNewLclvNode(addrSpillTemp, TYP_BYREF); } else { - // We can't clone multiple copies of a tree with persistent side effects - noway_assert((srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); + if (i == 0) + { + // Use the orginal srcAddr tree when i == 0 + srcAddrClone = srcAddr; + } + else + { + // We can't clone multiple copies of a tree with persistent side effects + noway_assert((srcAddr->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) == 0); - srcFld = gtCloneExpr(srcAddr); - noway_assert(srcFld != nullptr); + srcAddrClone = gtCloneExpr(srcAddr); + noway_assert(srcAddrClone != nullptr); - JITDUMP("srcFld - Multiple Fields Clone created:\n"); - DISPTREE(srcFld); + JITDUMP("srcAddr - Multiple Fields Clone created:\n"); + DISPTREE(srcAddrClone); - // Morph the newly created tree - srcFld = fgMorphTree(srcFld); + // Morph the newly created tree + srcAddrClone = fgMorphTree(srcAddrClone); + } } } @@ -11749,20 +11807,36 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) if (!done) { unsigned fldOffset = lvaGetDesc(dstFieldLclNum)->lvFldOffset; - if (fldOffset == 0) + if (!srcUseLclFld) { - fgAddFieldSeqForZeroOffset(srcFld, curFieldSeq); + assert(srcAddrClone != nullptr); + if (fldOffset == 0) + { + fgAddFieldSeqForZeroOffset(srcAddrClone, curFieldSeq); + } + else + { + GenTreeIntCon* fldOffsetNode = gtNewIconNode(fldOffset, curFieldSeq); + srcAddrClone = gtNewOperNode(GT_ADD, TYP_BYREF, srcAddrClone, fldOffsetNode); + } + srcFld = gtNewIndir(destType, srcAddrClone); } else { - GenTreeIntCon* fldOffsetNode = gtNewIconNode(fldOffset, curFieldSeq); - srcFld = gtNewOperNode(GT_ADD, TYP_BYREF, srcFld, fldOffsetNode); + assert((srcLclOffset == 0) || (srcFldSeq != 0)); + // If the src was a struct type field "B" in a struct "A" then we add + // add offset of ("B" in "A") + current offset in "B". + unsigned summOffset = srcLclOffset + fldOffset; + srcFld = gtNewLclFldNode(srcLclNum, destType, summOffset); + FieldSeqNode* srcFldFldSeq = GetFieldSeqStore()->Append(srcFldSeq, curFieldSeq); + srcFld->AsLclFld()->SetFieldSeq(srcFldFldSeq); + // TODO-1stClassStructs: remove this and implement reading a field from a struct in a reg. + lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DNER_LocalField)); } - srcFld = gtNewIndir(destType, srcFld); } } } - + assert(srcFld != nullptr); noway_assert(dstFld->TypeGet() == srcFld->TypeGet()); asg = gtNewAssignNode(dstFld, srcFld); @@ -11813,6 +11887,46 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) return tree; } +//------------------------------------------------------------------------ +// fgMorphCanUseLclFldForCopy: check if we can access LclVar2 using LclVar1's fields. +// +// Arguments: +// lclNum1 - a promoted lclVar that is used in fieldwise assignment; +// lclNum2 - the local variable on the other side of ASG, can be BAD_VAR_NUM. +// +// Return Value: +// True if the second local is valid and has the same struct handle as the first, +// false otherwise. +// +// Notes: +// This check is needed to avoid accesing LCL_VARs with incorrect +// CORINFO_FIELD_HANDLE that would confuse VN optimizations. +// +bool Compiler::fgMorphCanUseLclFldForCopy(unsigned lclNum1, unsigned lclNum2) +{ + assert(lclNum1 != BAD_VAR_NUM); + if (lclNum2 == BAD_VAR_NUM) + { + return false; + } + const LclVarDsc* varDsc1 = lvaGetDesc(lclNum1); + const LclVarDsc* varDsc2 = lvaGetDesc(lclNum2); + assert(varTypeIsStruct(varDsc1)); + if (!varTypeIsStruct(varDsc2)) + { + return false; + } + CORINFO_CLASS_HANDLE struct1 = varDsc1->GetStructHnd(); + CORINFO_CLASS_HANDLE struct2 = varDsc2->GetStructHnd(); + assert(struct1 != NO_CLASS_HANDLE); + assert(struct2 != NO_CLASS_HANDLE); + if (struct1 != struct2) + { + return false; + } + return true; +} + // insert conversions and normalize to make tree amenable to register // FP architectures GenTree* Compiler::fgMorphForRegisterFP(GenTree* tree) @@ -12762,7 +12876,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) "the return [%06u]\n", lclVar->GetLclNum(), fieldLclNum, dspTreeID(tree)); lclVar->SetLclNum(fieldLclNum); - var_types fieldType = fieldDsc->lvType; lclVar->ChangeType(fieldDsc->lvType); } } @@ -13528,8 +13641,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) goto SKIP; } - LclVarDsc* varDsc = lvaTable + lclNum; - /* Set op1 to the right side of asg, (i.e. the RELOP) */ op1 = asg->AsOp()->gtOp2; @@ -14300,8 +14411,12 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac) case GT_NOT: case GT_NEG: - // Remove double negation/not - if (op1->OperIs(oper) && opts.OptimizationEnabled()) + // Remove double negation/not. + // Note: this is not a safe tranformation if "tree" is a CSE candidate. + // Consider for example the following expression: NEG(NEG(OP)), where the top-level + // NEG is a CSE candidate. Were we to morph this to just OP, CSE would fail to find + // the original NEG in the statement. + if (op1->OperIs(oper) && opts.OptimizationEnabled() && !gtIsActiveCSE_Candidate(tree)) { GenTree* child = op1->AsOp()->gtGetOp1(); return child; @@ -16546,7 +16661,7 @@ bool Compiler::fgFoldConditional(BasicBlock* block) #ifdef DEBUG if (verbose) { - printf("Removing loop L%02u (from " FMT_BB " to " FMT_BB ")\n\n", loopNum, + printf("Removing loop " FMT_LP " (from " FMT_BB " to " FMT_BB ")\n\n", loopNum, optLoopTable[loopNum].lpFirst->bbNum, optLoopTable[loopNum].lpBottom->bbNum); } #endif @@ -18513,6 +18628,18 @@ void Compiler::fgRetypeImplicitByRefArgs() bool undoPromotion = ((lvaGetPromotionType(newVarDsc) == PROMOTION_TYPE_DEPENDENT) || (nonCallAppearances <= varDsc->lvFieldCnt)); +#ifdef DEBUG + // Above is a profitability heurisic; either value of + // undoPromotion should lead to correct code. So, + // under stress, make different decisions at times. + if (compStressCompile(STRESS_BYREF_PROMOTION, 25)) + { + undoPromotion = !undoPromotion; + JITDUMP("Stress -- changing byref undo promotion for V%02u to %s undo\n", lclNum, + undoPromotion ? "" : "NOT"); + } +#endif // DEBUG + JITDUMP("%s promotion of implicit by-ref V%02u: %s total: %u non-call: %u fields: %u\n", undoPromotion ? "Undoing" : "Keeping", lclNum, (lvaGetPromotionType(newVarDsc) == PROMOTION_TYPE_DEPENDENT) ? "dependent;" : "", @@ -18910,7 +19037,6 @@ void Compiler::fgAddFieldSeqForZeroOffset(GenTree* addr, FieldSeqNode* fieldSeqZ FieldSeqNode* fieldSeqUpdate = fieldSeqZero; GenTree* fieldSeqNode = addr; bool fieldSeqRecorded = false; - bool isMapAnnotation = false; #ifdef DEBUG if (verbose) diff --git a/src/coreclr/jit/optcse.cpp b/src/coreclr/jit/optcse.cpp index 9697185569ac2..2b9bebd53d5b0 100644 --- a/src/coreclr/jit/optcse.cpp +++ b/src/coreclr/jit/optcse.cpp @@ -409,7 +409,6 @@ unsigned Compiler::optValnumCSE_Index(GenTree* tree, Statement* stmt) size_t key; unsigned hval; CSEdsc* hashDsc; - bool isIntConstHash = false; bool enableSharedConstCSE = false; bool isSharedConst = false; int configValue = JitConfig.JitConstCSE(); @@ -1196,7 +1195,7 @@ class CSE_DataFlow #ifdef DEBUG if (m_comp->verbose) { - printf("StartMerge BB%02u\n", block->bbNum); + printf("StartMerge " FMT_BB "\n", block->bbNum); printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut)); } #endif // DEBUG @@ -1208,7 +1207,7 @@ class CSE_DataFlow #ifdef DEBUG if (m_comp->verbose) { - printf("Merge BB%02u and BB%02u\n", block->bbNum, predBlock->bbNum); + printf("Merge " FMT_BB " and " FMT_BB "\n", block->bbNum, predBlock->bbNum); printf(" :: cseIn = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseIn)); printf(" :: cseOut = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseOut)); } @@ -1284,7 +1283,7 @@ class CSE_DataFlow #ifdef DEBUG if (m_comp->verbose) { - printf("EndMerge BB%02u\n", block->bbNum); + printf("EndMerge " FMT_BB "\n", block->bbNum); printf(" :: cseIn = %s\n", genES2str(m_comp->cseLivenessTraits, block->bbCseIn)); if (((block->bbFlags & BBF_HAS_CALL) != 0) && !BitVecOps::IsEmpty(m_comp->cseLivenessTraits, block->bbCseIn)) @@ -1452,7 +1451,7 @@ void Compiler::optValnumCSE_Availablity() if (verbose) { - printf("BB%02u ", block->bbNum); + printf(FMT_BB " ", block->bbNum); printTreeID(tree); printf(" %s of CSE #%02u [weight=%s]%s\n", isUse ? "Use" : "Def", CSEnum, refCntWtd2str(stmw), @@ -2366,14 +2365,12 @@ class CSE_Heuristic // Each CSE Def will contain two Refs and each CSE Use will have one Ref of this new LclVar BasicBlock::weight_t cseRefCnt = (candidate->DefCount() * 2) + candidate->UseCount(); - bool canEnregister = true; - unsigned slotCount = 1; - var_types cseLclVarTyp = genActualType(candidate->Expr()->TypeGet()); + bool canEnregister = true; + unsigned slotCount = 1; if (candidate->Expr()->TypeGet() == TYP_STRUCT) { // This is a non-enregisterable struct. canEnregister = false; - GenTree* value = candidate->Expr(); CORINFO_CLASS_HANDLE structHnd = m_pCompiler->gtGetStructHandleIfPresent(candidate->Expr()); if (structHnd == NO_CLASS_HANDLE) { diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 6bf058b878006..17b4a1858cea7 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -27,6 +27,11 @@ void Compiler::optInit() optLoopCount = 0; optLoopTable = nullptr; +#ifdef DEBUG + loopAlignCandidates = 0; + loopsAligned = 0; +#endif + /* Keep track of the number of calls and indirect calls made by this method */ optCallCount = 0; optIndirectCallCount = 0; @@ -95,13 +100,12 @@ void Compiler::optSetBlockWeights() // If we are not using profile weight then we lower the weight // of blocks that do not dominate a return block // - if (firstBBdomsRets && (fgIsUsingProfileWeights() == false) && (domsRets == false)) + if (firstBBdomsRets && !fgIsUsingProfileWeights() && !domsRets) { #if DEBUG changed = true; #endif - block->modifyBBWeight(block->bbWeight / 2); - noway_assert(block->bbWeight); + block->inheritWeightPercentage(block, 50); } } } @@ -215,36 +219,19 @@ void Compiler::optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool ex { noway_assert(curBlk->bbWeight > BB_ZERO_WEIGHT); - BasicBlock::weight_t weight; - - if (curBlk->hasProfileWeight()) + if (!curBlk->hasProfileWeight()) { - // We have real profile weights, so we aren't going to change this blocks weight - weight = curBlk->bbWeight; - } - else - { - if (dominates) - { - weight = curBlk->bbWeight * BB_LOOP_WEIGHT_SCALE; - } - else - { - weight = curBlk->bbWeight * (BB_LOOP_WEIGHT_SCALE / 2); - } + BasicBlock::weight_t scale = BB_LOOP_WEIGHT_SCALE; - // - // The multiplication may have caused us to overflow - // - if (weight < curBlk->bbWeight) + if (!dominates) { - // The multiplication caused us to overflow - weight = BB_MAX_WEIGHT; + scale = scale / 2; } + // // Set the new weight // - curBlk->modifyBBWeight(weight); + curBlk->scaleBBWeight(scale); } #ifdef DEBUG if (verbose) @@ -355,13 +342,13 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) // if (!curBlk->isRunRarely() && fgReachable(curBlk, begBlk) && fgReachable(begBlk, curBlk)) { - BasicBlock::weight_t weight = curBlk->bbWeight; - // Don't unmark blocks that are set to BB_MAX_WEIGHT // Don't unmark blocks when we are using profile weights // if (!curBlk->isMaxBBWeight() && !curBlk->hasProfileWeight()) { + BasicBlock::weight_t weight = curBlk->bbWeight; + if (!fgDominate(curBlk, endBlk)) { weight *= 2; @@ -382,9 +369,10 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) weight = BB_MAX_WEIGHT; } - assert(weight >= BB_LOOP_WEIGHT_SCALE); + assert(curBlk->bbWeight != BB_ZERO_WEIGHT); + BasicBlock::weight_t scale = weight / curBlk->bbWeight; - curBlk->modifyBBWeight(weight / BB_LOOP_WEIGHT_SCALE); + curBlk->scaleBBWeight(scale); } #ifdef DEBUG @@ -432,17 +420,19 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar for (unsigned loopNum = 0; loopNum < optLoopCount; loopNum++) { + LoopDsc& loop = optLoopTable[loopNum]; + /* Some loops may have been already removed by * loop unrolling or conditional folding */ - if (optLoopTable[loopNum].lpFlags & LPFLG_REMOVED) + if (loop.lpFlags & LPFLG_REMOVED) { continue; } - if (block == optLoopTable[loopNum].lpEntry || block == optLoopTable[loopNum].lpBottom) + if (block == loop.lpEntry || block == loop.lpBottom) { - optLoopTable[loopNum].lpFlags |= LPFLG_REMOVED; + loop.lpFlags |= LPFLG_REMOVED; continue; } @@ -457,14 +447,13 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar /* If the loop is still in the table * any block in the loop must be reachable !!! */ - noway_assert(optLoopTable[loopNum].lpEntry != block); - noway_assert(optLoopTable[loopNum].lpBottom != block); + noway_assert(loop.lpEntry != block); + noway_assert(loop.lpBottom != block); - if (optLoopTable[loopNum].lpExit == block) + if (loop.lpExit == block) { - optLoopTable[loopNum].lpExit = nullptr; - optLoopTable[loopNum].lpFlags &= ~LPFLG_ONE_EXIT; - ; + loop.lpExit = nullptr; + loop.lpFlags &= ~LPFLG_ONE_EXIT; } /* If this points to the actual entry in the loop @@ -477,7 +466,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar case BBJ_NONE: case BBJ_COND: - if (block->bbNext == optLoopTable[loopNum].lpEntry) + if (block->bbNext == loop.lpEntry) { removeLoop = true; break; @@ -491,7 +480,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar case BBJ_ALWAYS: noway_assert(block->bbJumpDest); - if (block->bbJumpDest == optLoopTable[loopNum].lpEntry) + if (block->bbJumpDest == loop.lpEntry) { removeLoop = true; } @@ -504,7 +493,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar do { noway_assert(*jumpTab); - if ((*jumpTab) == optLoopTable[loopNum].lpEntry) + if ((*jumpTab) == loop.lpEntry) { removeLoop = true; } @@ -525,8 +514,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar { /* Ignore blocks in the loop */ - if (auxBlock->bbNum > optLoopTable[loopNum].lpHead->bbNum && - auxBlock->bbNum <= optLoopTable[loopNum].lpBottom->bbNum) + if (auxBlock->bbNum > loop.lpHead->bbNum && auxBlock->bbNum <= loop.lpBottom->bbNum) { continue; } @@ -538,7 +526,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar case BBJ_NONE: case BBJ_COND: - if (auxBlock->bbNext == optLoopTable[loopNum].lpEntry) + if (auxBlock->bbNext == loop.lpEntry) { removeLoop = false; break; @@ -552,7 +540,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar case BBJ_ALWAYS: noway_assert(auxBlock->bbJumpDest); - if (auxBlock->bbJumpDest == optLoopTable[loopNum].lpEntry) + if (auxBlock->bbJumpDest == loop.lpEntry) { removeLoop = false; } @@ -565,7 +553,7 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar do { noway_assert(*jumpTab); - if ((*jumpTab) == optLoopTable[loopNum].lpEntry) + if ((*jumpTab) == loop.lpEntry) { removeLoop = false; } @@ -579,13 +567,13 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar if (removeLoop) { - optLoopTable[loopNum].lpFlags |= LPFLG_REMOVED; + loop.lpFlags |= LPFLG_REMOVED; } } - else if (optLoopTable[loopNum].lpHead == block) + else if (loop.lpHead == block) { /* The loop has a new head - Just update the loop table */ - optLoopTable[loopNum].lpHead = block->bbPrev; + loop.lpHead = block->bbPrev; } #ifdef DEBUG @@ -644,11 +632,11 @@ void Compiler::optPrintLoopInfo(unsigned loopInd, BasicBlock* lpBottom, unsigned char lpExitCnt, BasicBlock* lpExit, - unsigned parentLoop) + unsigned parentLoop) const { noway_assert(lpHead); - printf("L%02u, from " FMT_BB, loopInd, lpFirst->bbNum); + printf(FMT_LP ", from " FMT_BB, loopInd, lpFirst->bbNum); if (lpTop != lpFirst) { printf(" (loop top is " FMT_BB ")", lpTop->bbNum); @@ -664,7 +652,7 @@ void Compiler::optPrintLoopInfo(unsigned loopInd, if (parentLoop != BasicBlock::NOT_IN_LOOP) { - printf(", parent loop = L%02u", parentLoop); + printf(", parent loop = " FMT_LP, parentLoop); } printf(")"); } @@ -674,11 +662,11 @@ void Compiler::optPrintLoopInfo(unsigned loopInd, * Print loop information given the index of the loop in the loop table. */ -void Compiler::optPrintLoopInfo(unsigned lnum) +void Compiler::optPrintLoopInfo(unsigned lnum) const { noway_assert(lnum < optLoopCount); - LoopDsc* ldsc = &optLoopTable[lnum]; // lnum is the INDEX to the loop table. + const LoopDsc* ldsc = &optLoopTable[lnum]; // lnum is the INDEX to the loop table. optPrintLoopInfo(lnum, ldsc->lpHead, ldsc->lpFirst, ldsc->lpTop, ldsc->lpEntry, ldsc->lpBottom, ldsc->lpExitCnt, ldsc->lpExit, ldsc->lpParent); @@ -1319,43 +1307,43 @@ bool Compiler::optRecordLoop(BasicBlock* head, // Arguments: // loopInd - loop index. // -void Compiler::optPrintLoopRecording(unsigned loopInd) +void Compiler::optPrintLoopRecording(unsigned loopInd) const { + const LoopDsc& loop = optLoopTable[loopInd]; + printf("Recorded loop %s", (loopInd != optLoopCount ? "(extended) " : "")); optPrintLoopInfo(optLoopCount, // Not necessarily the loop index, but the number of loops that have been added. - optLoopTable[loopInd].lpHead, optLoopTable[loopInd].lpFirst, optLoopTable[loopInd].lpTop, - optLoopTable[loopInd].lpEntry, optLoopTable[loopInd].lpBottom, optLoopTable[loopInd].lpExitCnt, - optLoopTable[loopInd].lpExit); + loop.lpHead, loop.lpFirst, loop.lpTop, loop.lpEntry, loop.lpBottom, loop.lpExitCnt, loop.lpExit); // If an iterator loop print the iterator and the initialization. - if (optLoopTable[loopInd].lpFlags & LPFLG_ITER) + if (loop.lpFlags & LPFLG_ITER) { - printf(" [over V%02u", optLoopTable[loopInd].lpIterVar()); + printf(" [over V%02u", loop.lpIterVar()); printf(" ("); - printf(GenTree::OpName(optLoopTable[loopInd].lpIterOper())); + printf(GenTree::OpName(loop.lpIterOper())); printf(" "); - printf("%d )", optLoopTable[loopInd].lpIterConst()); + printf("%d )", loop.lpIterConst()); - if (optLoopTable[loopInd].lpFlags & LPFLG_CONST_INIT) + if (loop.lpFlags & LPFLG_CONST_INIT) { - printf(" from %d", optLoopTable[loopInd].lpConstInit); + printf(" from %d", loop.lpConstInit); } - if (optLoopTable[loopInd].lpFlags & LPFLG_VAR_INIT) + if (loop.lpFlags & LPFLG_VAR_INIT) { - printf(" from V%02u", optLoopTable[loopInd].lpVarInit); + printf(" from V%02u", loop.lpVarInit); } // If a simple test condition print operator and the limits */ - printf(GenTree::OpName(optLoopTable[loopInd].lpTestOper())); + printf(GenTree::OpName(loop.lpTestOper())); - if (optLoopTable[loopInd].lpFlags & LPFLG_CONST_LIMIT) + if (loop.lpFlags & LPFLG_CONST_LIMIT) { - printf("%d ", optLoopTable[loopInd].lpConstLimit()); + printf("%d ", loop.lpConstLimit()); } - if (optLoopTable[loopInd].lpFlags & LPFLG_VAR_LIMIT) + if (loop.lpFlags & LPFLG_VAR_LIMIT) { - printf("V%02u ", optLoopTable[loopInd].lpVarLimit()); + printf("V%02u ", loop.lpVarLimit()); } printf("]"); @@ -1770,6 +1758,14 @@ class LoopSearch return true; } + //------------------------------------------------------------------------ + // GetExitCount: Return the exit count computed for the loop + // + unsigned char GetExitCount() const + { + return exitCount; + } + private: //------------------------------------------------------------------------ // FindEntry: See if given HEAD flows to valid ENTRY between given TOP and BOTTOM @@ -2463,7 +2459,13 @@ void Compiler::optFindNaturalLoops() loopsThisMethod++; /* keep track of the number of exits */ - loopExitCountTable.record(static_cast(exitCount)); + loopExitCountTable.record(static_cast(search.GetExitCount())); + + // Note that we continue to look for loops even if + // (optLoopCount == MAX_LOOP_NUM), in contrast to the !COUNT_LOOPS code below. + // This gives us a better count and stats. Hopefully it doesn't affect actual codegen. + CLANG_FORMAT_COMMENT_ANCHOR; + #else // COUNT_LOOPS assert(recordedLoop); if (optLoopCount == MAX_LOOP_NUM) @@ -2483,7 +2485,10 @@ void Compiler::optFindNaturalLoops() } } } + +#if !COUNT_LOOPS NO_MORE_LOOPS: +#endif // !COUNT_LOOPS #if COUNT_LOOPS loopCountTable.record(loopsThisMethod); @@ -2599,13 +2604,13 @@ void Compiler::optIdentifyLoopsForAlignment() if (first->getBBWeight(this) >= (opts.compJitAlignLoopMinBlockWeight * BB_UNITY_WEIGHT)) { first->bbFlags |= BBF_LOOP_ALIGN; - JITDUMP("L%02u that starts at " FMT_BB " needs alignment, weight=%f.\n", loopInd, first->bbNum, - first->getBBWeight(this)); + JITDUMP(FMT_LP " that starts at " FMT_BB " needs alignment, weight=" FMT_WT ".\n", loopInd, + first->bbNum, first->getBBWeight(this)); } else { - JITDUMP("Skip alignment for L%02u that starts at " FMT_BB " weight=%f.\n", loopInd, first->bbNum, - first->getBBWeight(this)); + JITDUMP("Skip alignment for " FMT_LP " that starts at " FMT_BB " weight=" FMT_WT ".\n", loopInd, + first->bbNum, first->getBBWeight(this)); } } } @@ -2702,7 +2707,7 @@ void Compiler::optCopyBlkDest(BasicBlock* from, BasicBlock* to) } // Returns true if 'block' is an entry block for any loop in 'optLoopTable' -bool Compiler::optIsLoopEntry(BasicBlock* block) +bool Compiler::optIsLoopEntry(BasicBlock* block) const { for (unsigned char loopInd = 0; loopInd < optLoopCount; loopInd++) { @@ -2752,9 +2757,8 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd) return false; } - JITDUMP("in optCanonicalizeLoop: L%02u has top " FMT_BB " (bottom " FMT_BB - ") with natural loop number L%02u: need to " - "canonicalize\n", + JITDUMP("in optCanonicalizeLoop: " FMT_LP " has top " FMT_BB " (bottom " FMT_BB ") with natural loop number " FMT_LP + ": need to canonicalize\n", loopInd, t->bbNum, optLoopTable[loopInd].lpBottom->bbNum, t->bbNatLoopNum); // Otherwise, the top of this loop is also part of a nested loop. @@ -2883,9 +2887,8 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd) // outside-in, so we shouldn't encounter the new blocks at the loop boundaries, or in the predecessor lists. if (t->bbNum <= topPredBlock->bbNum && topPredBlock->bbNum <= b->bbNum) { - JITDUMP("in optCanonicalizeLoop: 'top' predecessor " FMT_BB " is in the range of L%02u (" FMT_BB ".." FMT_BB - "); not " - "redirecting its bottom edge\n", + JITDUMP("in optCanonicalizeLoop: 'top' predecessor " FMT_BB " is in the range of " FMT_LP " (" FMT_BB + ".." FMT_BB "); not redirecting its bottom edge\n", topPredBlock->bbNum, loopInd, t->bbNum, b->bbNum); continue; } @@ -2913,7 +2916,7 @@ bool Compiler::optCanonicalizeLoop(unsigned char loopInd) newT->bbNum, topPredBlock->bbNum); BasicBlock::weight_t newWeight = newT->getBBWeight(this) + topPredBlock->getBBWeight(this); - newT->setBBWeight(newWeight); + newT->setBBProfileWeight(newWeight); } } } @@ -3696,7 +3699,6 @@ void Compiler::optUnrollLoops() incr = incr->AsOp()->gtOp2; GenTree* init = initStmt->GetRootNode(); - GenTree* test = testStmt->GetRootNode(); /* Make sure everything looks ok */ if ((init->gtOper != GT_ASG) || (init->AsOp()->gtOp1->gtOper != GT_LCL_VAR) || @@ -3830,8 +3832,14 @@ void Compiler::optUnrollLoops() optLoopTable[lnum].lpFlags |= LPFLG_DONT_UNROLL; goto DONE_LOOP; } + // Block weight should no longer have the loop multiplier - newBlock->modifyBBWeight(newBlock->bbWeight / BB_LOOP_WEIGHT_SCALE); + // + // Note this is not quite right, as we may not have upscaled by this amount + // and we might not have upscaled at all, if we had profile data. + // + newBlock->scaleBBWeight(1.0f / BB_LOOP_WEIGHT_SCALE); + // Jump dests are set in a post-pass; make sure CloneBlockState hasn't tried to set them. assert(newBlock->bbJumpDest == nullptr); @@ -4060,31 +4068,6 @@ bool Compiler::optReachWithoutCall(BasicBlock* topBB, BasicBlock* botBB) return true; } -/***************************************************************************** - * - * Find the loop termination test at the bottom of the loop - */ - -static Statement* optFindLoopTermTest(BasicBlock* bottom) -{ - Statement* testStmt = bottom->firstStmt(); - - assert(testStmt != nullptr); - - Statement* result = testStmt->GetPrevStmt(); - -#ifdef DEBUG - while (testStmt->GetNextStmt() != nullptr) - { - testStmt = testStmt->GetNextStmt(); - } - - assert(testStmt == result); -#endif - - return result; -} - //----------------------------------------------------------------------------- // optInvertWhileLoop: modify flow and duplicate code so that for/while loops are // entered at top and tested at bottom (aka loop rotation or bottom testing). @@ -4103,8 +4086,8 @@ static Statement* optFindLoopTermTest(BasicBlock* bottom) // void Compiler::optInvertWhileLoop(BasicBlock* block) { - noway_assert(opts.OptimizationEnabled()); - noway_assert(compCodeOpt() != SMALL_CODE); + assert(opts.OptimizationEnabled()); + assert(compCodeOpt() != SMALL_CODE); /* Optimize while loops into do { } while loop @@ -4140,7 +4123,8 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) /* Does the BB end with an unconditional jump? */ if (block->bbJumpKind != BBJ_ALWAYS || (block->bbFlags & BBF_KEEP_BBJ_ALWAYS)) - { // It can't be one of the ones we use for our exception magic + { + // It can't be one of the ones we use for our exception magic return; } @@ -4151,14 +4135,6 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) return; } - // It has to be a forward jump - // TODO-CQ: Check if we can also optimize the backwards jump as well. - // - if (fgIsForwardBranch(block) == false) - { - return; - } - // Get hold of the jump target BasicBlock* bTest = block->bbJumpDest; @@ -4175,7 +4151,7 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) } // Since test is a BBJ_COND it will have a bbNext - noway_assert(bTest->bbNext); + noway_assert(bTest->bbNext != nullptr); // 'block' must be in the same try region as the condition, since we're going to insert // a duplicated condition in 'block', and the condition might include exception throwing code. @@ -4192,11 +4168,12 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) return; } - Statement* condStmt = optFindLoopTermTest(bTest); + // Find the loop termination test at the bottom of the loop. + Statement* condStmt = bTest->lastStmt(); // bTest must only contain only a jtrue with no other stmts, we will only clone // the conditional, so any other statements will not get cloned - // TODO-CQ: consider cloning the whole bTest block as inserting it after block. + // TODO-CQ: consider cloning the whole bTest block and inserting it after block. // if (bTest->bbStmtList != condStmt) { @@ -4218,6 +4195,15 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) return; } + // It has to be a forward jump. Defer this check until after all the cheap checks + // are done, since it iterates forward in the block list looking for bbJumpDest. + // TODO-CQ: Check if we can also optimize the backwards jump as well. + // + if (fgIsForwardBranch(block) == false) + { + return; + } + /* We call gtPrepareCost to measure the cost of duplicating this tree */ gtPrepareCost(condTree); @@ -4230,7 +4216,7 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) BasicBlock::weight_t const weightTest = bTest->bbWeight; BasicBlock::weight_t const weightNext = block->bbNext->bbWeight; - // If we have profile data then we calculate the number of time + // If we have profile data then we calculate the number of times // the loop will iterate into loopIterations if (fgIsUsingProfileWeights()) { @@ -4240,7 +4226,7 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) { // If this while loop never iterates then don't bother transforming // - if (weightNext == 0) + if (weightNext == BB_ZERO_WEIGHT) { return; } @@ -4251,8 +4237,8 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) // if (!fgProfileWeightsConsistent(weightBlock + weightNext, weightTest)) { - JITDUMP("Profile weights locally inconsistent: block %f, next %f, test %f\n", weightBlock, weightNext, - weightTest); + JITDUMP("Profile weights locally inconsistent: block " FMT_WT ", next " FMT_WT ", test " FMT_WT "\n", + weightBlock, weightNext, weightTest); } else { @@ -4286,35 +4272,46 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) if (loopIterations >= 12.0) { maxDupCostSz *= 2; - } - if (loopIterations >= 96.0) - { - maxDupCostSz *= 2; + if (loopIterations >= 96.0) + { + maxDupCostSz *= 2; + } } - // If the loop condition has a shared static helper, we really want this loop converted - // as not converting the loop will disable loop hoisting, meaning the shared helper will - // be executed on every loop iteration. - int countOfHelpers = 0; - fgWalkTreePre(&condTree, CountSharedStaticHelper, &countOfHelpers); + // If the compare has too high cost then we don't want to dup - if (countOfHelpers > 0 && compCodeOpt() != SMALL_CODE) + bool costIsTooHigh = (estDupCostSz > maxDupCostSz); + + int countOfHelpers = 0; + if (costIsTooHigh) { - maxDupCostSz += 24 * min(countOfHelpers, (int)(loopIterations + 1.5)); - } + // If we already know that the cost is acceptable, then don't waste time walking the tree + // counting shared static helpers. + // + // If the loop condition has a shared static helper, we really want this loop converted + // as not converting the loop will disable loop hoisting, meaning the shared helper will + // be executed on every loop iteration. + fgWalkTreePre(&condTree, CountSharedStaticHelper, &countOfHelpers); - // If the compare has too high cost then we don't want to dup + if (countOfHelpers > 0) + { + maxDupCostSz += 24 * min(countOfHelpers, (int)(loopIterations + 1.5)); - bool costIsTooHigh = (estDupCostSz > maxDupCostSz); + // Is the cost too high now? + costIsTooHigh = (estDupCostSz > maxDupCostSz); + } + } #ifdef DEBUG if (verbose) { + // Note that `countOfHelpers = 0` means either there were zero helpers, or the tree walk to count them was not + // done. printf("\nDuplication of loop condition [%06u] is %s, because the cost of duplication (%i) is %s than %i," "\n loopIterations = %7.3f, countOfHelpers = %d, validProfileWeights = %s\n", - condTree->gtTreeID, costIsTooHigh ? "not done" : "performed", estDupCostSz, + dspTreeID(condTree), costIsTooHigh ? "not done" : "performed", estDupCostSz, costIsTooHigh ? "greater" : "less or equal", maxDupCostSz, loopIterations, countOfHelpers, - allProfileWeightsAreValid ? "true" : "false"); + dspBool(allProfileWeightsAreValid)); } #endif @@ -4378,14 +4375,13 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) #ifdef DEBUG if (verbose) { - printf("\nDuplicated loop condition in " FMT_BB " for loop (" FMT_BB " - " FMT_BB ")", block->bbNum, + printf("\nDuplicated loop condition in " FMT_BB " for loop (" FMT_BB " - " FMT_BB ")\n", block->bbNum, block->bbNext->bbNum, bTest->bbNum); - printf("\nEstimated code size expansion is %d\n ", estDupCostSz); + printf("Estimated code size expansion is %d\n", estDupCostSz); gtDispStmt(copyOfCondStmt); } - -#endif +#endif // DEBUG // If we have profile data for all blocks and we know that we are cloning the // bTest block into block and thus changing the control flow from block so @@ -4396,7 +4392,8 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) { // Update the weight for bTest // - JITDUMP("Reducing profile weight of " FMT_BB " from %f to %f\n", bTest->bbNum, weightTest, weightNext); + JITDUMP("Reducing profile weight of " FMT_BB " from " FMT_WT " to " FMT_WT "\n", bTest->bbNum, weightTest, + weightNext); bTest->bbWeight = weightNext; // Determine the new edge weights. @@ -4418,17 +4415,17 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) flowList* const edgeTestToNext = fgGetPredForBlock(bTest->bbJumpDest, bTest); flowList* const edgeTestToAfter = fgGetPredForBlock(bTest->bbNext, bTest); - JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to %f (iterate loop)\n", bTest->bbNum, + JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to " FMT_WT " (iterate loop)\n", bTest->bbNum, bTest->bbJumpDest->bbNum, testToNextWeight); - JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to %f (exit loop)\n", bTest->bbNum, bTest->bbNext->bbNum, - testToAfterWeight); + JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to " FMT_WT " (exit loop)\n", bTest->bbNum, + bTest->bbNext->bbNum, testToAfterWeight); edgeTestToNext->setEdgeWeights(testToNextWeight, testToNextWeight, bTest->bbJumpDest); edgeTestToAfter->setEdgeWeights(testToAfterWeight, testToAfterWeight, bTest->bbNext); // Adjust edges out of block, using the same distribution. // - JITDUMP("Profile weight of " FMT_BB " remains unchanged at %f\n", block->bbNum, weightBlock); + JITDUMP("Profile weight of " FMT_BB " remains unchanged at " FMT_WT "\n", block->bbNum, weightBlock); BasicBlock::weight_t const blockToNextLikelihood = testToNextLikelihood; BasicBlock::weight_t const blockToAfterLikelihood = testToAfterLikelihood; @@ -4439,9 +4436,9 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) flowList* const edgeBlockToNext = fgGetPredForBlock(block->bbNext, block); flowList* const edgeBlockToAfter = fgGetPredForBlock(block->bbJumpDest, block); - JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to %f (enter loop)\n", block->bbNum, block->bbNext->bbNum, - blockToNextWeight); - JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to %f (avoid loop)\n", block->bbNum, + JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to " FMT_WT " (enter loop)\n", block->bbNum, + block->bbNext->bbNum, blockToNextWeight); + JITDUMP("Setting weight of " FMT_BB " -> " FMT_BB " to " FMT_WT " (avoid loop)\n", block->bbNum, block->bbJumpDest->bbNum, blockToAfterWeight); edgeBlockToNext->setEdgeWeights(blockToNextWeight, blockToNextWeight, block->bbNext); @@ -4452,7 +4449,7 @@ void Compiler::optInvertWhileLoop(BasicBlock* block) // fgDebugCheckIncomingProfileData(block->bbNext); fgDebugCheckIncomingProfileData(block->bbJumpDest); -#endif +#endif // DEBUG } } @@ -4467,6 +4464,11 @@ PhaseStatus Compiler::optInvertLoops() noway_assert(opts.OptimizationEnabled()); noway_assert(fgModified == false); + if (compCodeOpt() == SMALL_CODE) + { + return PhaseStatus::MODIFIED_NOTHING; + } + for (BasicBlock* block = fgFirstBB; block; block = block->bbNext) { // Make sure the appropriate fields are initialized @@ -4478,10 +4480,7 @@ PhaseStatus Compiler::optInvertLoops() continue; } - if (compCodeOpt() != SMALL_CODE) - { - optInvertWhileLoop(block); - } + optInvertWhileLoop(block); } bool madeChanges = fgModified; @@ -4696,7 +4695,7 @@ PhaseStatus Compiler::optFindLoops() bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext* context) { JITDUMP("------------------------------------------------------------\n"); - JITDUMP("Deriving cloning conditions for L%02u\n", loopNum); + JITDUMP("Deriving cloning conditions for " FMT_LP "\n", loopNum); LoopDsc* loop = &optLoopTable[loopNum]; JitExpandArrayStack* optInfos = context->GetLoopOptInfo(loopNum); @@ -4713,7 +4712,8 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext // Init conditions if (loop->lpFlags & LPFLG_CONST_INIT) { - // Only allowing const init at this time. + // Only allowing non-negative const init at this time. + // REVIEW: why? if (loop->lpConstInit < 0) { JITDUMP("> Init %d is invalid\n", loop->lpConstInit); @@ -4722,7 +4722,7 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext } else if (loop->lpFlags & LPFLG_VAR_INIT) { - // limitVar >= 0 + // initVar >= 0 LC_Condition geZero(GT_GE, LC_Expr(LC_Ident(loop->lpVarInit, LC_Ident::Var)), LC_Expr(LC_Ident(0, LC_Ident::Const))); context->EnsureConditions(loopNum)->Push(geZero); @@ -4841,23 +4841,23 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext // for (j in 0..n) // for (k in 0..n) // Inner most loop is being cloned. Cloning needs to check if // // (n <= a[i][j].len) and other safer conditions to take the fast path -// a[i][j][k] = 0; +// a[i][j][k] = 0 // // Now, we want to deref a[i][j] to invoke length operator on it to perform the cloning fast path check. // This involves deref of (a), (a[i]), (a[i][j]), therefore, the following should first // be true to do the deref. // -// (a != null) && (i < a.len) && (a[i] != null) && (j < a[i].len) && (a[i][j] != null) --> (1) +// (a != null) && (i < a.len) && (a[i] != null) && (j < a[i].len) && (a[i][j] != null) --> condition set (1) // // Note the short circuiting AND. Implication: these conditions should be performed in separate // blocks each of which will branch to slow path if the condition evaluates to false. // -// Now, imagine a situation where we have -// a[x][y][k] = 20 and a[i][j][k] = 0 -// also in the inner most loop where x, y are parameters, then our conditions will have -// to include -// (x < a.len) && -// (y < a[x].len) +// Now, imagine a situation where, in the inner loop above, in addition to "a[i][j][k] = 0" we +// also have: +// a[x][y][k] = 20 +// where x and y are parameters, then our conditions will have to include: +// (x < a.len) && +// (y < a[x].len) // in addition to the above conditions (1) to get rid of bounds check on index 'k' // // But these conditions can be checked together with conditions @@ -4909,6 +4909,7 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext // b => { // i => {} // } +// // In this method, we will construct such a tree by descending depth first into the array // index operation and forming a tree structure as we encounter the array or the index variables. // @@ -4923,7 +4924,6 @@ bool Compiler::optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext // // and so on. // -// bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* context) { JitExpandArrayStack nodes(getAllocator()); @@ -4972,6 +4972,7 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con #ifdef DEBUG if (verbose) { + printf("Deref condition tree:\n"); for (unsigned i = 0; i < nodes.Size(); ++i) { if (i != 0) @@ -4994,7 +4995,10 @@ bool Compiler::optComputeDerefConditions(unsigned loopNum, LoopCloneContext* con // So add 1 after rank * 2. unsigned condBlocks = (unsigned)maxRank * 2 + 1; - // Heuristic to not create too many blocks; + // Heuristic to not create too many blocks. + // REVIEW: due to the definition of `condBlocks`, above, the effective max is 3 blocks, meaning + // `maxRank` of 1. Question: should the heuristic allow more blocks to be created in some situations? + // REVIEW: make this based on a COMPlus configuration? if (condBlocks > 4) { return false; @@ -5031,7 +5035,7 @@ void Compiler::optDebugLogLoopCloning(BasicBlock* block, Statement* insertBefore fgInsertStmtBefore(block, insertBefore, stmt); fgMorphBlockStmt(block, stmt DEBUGARG("Debug log loop cloning")); } -#endif +#endif // DEBUG //------------------------------------------------------------------------ // optPerformStaticOptimizations: Perform the optimizations for the optimization @@ -5068,7 +5072,7 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* { LcJaggedArrayOptInfo* arrIndexInfo = optInfo->AsLcJaggedArrayOptInfo(); compCurBB = arrIndexInfo->arrIndex.useBlock; - optRemoveRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt); + optRemoveCommaBasedRangeCheck(arrIndexInfo->arrIndex.bndsChks[arrIndexInfo->dim], arrIndexInfo->stmt); DBEXEC(dynamicPath, optDebugLogLoopCloning(arrIndexInfo->arrIndex.useBlock, arrIndexInfo->stmt)); } break; @@ -5082,40 +5086,65 @@ void Compiler::optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* } //---------------------------------------------------------------------------- -// optCanCloneLoops: Use the environment flag to determine whether loop -// cloning is allowed to be performed. +// optLoopCloningEnabled: Determine whether loop cloning is allowed. It is allowed +// in release builds. For debug builds, use the value of the COMPlus_JitCloneLoops +// flag (which defaults to 1, or allowed). // -// Return Value: -// Returns true in debug builds if COMPlus_JitCloneLoops flag is set. -// Disabled for retail for now. +// Return Value: +// true if loop cloning is allowed, false if disallowed. // -bool Compiler::optCanCloneLoops() +bool Compiler::optLoopCloningEnabled() { - // Enabled for retail builds now. - unsigned cloneLoopsFlag = 1; #ifdef DEBUG - cloneLoopsFlag = JitConfig.JitCloneLoops(); + return JitConfig.JitCloneLoops() != 0; +#else + return true; #endif - return (cloneLoopsFlag != 0); } //---------------------------------------------------------------------------- -// optIsLoopClonable: Determine whether this loop can be cloned. +// optIsLoopClonable: Determine whether this loop can be cloned. // -// Arguments: +// Arguments: // loopInd loop index which needs to be checked if it can be cloned. // -// Return Value: -// Returns true if the loop can be cloned. If it returns false -// prints a message in debug as why the loop can't be cloned. +// Return Value: +// Returns true if the loop can be cloned. If it returns false, +// it prints a message to the JIT dump describing why the loop can't be cloned. +// +// Notes: if `true` is returned, then `fgReturnCount` is increased by the number of +// return blocks in the loop that will be cloned. (REVIEW: this 'predicate' function +// doesn't seem like the right place to do this change.) // bool Compiler::optIsLoopClonable(unsigned loopInd) { - // First, for now, make sure the loop doesn't have any embedded exception handling -- I don't want to tackle - // inserting new EH regions in the exception table yet. - BasicBlock* stopAt = optLoopTable[loopInd].lpBottom->bbNext; + const LoopDsc& loop = optLoopTable[loopInd]; + + if (!(loop.lpFlags & LPFLG_ITER)) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". No LPFLG_ITER flag.\n", loopInd); + return false; + } + + if (loop.lpFlags & LPFLG_REMOVED) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". It is marked LPFLG_REMOVED.\n", loopInd); + return false; + } + + // Make sure the loop doesn't have any embedded exception handling. + // Walk the loop blocks from lexically first to lexically last (all blocks in this region must be + // part of the loop), looking for a `try` begin block. Note that a loop must entirely contain any + // EH region, or be itself entirely contained within an EH region. Thus, looking just for a `try` + // begin is sufficient; there is no need to look for other EH constructs, such as a `catch` begin. + // + // TODO: this limitation could be removed if we do the work to insert new EH regions in the exception table, + // for the cloned loop (and its embedded EH regions). + // + // Also, count the number of return blocks within the loop for future use. + BasicBlock* stopAt = loop.lpBottom->bbNext; unsigned loopRetCount = 0; - for (BasicBlock* blk = optLoopTable[loopInd].lpFirst; blk != stopAt; blk = blk->bbNext) + for (BasicBlock* blk = loop.lpFirst; blk != stopAt; blk = blk->bbNext) { if (blk->bbJumpKind == BBJ_RETURN) { @@ -5123,23 +5152,24 @@ bool Compiler::optIsLoopClonable(unsigned loopInd) } if (bbIsTryBeg(blk)) { - JITDUMP("Loop cloning: rejecting loop %d in %s, because it has a try begin.\n", loopInd, info.compFullName); + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". It has a `try` begin.\n", loopInd); return false; } } // Is the entry block a handler or filter start? If so, then if we cloned, we could create a jump // into the middle of a handler (to go to the cloned copy.) Reject. - if (bbIsHandlerBeg(optLoopTable[loopInd].lpEntry)) + if (bbIsHandlerBeg(loop.lpEntry)) { - JITDUMP("Loop cloning: rejecting loop because entry block is a handler start.\n"); + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Entry block is a handler start.\n", loopInd); return false; } // If the head and entry are in different EH regions, reject. - if (!BasicBlock::sameEHRegion(optLoopTable[loopInd].lpHead, optLoopTable[loopInd].lpEntry)) + if (!BasicBlock::sameEHRegion(loop.lpHead, loop.lpEntry)) { - JITDUMP("Loop cloning: rejecting loop because head and entry blocks are in different EH regions.\n"); + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Head and entry blocks are in different EH regions.\n", + loopInd); return false; } @@ -5149,10 +5179,10 @@ bool Compiler::optIsLoopClonable(unsigned loopInd) // that block; this is one of those cases. This could be fixed fairly easily; for example, // we could add a dummy nop block after the (cloned) loop bottom, in the same handler scope as the // loop. This is just a corner to cut to get this working faster. - BasicBlock* bbAfterLoop = optLoopTable[loopInd].lpBottom->bbNext; + BasicBlock* bbAfterLoop = loop.lpBottom->bbNext; if (bbAfterLoop != nullptr && bbIsHandlerBeg(bbAfterLoop)) { - JITDUMP("Loop cloning: rejecting loop because next block after bottom is a handler start.\n"); + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Next block after bottom is a handler start.\n", loopInd); return false; } @@ -5166,40 +5196,110 @@ bool Compiler::optIsLoopClonable(unsigned loopInd) #endif // JIT32_GCENCODER if (fgReturnCount + loopRetCount > epilogLimit) { - JITDUMP("Loop cloning: rejecting loop because it has %d returns; if added to previously-existing %d returns, " - "would exceed the limit of %d.\n", - loopRetCount, fgReturnCount, epilogLimit); + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". It has %d returns;" + " if added to previously existing %d returns, it would exceed the limit of %d.\n", + loopInd, loopRetCount, fgReturnCount, epilogLimit); + return false; + } + + unsigned ivLclNum = loop.lpIterVar(); + if (lvaVarAddrExposed(ivLclNum)) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Rejected V%02u as iter var because is address-exposed.\n", + loopInd, ivLclNum); + return false; + } + + BasicBlock* head = loop.lpHead; + BasicBlock* end = loop.lpBottom; + BasicBlock* beg = head->bbNext; + + if (end->bbJumpKind != BBJ_COND) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Couldn't find termination test.\n", loopInd); + return false; + } + + if (end->bbJumpDest != beg) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Branch at loop 'end' not looping to 'begin'.\n", loopInd); + return false; + } + + // TODO-CQ: CLONE: Mark increasing or decreasing loops. + if ((loop.lpIterOper() != GT_ADD) || (loop.lpIterConst() != 1)) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Loop iteration operator not matching.\n", loopInd); return false; } + if ((loop.lpFlags & LPFLG_CONST_LIMIT) == 0 && (loop.lpFlags & LPFLG_VAR_LIMIT) == 0 && + (loop.lpFlags & LPFLG_ARRLEN_LIMIT) == 0) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Loop limit is neither constant, variable or array length.\n", + loopInd); + return false; + } + + if (!((GenTree::StaticOperIs(loop.lpTestOper(), GT_LT, GT_LE) && (loop.lpIterOper() == GT_ADD)) || + (GenTree::StaticOperIs(loop.lpTestOper(), GT_GT, GT_GE) && (loop.lpIterOper() == GT_SUB)))) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP + ". Loop test (%s) doesn't agree with the direction (%s) of the loop.\n", + loopInd, GenTree::OpName(loop.lpTestOper()), GenTree::OpName(loop.lpIterOper())); + return false; + } + + if (!(loop.lpTestTree->OperKind() & GTK_RELOP) || !(loop.lpTestTree->gtFlags & GTF_RELOP_ZTT)) + { + JITDUMP("Loop cloning: rejecting loop " FMT_LP ". Loop inversion NOT present, loop test [%06u] may not protect " + "entry from head.\n", + loopInd, loop.lpTestTree->gtTreeID); + return false; + } + +#ifdef DEBUG + GenTree* op1 = loop.lpIterator(); + assert((op1->gtOper == GT_LCL_VAR) && (op1->AsLclVarCommon()->GetLclNum() == ivLclNum)); +#endif + // Otherwise, we're going to add those return blocks. fgReturnCount += loopRetCount; return true; } -/***************************************************************************** - * - * Identify loop cloning opportunities, derive loop cloning conditions, - * perform loop cloning, use the derived conditions to choose which - * path to take. - */ +//------------------------------------------------------------------------ +// optCloneLoops: Implements loop cloning optimization. +// +// Identify loop cloning opportunities, derive loop cloning conditions, +// perform loop cloning, use the derived conditions to choose which +// path to take. +// void Compiler::optCloneLoops() { JITDUMP("\n*************** In optCloneLoops()\n"); - if (optLoopCount == 0 || !optCanCloneLoops()) + if (optLoopCount == 0) { + JITDUMP(" No loops to clone\n"); + return; + } + if (!optLoopCloningEnabled()) + { + JITDUMP(" Loop cloning disabled\n"); return; } #ifdef DEBUG if (verbose) { - printf("Blocks/Trees at start of phase\n"); - fgDispBasicBlocks(true); + printf("\nBefore loop cloning:\n"); + fgDispBasicBlocks(/*dumpTrees*/ true); } #endif + unsigned optStaticallyOptimizedLoops = 0; + LoopCloneContext context(optLoopCount, getAllocator()); // Obtain array optimization candidates in the context. @@ -5234,6 +5334,8 @@ void Compiler::optCloneLoops() // have to take the cloned path. optPerformStaticOptimizations(i, &context DEBUGARG(false)); + ++optStaticallyOptimizedLoops; + // No need to clone. context.CancelLoopOptInfo(i); } @@ -5279,7 +5381,9 @@ void Compiler::optCloneLoops() #ifdef DEBUG if (verbose) { - printf("\nAfter loop cloning:\n"); + printf("Loops cloned: %d\n", optLoopsCloned); + printf("Loops statically optimized: %d\n", optStaticallyOptimizedLoops); + printf("After loop cloning:\n"); fgDispBasicBlocks(/*dumpTrees*/ true); } @@ -5287,14 +5391,23 @@ void Compiler::optCloneLoops() #endif } +//------------------------------------------------------------------------ +// optCloneLoop: Perform the mechanical cloning of the specified loop +// +// Arguments: +// loopInd - loop index of loop to clone +// context - data structure where all loop cloning info is kept. +// void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) { assert(loopInd < optLoopCount); - JITDUMP("\nCloning loop %d: [h: %d, f: %d, t: %d, e: %d, b: %d, c: %d].\n", loopInd, - optLoopTable[loopInd].lpHead->bbNum, optLoopTable[loopInd].lpFirst->bbNum, - optLoopTable[loopInd].lpTop->bbNum, optLoopTable[loopInd].lpEntry->bbNum, - optLoopTable[loopInd].lpBottom->bbNum, optLoopTable[loopInd].lpChild); + LoopDsc& loop = optLoopTable[loopInd]; + + JITDUMP("\nCloning loop " FMT_LP ": [head: " FMT_BB ", first: " FMT_BB ", top: " FMT_BB ", entry: " FMT_BB + ", bottom: " FMT_BB ", child: " FMT_LP "].\n", + loopInd, loop.lpHead->bbNum, loop.lpFirst->bbNum, loop.lpTop->bbNum, loop.lpEntry->bbNum, + loop.lpBottom->bbNum, loop.lpChild); // Determine the depth of the loop, so we can properly weight blocks added (outside the cloned loop blocks). unsigned depth = optLoopDepth(loopInd); @@ -5308,25 +5421,25 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // If we're in a non-natural loop, the ambient weight might be higher than we computed above. // Be safe by taking the max with the head block's weight. - ambientWeight = max(ambientWeight, optLoopTable[loopInd].lpHead->bbWeight); + ambientWeight = max(ambientWeight, loop.lpHead->bbWeight); // This is the containing loop, if any -- to label any blocks we create that are outside // the loop being cloned. - unsigned char ambientLoop = optLoopTable[loopInd].lpParent; + unsigned char ambientLoop = loop.lpParent; // First, make sure that the loop has a unique header block, creating an empty one if necessary. optEnsureUniqueHead(loopInd, ambientWeight); - // We're going to make - + // We're going to transform this loop: + // // H --> E // F // T // E - // B ?-> T + // B ?-> T // X // - // become + // to this pair of loops: // // H ?-> E2 // H2--> E (Optional; if E == T == F, let H fall through to F/T/E) @@ -5341,24 +5454,23 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // B2 ?-> T2 // X - BasicBlock* h = optLoopTable[loopInd].lpHead; + BasicBlock* h = loop.lpHead; if (h->bbJumpKind != BBJ_NONE && h->bbJumpKind != BBJ_ALWAYS) { // Make a new block to be the unique entry to the loop. - assert(h->bbJumpKind == BBJ_COND && h->bbNext == optLoopTable[loopInd].lpEntry); - BasicBlock* newH = fgNewBBafter(BBJ_NONE, h, - /*extendRegion*/ true); - newH->bbWeight = (newH->isRunRarely() ? 0 : ambientWeight); + assert(h->bbJumpKind == BBJ_COND && h->bbNext == loop.lpEntry); + BasicBlock* newH = fgNewBBafter(BBJ_NONE, h, /*extendRegion*/ true); + newH->bbWeight = newH->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; BlockSetOps::Assign(this, newH->bbReach, h->bbReach); // This is in the scope of a surrounding loop, if one exists -- the parent of the loop we're cloning. newH->bbNatLoopNum = ambientLoop; h = newH; - optUpdateLoopHead(loopInd, optLoopTable[loopInd].lpHead, h); + optUpdateLoopHead(loopInd, loop.lpHead, h); } // First, make X2 after B, if necessary. (Not necessary if b is a BBJ_ALWAYS.) // "newPred" will be the predecessor of the blocks of the cloned loop. - BasicBlock* b = optLoopTable[loopInd].lpBottom; + BasicBlock* b = loop.lpBottom; BasicBlock* newPred = b; if (b->bbJumpKind != BBJ_ALWAYS) { @@ -5366,7 +5478,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) if (x != nullptr) { BasicBlock* x2 = fgNewBBafter(BBJ_ALWAYS, b, /*extendRegion*/ true); - x2->bbWeight = (x2->isRunRarely() ? 0 : ambientWeight); + x2->bbWeight = x2->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; // This is in the scope of a surrounding loop, if one exists -- the parent of the loop we're cloning. x2->bbNatLoopNum = ambientLoop; @@ -5380,17 +5492,16 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // Now we'll make "h2", after "h" to go to "e" -- unless the loop is a do-while, // so that "h" already falls through to "e" (e == t == f). BasicBlock* h2 = nullptr; - if (optLoopTable[loopInd].lpHead->bbNext != optLoopTable[loopInd].lpEntry) + if (loop.lpHead->bbNext != loop.lpEntry) { - BasicBlock* h2 = fgNewBBafter(BBJ_ALWAYS, optLoopTable[loopInd].lpHead, - /*extendRegion*/ true); - h2->bbWeight = (h2->isRunRarely() ? 0 : ambientWeight); + BasicBlock* h2 = fgNewBBafter(BBJ_ALWAYS, loop.lpHead, /*extendRegion*/ true); + h2->bbWeight = h2->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; // This is in the scope of a surrounding loop, if one exists -- the parent of the loop we're cloning. h2->bbNatLoopNum = ambientLoop; - h2->bbJumpDest = optLoopTable[loopInd].lpEntry; - optUpdateLoopHead(loopInd, optLoopTable[loopInd].lpHead, h2); + h2->bbJumpDest = loop.lpEntry; + optUpdateLoopHead(loopInd, loop.lpHead, h2); } // Now we'll clone the blocks of the loop body. @@ -5398,11 +5509,9 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) BasicBlock* newBot = nullptr; BlockToBlockMap* blockMap = new (getAllocator()) BlockToBlockMap(getAllocator()); - for (BasicBlock* blk = optLoopTable[loopInd].lpFirst; blk != optLoopTable[loopInd].lpBottom->bbNext; - blk = blk->bbNext) + for (BasicBlock* blk = loop.lpFirst; blk != loop.lpBottom->bbNext; blk = blk->bbNext) { - BasicBlock* newBlk = fgNewBBafter(blk->bbJumpKind, newPred, - /*extendRegion*/ true); + BasicBlock* newBlk = fgNewBBafter(blk->bbJumpKind, newPred, /*extendRegion*/ true); // Call CloneBlockState to make a copy of the block's statements (and attributes), and assert that it // has a return value indicating success, because optCanOptimizeByLoopCloningVisitor has already @@ -5424,7 +5533,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // TODO-Cleanup: The above clones the bbNatLoopNum, which is incorrect. Eventually, we should probably insert // the cloned loop in the loop table. For now, however, we'll just make these blocks be part of the surrounding // loop, if one exists -- the parent of the loop we're cloning. - newBlk->bbNatLoopNum = optLoopTable[loopInd].lpParent; + newBlk->bbNatLoopNum = loop.lpParent; if (newFirst == nullptr) { @@ -5439,10 +5548,8 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) optPerformStaticOptimizations(loopInd, context DEBUGARG(true)); // Now go through the new blocks, remapping their jump targets within the loop. - for (BasicBlock* blk = optLoopTable[loopInd].lpFirst; blk != optLoopTable[loopInd].lpBottom->bbNext; - blk = blk->bbNext) + for (BasicBlock* blk = loop.lpFirst; blk != loop.lpBottom->bbNext; blk = blk->bbNext) { - BasicBlock* newblk = nullptr; bool b = blockMap->Lookup(blk, &newblk); assert(b && newblk != nullptr); @@ -5456,12 +5563,12 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) optRedirectBlock(newblk, blockMap); } - assert((h->bbJumpKind == BBJ_NONE && (h->bbNext == h2 || h->bbNext == optLoopTable[loopInd].lpEntry)) || + assert((h->bbJumpKind == BBJ_NONE && (h->bbNext == h2 || h->bbNext == loop.lpEntry)) || (h->bbJumpKind == BBJ_ALWAYS)); // If all the conditions are true, go to E2. BasicBlock* e2 = nullptr; - bool foundIt = blockMap->Lookup(optLoopTable[loopInd].lpEntry, &e2); + bool foundIt = blockMap->Lookup(loop.lpEntry, &e2); h->bbJumpKind = BBJ_COND; @@ -5480,7 +5587,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // Create a unique header for the slow path. BasicBlock* slowHead = fgNewBBafter(BBJ_ALWAYS, h, true); - slowHead->bbWeight = (h->isRunRarely() ? 0 : ambientWeight); + slowHead->bbWeight = h->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; slowHead->bbNatLoopNum = ambientLoop; slowHead->bbJumpDest = e2; @@ -5490,7 +5597,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // If h2 is present it is already the head or replace 'h' by 'condLast'. if (h2 == nullptr) { - optUpdateLoopHead(loopInd, optLoopTable[loopInd].lpHead, condLast); + optUpdateLoopHead(loopInd, loop.lpHead, condLast); } assert(foundIt && e2 != nullptr); @@ -5498,7 +5605,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // initialize the loop counter immediately before entering the loop, but we've left a shared // initialization of the loop counter up above the test that determines which version of the // loop to take. - optLoopTable[loopInd].lpFlags |= LPFLG_DONT_UNROLL; + loop.lpFlags |= LPFLG_DONT_UNROLL; fgUpdateChangedFlowGraph(); } @@ -5518,7 +5625,7 @@ void Compiler::optCloneLoop(unsigned loopInd, LoopCloneContext* context) // Operation: // Create the following structure. // -// Note below that the cond0 is inverted in head i.e., if true jump to cond1. This is because +// Note below that the cond0 is inverted in head, i.e., if true jump to cond1. This is because // condn cannot jtrue to loop head h2. It has to be from a direct pred block. // // cond0 (in h) -?> cond1 @@ -5555,7 +5662,7 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, curCond->inheritWeight(head); curCond->bbNatLoopNum = head->bbNatLoopNum; - JITDUMP("Created new " FMT_BB " for new level\n", curCond->bbNum); + JITDUMP("Created new " FMT_BB " for new level %u\n", curCond->bbNum, i); } // Finally insert cloning conditions after all deref conditions have been inserted. @@ -5563,12 +5670,25 @@ BasicBlock* Compiler::optInsertLoopChoiceConditions(LoopCloneContext* context, return curCond; } +//------------------------------------------------------------------------ +// OptEnsureUniqueHead: Ensure that loop "loopInd" has a unique head block. +// If the existing entry has non-loop predecessors other than the head entry, +// create a new, empty block that goes (only) to the entry, and redirects the +// preds of the entry to this new block. Sets the weight of the newly created +// block to "ambientWeight". +// +// Arguments: +// loopInd - index of loop to process +// ambientWeight - weight to give the new head, if created. +// void Compiler::optEnsureUniqueHead(unsigned loopInd, BasicBlock::weight_t ambientWeight) { - BasicBlock* h = optLoopTable[loopInd].lpHead; - BasicBlock* t = optLoopTable[loopInd].lpTop; - BasicBlock* e = optLoopTable[loopInd].lpEntry; - BasicBlock* b = optLoopTable[loopInd].lpBottom; + LoopDsc& loop = optLoopTable[loopInd]; + + BasicBlock* h = loop.lpHead; + BasicBlock* t = loop.lpTop; + BasicBlock* e = loop.lpEntry; + BasicBlock* b = loop.lpBottom; // If "h" dominates the entry block, then it is the unique header. if (fgDominate(h, e)) @@ -5584,8 +5704,8 @@ void Compiler::optEnsureUniqueHead(unsigned loopInd, BasicBlock::weight_t ambien // (We will only create loops that are entirely within a region.) BasicBlock* h2 = fgNewBBafter(BBJ_ALWAYS, beforeTop, true); // This is in the containing loop. - h2->bbNatLoopNum = optLoopTable[loopInd].lpParent; - h2->bbWeight = (h2->isRunRarely() ? 0 : ambientWeight); + h2->bbNatLoopNum = loop.lpParent; + h2->bbWeight = h2->isRunRarely() ? BB_ZERO_WEIGHT : ambientWeight; // We don't care where it was put; splice it between beforeTop and top. if (beforeTop->bbNext != h2) @@ -5618,7 +5738,7 @@ void Compiler::optEnsureUniqueHead(unsigned loopInd, BasicBlock::weight_t ambien optRedirectBlock(predBlock, blockMap); } - optUpdateLoopHead(loopInd, optLoopTable[loopInd].lpHead, h2); + optUpdateLoopHead(loopInd, loop.lpHead, h2); } /***************************************************************************** @@ -6340,8 +6460,8 @@ void Compiler::optPerformHoistExpr(GenTree* origExpr, unsigned lnum) { printf("\nHoisting a copy of "); printTreeID(origExpr); - printf(" into PreHeader for loop L%02u <" FMT_BB ".." FMT_BB ">:\n", lnum, optLoopTable[lnum].lpFirst->bbNum, - optLoopTable[lnum].lpBottom->bbNum); + printf(" into PreHeader for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum, + optLoopTable[lnum].lpFirst->bbNum, optLoopTable[lnum].lpBottom->bbNum); gtDispTree(origExpr); printf("\n"); } @@ -6697,7 +6817,7 @@ void Compiler::optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt) #ifdef DEBUG if (verbose) { - printf("optHoistLoopCode for loop L%02u <" FMT_BB ".." FMT_BB ">:\n", lnum, begn, endn); + printf("optHoistLoopCode for loop " FMT_LP " <" FMT_BB ".." FMT_BB ">:\n", lnum, begn, endn); printf(" Loop body %s a call\n", pLoopDsc->lpContainsCall ? "contains" : "does not contain"); printf(" Loop has %s\n", (pLoopDsc->lpFlags & LPFLG_ONE_EXIT) ? "single exit" : "multiple exits"); } @@ -7335,10 +7455,10 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo BasicBlock* block = blocks->Pop(); BasicBlock::weight_t blockWeight = block->getBBWeight(this); - JITDUMP(" optHoistLoopBlocks " FMT_BB " (weight=%6s) of loop L%02u <" FMT_BB ".." FMT_BB + JITDUMP(" optHoistLoopBlocks " FMT_BB " (weight=%6s) of loop " FMT_LP " <" FMT_BB ".." FMT_BB ">, firstBlock is %s\n", block->bbNum, refCntWtd2str(blockWeight), loopNum, loopDsc->lpFirst->bbNum, loopDsc->lpBottom->bbNum, - (block == loopDsc->lpEntry) ? "true" : "false"); + dspBool(block == loopDsc->lpEntry)); if (blockWeight < (BB_UNITY_WEIGHT / 10)) { @@ -7537,7 +7657,7 @@ void Compiler::fgCreateLoopPreHeader(unsigned lnum) #ifdef DEBUG if (verbose) { - printf("\nCreated PreHeader (" FMT_BB ") for loop L%02u (" FMT_BB " - " FMT_BB "), with weight = %s\n", + printf("\nCreated PreHeader (" FMT_BB ") for loop " FMT_LP " (" FMT_BB " - " FMT_BB "), with weight = %s\n", preHead->bbNum, lnum, top->bbNum, pLoopDsc->lpBottom->bbNum, refCntWtd2str(preHead->getBBWeight(this))); } #endif @@ -7547,9 +7667,9 @@ void Compiler::fgCreateLoopPreHeader(unsigned lnum) if (fgIsUsingProfileWeights() && (head->bbJumpKind == BBJ_COND)) { - if ((head->bbWeight == 0) || (head->bbNext->bbWeight == 0)) + if ((head->bbWeight == BB_ZERO_WEIGHT) || (head->bbNext->bbWeight == BB_ZERO_WEIGHT)) { - preHead->bbWeight = 0; + preHead->bbWeight = BB_ZERO_WEIGHT; preHead->bbFlags |= BBF_RUN_RARELY; } else @@ -7755,7 +7875,7 @@ void Compiler::fgCreateLoopPreHeader(unsigned lnum) #ifdef DEBUG if (verbose) { - printf("Same PreHeader (" FMT_BB ") can be used for loop L%02u (" FMT_BB " - " FMT_BB ")\n\n", + printf("Same PreHeader (" FMT_BB ") can be used for loop " FMT_LP " (" FMT_BB " - " FMT_BB ")\n\n", preHead->bbNum, l, top->bbNum, optLoopTable[l].lpBottom->bbNum); } #endif @@ -8154,7 +8274,7 @@ void Compiler::AddContainsCallAllContainingLoops(unsigned lnum) { BasicBlock* first = optLoopTable[lnum].lpFirst; first->bbFlags &= ~BBF_LOOP_ALIGN; - JITDUMP("Removing LOOP_ALIGN flag for L%02u that starts at " FMT_BB " because loop has a call.\n", lnum, + JITDUMP("Removing LOOP_ALIGN flag for " FMT_LP " that starts at " FMT_BB " because loop has a call.\n", lnum, first->bbNum); } #endif @@ -8211,23 +8331,33 @@ void Compiler::AddModifiedElemTypeAllContainingLoops(unsigned lnum, CORINFO_CLAS } //------------------------------------------------------------------------------ -// optRemoveRangeCheck : Given an array index node, mark it as not needing a range check. +// optRemoveRangeCheck : Given an indexing node, mark it as not needing a range check. // // Arguments: -// tree - Range check tree -// stmt - Statement the tree belongs to - -void Compiler::optRemoveRangeCheck(GenTree* tree, Statement* stmt) +// check - Range check tree, the raw CHECK node (ARRAY, SIMD or HWINTRINSIC). +// comma - GT_COMMA to which the "check" belongs, "nullptr" if the check is a standalone one. +// stmt - Statement the indexing nodes belong to. +// +// Return Value: +// Rewritten "check" - no-op if it has no side effects or the tree that contains them. +// +// Assumptions: +// This method is capable of removing checks of two kinds: COMMA-based and standalone top-level ones. +// In case of a COMMA-based check, "check" must be a non-null first operand of a non-null COMMA. +// In case of a standalone check, "comma" must be null and "check" - "stmt"'s root. +// +GenTree* Compiler::optRemoveRangeCheck(GenTreeBoundsChk* check, GenTree* comma, Statement* stmt) { #if !REARRANGE_ADDS noway_assert(!"can't remove range checks without REARRANGE_ADDS right now"); #endif - noway_assert(tree->gtOper == GT_COMMA); + noway_assert(stmt != nullptr); + noway_assert((comma != nullptr && comma->OperIs(GT_COMMA) && comma->gtGetOp1() == check) || + (check != nullptr && check->OperIsBoundsCheck() && comma == nullptr)); + noway_assert(check->OperIsBoundsCheck()); - GenTree* bndsChkTree = tree->AsOp()->gtOp1; - - noway_assert(bndsChkTree->OperIsBoundsCheck()); + GenTree* tree = comma != nullptr ? comma : check; #ifdef DEBUG if (verbose) @@ -8239,19 +8369,40 @@ void Compiler::optRemoveRangeCheck(GenTree* tree, Statement* stmt) // Extract side effects GenTree* sideEffList = nullptr; - gtExtractSideEffList(bndsChkTree, &sideEffList, GTF_ASG); + gtExtractSideEffList(check, &sideEffList, GTF_ASG); - // Just replace the bndsChk with a NOP as an operand to the GT_COMMA, if there are no side effects. - tree->AsOp()->gtOp1 = (sideEffList != nullptr) ? sideEffList : gtNewNothingNode(); - // TODO-CQ: We should also remove the GT_COMMA, but in any case we can no longer CSE the GT_COMMA. - tree->gtFlags |= GTF_DONT_CSE; + if (sideEffList != nullptr) + { + // We've got some side effects. + if (tree->OperIs(GT_COMMA)) + { + // Make the comma handle them. + tree->AsOp()->gtOp1 = sideEffList; + } + else + { + // Make the statement execute them instead of the check. + stmt->SetRootNode(sideEffList); + tree = sideEffList; + } + } + else + { + check->gtBashToNOP(); + } + + if (tree->OperIs(GT_COMMA)) + { + // TODO-CQ: We should also remove the GT_COMMA, but in any case we can no longer CSE the GT_COMMA. + tree->gtFlags |= GTF_DONT_CSE; + } gtUpdateSideEffects(stmt, tree); - /* Recalculate the GetCostSz(), etc... */ + // Recalculate the GetCostSz(), etc... gtSetStmtInfo(stmt); - /* Re-thread the nodes if necessary */ + // Re-thread the nodes if necessary if (fgStmtListThreaded) { fgSetStmtSeq(stmt); @@ -8264,6 +8415,44 @@ void Compiler::optRemoveRangeCheck(GenTree* tree, Statement* stmt) gtDispTree(tree); } #endif + + return check; +} + +//------------------------------------------------------------------------------ +// optRemoveStandaloneRangeCheck : A thin wrapper over optRemoveRangeCheck that removes standalone checks. +// +// Arguments: +// check - The standalone top-level CHECK node. +// stmt - The statement "check" is a root node of. +// +// Return Value: +// If "check" has no side effects, it is retuned, bashed to a no-op. +// If it has side effects, the tree that executes them is returned. +// +GenTree* Compiler::optRemoveStandaloneRangeCheck(GenTreeBoundsChk* check, Statement* stmt) +{ + assert(check != nullptr); + assert(stmt != nullptr); + assert(check == stmt->GetRootNode()); + + return optRemoveRangeCheck(check, nullptr, stmt); +} + +//------------------------------------------------------------------------------ +// optRemoveCommaBasedRangeCheck : A thin wrapper over optRemoveRangeCheck that removes COMMA-based checks. +// +// Arguments: +// comma - GT_COMMA of which the first operand is the CHECK to be removed. +// stmt - The statement "comma" belongs to. +// +void Compiler::optRemoveCommaBasedRangeCheck(GenTree* comma, Statement* stmt) +{ + assert(comma != nullptr && comma->OperIs(GT_COMMA)); + assert(stmt != nullptr); + assert(comma->gtGetOp1()->OperIsBoundsCheck()); + + optRemoveRangeCheck(comma->gtGetOp1()->AsBoundsChk(), comma, stmt); } /***************************************************************************** @@ -8319,13 +8508,10 @@ void Compiler::optObtainLoopCloningOpts(LoopCloneContext* context) { for (unsigned i = 0; i < optLoopCount; i++) { - JITDUMP("Considering loop %d to clone for optimizations.\n", i); + JITDUMP("Considering loop " FMT_LP " to clone for optimizations.\n", i); if (optIsLoopClonable(i)) { - if (!(optLoopTable[i].lpFlags & LPFLG_REMOVED)) - { - optIdentifyLoopOptInfo(i, context); - } + optIdentifyLoopOptInfo(i, context); } JITDUMP("------------------------------------------------------------\n"); } @@ -8333,13 +8519,12 @@ void Compiler::optObtainLoopCloningOpts(LoopCloneContext* context) } //------------------------------------------------------------------------ -// optIdentifyLoopOptInfo: Identify loop optimization candidates an also -// check if the loop is suitable for the optimizations performed. +// optIdentifyLoopOptInfo: Identify loop optimization candidates. +// Also, check if the loop is suitable for the optimizations performed. // // Arguments: // loopNum - the current loop index for which conditions are derived. -// context - data structure where all loop cloning candidates will be -// updated. +// context - data structure where all loop cloning candidates will be updated. // // Return Value: // If the loop is not suitable for the optimizations, return false - context @@ -8357,71 +8542,11 @@ bool Compiler::optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* contex LoopDsc* pLoop = &optLoopTable[loopNum]; - if (!(pLoop->lpFlags & LPFLG_ITER)) - { - JITDUMP("> No iter flag on loop %d.\n", loopNum); - return false; - } - - unsigned ivLclNum = pLoop->lpIterVar(); - if (lvaVarAddrExposed(ivLclNum)) - { - JITDUMP("> Rejected V%02u as iter var because is address-exposed.\n", ivLclNum); - return false; - } - BasicBlock* head = pLoop->lpHead; + BasicBlock* beg = head->bbNext; // should this be pLoop->lpFirst or pLoop->lpTop instead? BasicBlock* end = pLoop->lpBottom; - BasicBlock* beg = head->bbNext; - - if (end->bbJumpKind != BBJ_COND) - { - JITDUMP("> Couldn't find termination test.\n"); - return false; - } - - if (end->bbJumpDest != beg) - { - JITDUMP("> Branch at loop 'end' not looping to 'begin'.\n"); - return false; - } - - // TODO-CQ: CLONE: Mark increasing or decreasing loops. - if ((pLoop->lpIterOper() != GT_ADD) || (pLoop->lpIterConst() != 1)) - { - JITDUMP("> Loop iteration operator not matching\n"); - return false; - } - - if ((pLoop->lpFlags & LPFLG_CONST_LIMIT) == 0 && (pLoop->lpFlags & LPFLG_VAR_LIMIT) == 0 && - (pLoop->lpFlags & LPFLG_ARRLEN_LIMIT) == 0) - { - JITDUMP("> Loop limit is neither constant, variable or array length\n"); - return false; - } - if (!(((pLoop->lpTestOper() == GT_LT || pLoop->lpTestOper() == GT_LE) && (pLoop->lpIterOper() == GT_ADD)) || - ((pLoop->lpTestOper() == GT_GT || pLoop->lpTestOper() == GT_GE) && (pLoop->lpIterOper() == GT_SUB)))) - { - JITDUMP("> Loop test (%s) doesn't agree with the direction (%s) of the pLoop->\n", - GenTree::OpName(pLoop->lpTestOper()), GenTree::OpName(pLoop->lpIterOper())); - return false; - } - - if (!(pLoop->lpTestTree->OperKind() & GTK_RELOP) || !(pLoop->lpTestTree->gtFlags & GTF_RELOP_ZTT)) - { - JITDUMP("> Loop inversion NOT present, loop test [%06u] may not protect entry from head.\n", - pLoop->lpTestTree->gtTreeID); - return false; - } - -#ifdef DEBUG - GenTree* op1 = pLoop->lpIterator(); - noway_assert((op1->gtOper == GT_LCL_VAR) && (op1->AsLclVarCommon()->GetLclNum() == ivLclNum)); -#endif - - JITDUMP("Checking blocks " FMT_BB ".." FMT_BB " for optimization candidates\n", beg->bbNum, - end->bbNext ? end->bbNext->bbNum : 0); + JITDUMP("Checking blocks " FMT_BB ".." FMT_BB " for optimization candidates\n", beg->bbNum, end->bbNum); LoopCloneVisitorInfo info(context, loopNum, nullptr); for (BasicBlock* block = beg; block != end->bbNext; block = block->bbNext) @@ -8446,7 +8571,7 @@ bool Compiler::optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* contex // Arguments: // tree the tree to be checked if it is the array [] operation. // result the extracted GT_INDEX information is updated in result. -// lhsNum for the root level (function is recursive) callers should be BAD_VAR_NUM. +// lhsNum for the root level (function is recursive) callers should pass BAD_VAR_NUM. // // Return Value: // Returns true if array index can be extracted, else, return false. See assumption about @@ -8613,8 +8738,8 @@ bool Compiler::optExtractArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsN // // Arguments: // tree the tree to be checked if it is an array [][][] operation. -// result the extracted GT_INDEX information. -// lhsNum for the root level (function is recursive) callers should be BAD_VAR_NUM. +// result OUT: the extracted GT_INDEX information. +// lhsNum for the root level (function is recursive) callers should pass BAD_VAR_NUM. // // Return Value: // Returns true if array index can be extracted, else, return false. "rank" field in @@ -8717,9 +8842,9 @@ bool Compiler::optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum) // candidates. Also supplies loopNum. // // Operation: -// If array index can be reconstructed, check if the iter var of the loop matches the -// array index var in some dim. Also ensure other index vars before the identified -// dim are loop invariant. +// If array index can be reconstructed, check if the iteration var of the loop matches the +// array index var in some dimension. Also ensure other index vars before the identified +// dimension are loop invariant. // // Return Value: // Skip sub trees if the optimization candidate is identified or else continue walking @@ -8732,16 +8857,19 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop if (optReconstructArrIndex(tree, &arrIndex, BAD_VAR_NUM)) { assert(tree->gtOper == GT_COMMA); + #ifdef DEBUG if (verbose) { - JITDUMP("Found ArrIndex at tree "); + printf("Found ArrIndex at tree "); printTreeID(tree); printf(" which is equivalent to: "); arrIndex.Print(); - JITDUMP("\n"); + printf("\n"); } #endif + + // Check that the array object local variable is invariant within the loop body. if (!optIsStackLocalInvariant(info->loopNum, arrIndex.arrLcl)) { return WALK_SKIP_SUBTREES; @@ -8750,7 +8878,7 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop // Walk the dimensions and see if iterVar of the loop is used as index. for (unsigned dim = 0; dim < arrIndex.rank; ++dim) { - // Is index variable also used as the loop iter var. + // Is index variable also used as the loop iter var? if (arrIndex.indLcls[dim] == optLoopTable[info->loopNum].lpIterVar()) { // Check the previous indices are all loop invariant. @@ -8765,9 +8893,9 @@ Compiler::fgWalkResult Compiler::optCanOptimizeByLoopCloning(GenTree* tree, Loop #ifdef DEBUG if (verbose) { - JITDUMP("Loop %d can be cloned for ArrIndex ", info->loopNum); + printf("Loop " FMT_LP " can be cloned for ArrIndex ", info->loopNum); arrIndex.Print(); - JITDUMP(" on dim %d\n", dim); + printf(" on dim %d\n", dim); } #endif // Update the loop context. @@ -9529,27 +9657,16 @@ void Compiler::optRemoveRedundantZeroInits() { GenTreeOp* treeOp = tree->AsOp(); - unsigned lclNum = BAD_VAR_NUM; + GenTreeLclVarCommon* lclVar; + bool isEntire; - if (treeOp->gtOp1->OperIs(GT_LCL_VAR, GT_LCL_FLD)) - { - lclNum = treeOp->gtOp1->AsLclVarCommon()->GetLclNum(); - } - else if (treeOp->gtOp1->OperIs(GT_OBJ, GT_BLK)) - { - GenTreeLclVarCommon* lcl = treeOp->gtOp1->gtGetOp1()->IsLocalAddrExpr(); - - if (lcl != nullptr) - { - lclNum = lcl->GetLclNum(); - } - } - - if (lclNum == BAD_VAR_NUM) + if (!tree->DefinesLocal(this, &lclVar, &isEntire)) { break; } + const unsigned lclNum = lclVar->GetLclNum(); + LclVarDsc* const lclDsc = lvaGetDesc(lclNum); unsigned* pRefCount = refCounts.LookupPointer(lclNum); @@ -9586,7 +9703,7 @@ void Compiler::optRemoveRedundantZeroInits() // The local hasn't been referenced before this assignment. bool removedExplicitZeroInit = false; - if (treeOp->gtOp2->IsIntegralConst(0)) + if (treeOp->gtGetOp2()->IsIntegralConst(0)) { bool bbInALoop = (block->bbFlags & BBF_BACKWARD_JUMP) != 0; bool bbIsReturn = block->bbJumpKind == BBJ_RETURN; @@ -9617,7 +9734,7 @@ void Compiler::optRemoveRedundantZeroInits() } } - if (treeOp->gtOp1->OperIs(GT_LCL_VAR)) + if (isEntire) { BitVecOps::AddElemD(&bitVecTraits, zeroInitLocals, lclNum); } @@ -9625,8 +9742,7 @@ void Compiler::optRemoveRedundantZeroInits() } } - if (!removedExplicitZeroInit && treeOp->gtOp1->OperIs(GT_LCL_VAR) && - (!canThrow || !lclDsc->lvLiveInOutOfHndlr)) + if (!removedExplicitZeroInit && isEntire && (!canThrow || !lclDsc->lvLiveInOutOfHndlr)) { // If compMethodRequiresPInvokeFrame() returns true, lower may later // insert a call to CORINFO_HELP_INIT_PINVOKE_FRAME which is a gc-safe point. @@ -9637,6 +9753,7 @@ void Compiler::optRemoveRedundantZeroInits() // the prolog and this explicit intialization. Therefore, it doesn't // require zero initialization in the prolog. lclDsc->lvHasExplicitInit = 1; + JITDUMP("Marking L%02u as having an explicit init\n", lclNum); } } break; diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 7260f4934c111..b0a296842023d 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -187,18 +187,21 @@ bool RangeCheck::BetweenBounds(Range& range, GenTree* upper, int arrSize) void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree* treeParent) { // Check if we are dealing with a bounds check node. - if (treeParent->OperGet() != GT_COMMA) + bool isComma = treeParent->OperIs(GT_COMMA); + bool isTopLevelNode = treeParent == stmt->GetRootNode(); + if (!(isComma || isTopLevelNode)) { return; } // If we are not looking at array bounds check, bail. - GenTree* tree = treeParent->AsOp()->gtOp1; + GenTree* tree = isComma ? treeParent->AsOp()->gtOp1 : treeParent; if (!tree->OperIsBoundsCheck()) { return; } + GenTree* comma = treeParent->OperIs(GT_COMMA) ? treeParent : nullptr; GenTreeBoundsChk* bndsChk = tree->AsBoundsChk(); m_pCurBndsChk = bndsChk; GenTree* treeIndex = bndsChk->gtIndex; @@ -258,7 +261,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree* if ((idxVal < arrSize) && (idxVal >= 0)) { JITDUMP("Removing range check\n"); - m_pCompiler->optRemoveRangeCheck(treeParent, stmt); + m_pCompiler->optRemoveRangeCheck(bndsChk, comma, stmt); return; } } @@ -299,7 +302,7 @@ void RangeCheck::OptimizeRangeCheck(BasicBlock* block, Statement* stmt, GenTree* if (BetweenBounds(range, bndsChk->gtArrLen, arrSize)) { JITDUMP("[RangeCheck::OptimizeRangeCheck] Between bounds\n"); - m_pCompiler->optRemoveRangeCheck(treeParent, stmt); + m_pCompiler->optRemoveRangeCheck(bndsChk, comma, stmt); } return; } diff --git a/src/coreclr/jit/simdashwintrinsic.cpp b/src/coreclr/jit/simdashwintrinsic.cpp index d50def6863f45..f117c48f4253f 100644 --- a/src/coreclr/jit/simdashwintrinsic.cpp +++ b/src/coreclr/jit/simdashwintrinsic.cpp @@ -369,9 +369,8 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, GenTree* op2 = nullptr; GenTree* op3 = nullptr; - SimdAsHWIntrinsicClassId classId = SimdAsHWIntrinsicInfo::lookupClassId(intrinsic); - unsigned numArgs = sig->numArgs; - bool isInstanceMethod = false; + unsigned numArgs = sig->numArgs; + bool isInstanceMethod = false; if (sig->hasThis()) { @@ -880,56 +879,154 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, case NI_VectorT128_op_Multiply: { - assert(baseType == TYP_INT); - NamedIntrinsic hwIntrinsic = NI_Illegal; + GenTree** broadcastOp = nullptr; - if (compOpportunisticallyDependsOn(InstructionSet_SSE41)) + if (varTypeIsArithmetic(op1->TypeGet())) { - hwIntrinsic = NI_SSE41_MultiplyLow; + broadcastOp = &op1; } - else + else if (varTypeIsArithmetic(op2->TypeGet())) + { + broadcastOp = &op2; + } + + if (broadcastOp != nullptr) + { + *broadcastOp = gtNewSimdCreateBroadcastNode(simdType, *broadcastOp, baseType, simdSize, + /* isSimdAsHWIntrinsic */ true); + } + + switch (baseType) { - // op1Dup = op1 - GenTree* op1Dup; - op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op1 for Vector.Multiply")); + case TYP_SHORT: + case TYP_USHORT: + { + hwIntrinsic = NI_SSE2_MultiplyLow; + break; + } - // op2Dup = op2 - GenTree* op2Dup; - op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, - nullptr DEBUGARG("Clone op2 for Vector.Multiply")); + case TYP_INT: + case TYP_UINT: + { + if (compOpportunisticallyDependsOn(InstructionSet_SSE41)) + { + hwIntrinsic = NI_SSE41_MultiplyLow; + } + else + { + // op1Dup = op1 + GenTree* op1Dup; + op1 = impCloneExpr(op1, &op1Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, + nullptr DEBUGARG("Clone op1 for Vector.Multiply")); + + // op2Dup = op2 + GenTree* op2Dup; + op2 = impCloneExpr(op2, &op2Dup, clsHnd, (unsigned)CHECK_SPILL_ALL, + nullptr DEBUGARG("Clone op2 for Vector.Multiply")); + + // op1 = Sse2.ShiftRightLogical128BitLane(op1, 4) + op1 = + gtNewSimdAsHWIntrinsicNode(retType, op1, gtNewIconNode(4, TYP_INT), + NI_SSE2_ShiftRightLogical128BitLane, baseType, simdSize); + + // op2 = Sse2.ShiftRightLogical128BitLane(op1, 4) + op2 = + gtNewSimdAsHWIntrinsicNode(retType, op2, gtNewIconNode(4, TYP_INT), + NI_SSE2_ShiftRightLogical128BitLane, baseType, simdSize); + + // op2 = Sse2.Multiply(op2.AsUInt64(), op1.AsUInt64()).AsInt32() + op2 = gtNewSimdAsHWIntrinsicNode(retType, op2, op1, NI_SSE2_Multiply, TYP_ULONG, + simdSize); + + // op2 = Sse2.Shuffle(op2, (0, 0, 2, 0)) + op2 = gtNewSimdAsHWIntrinsicNode(retType, op2, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), + NI_SSE2_Shuffle, baseType, simdSize); + + // op1 = Sse2.Multiply(op1Dup.AsUInt64(), op2Dup.AsUInt64()).AsInt32() + op1 = gtNewSimdAsHWIntrinsicNode(retType, op1Dup, op2Dup, NI_SSE2_Multiply, TYP_ULONG, + simdSize); + + // op1 = Sse2.Shuffle(op1, (0, 0, 2, 0)) + op1 = gtNewSimdAsHWIntrinsicNode(retType, op1, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), + NI_SSE2_Shuffle, baseType, simdSize); + + // result = Sse2.UnpackLow(op1, op2) + hwIntrinsic = NI_SSE2_UnpackLow; + } + break; + } - // op1 = Sse2.ShiftRightLogical128BitLane(op1, 4) - op1 = gtNewSimdAsHWIntrinsicNode(retType, op1, gtNewIconNode(4, TYP_INT), - NI_SSE2_ShiftRightLogical128BitLane, baseType, simdSize); + case TYP_FLOAT: + { + hwIntrinsic = NI_SSE_Multiply; + break; + } - // op2 = Sse2.ShiftRightLogical128BitLane(op1, 4) - op2 = gtNewSimdAsHWIntrinsicNode(retType, op2, gtNewIconNode(4, TYP_INT), - NI_SSE2_ShiftRightLogical128BitLane, baseType, simdSize); + case TYP_DOUBLE: + { + hwIntrinsic = NI_SSE2_Multiply; + break; + } - // op2 = Sse2.Multiply(op2.AsUInt64(), op1.AsUInt64()).AsInt32() - op2 = gtNewSimdAsHWIntrinsicNode(retType, op2, op1, NI_SSE2_Multiply, TYP_ULONG, simdSize); + default: + { + unreached(); + } + } - // op2 = Sse2.Shuffle(op2, (0, 0, 2, 0)) - op2 = gtNewSimdAsHWIntrinsicNode(retType, op2, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), - NI_SSE2_Shuffle, baseType, simdSize); + assert(hwIntrinsic != NI_Illegal); + return gtNewSimdAsHWIntrinsicNode(retType, op1, op2, hwIntrinsic, baseType, simdSize); + } - // op1 = Sse2.Multiply(op1Dup.AsUInt64(), op2Dup.AsUInt64()).AsInt32() - op1 = - gtNewSimdAsHWIntrinsicNode(retType, op1Dup, op2Dup, NI_SSE2_Multiply, TYP_ULONG, simdSize); + case NI_VectorT256_op_Multiply: + { + NamedIntrinsic hwIntrinsic = NI_Illegal; + GenTree** broadcastOp = nullptr; - // op1 = Sse2.Shuffle(op1, (0, 0, 2, 0)) - op1 = gtNewSimdAsHWIntrinsicNode(retType, op1, gtNewIconNode(SHUFFLE_XXZX, TYP_INT), - NI_SSE2_Shuffle, baseType, simdSize); + if (varTypeIsArithmetic(op1->TypeGet())) + { + broadcastOp = &op1; + } + else if (varTypeIsArithmetic(op2->TypeGet())) + { + broadcastOp = &op2; + } - // result = Sse2.UnpackLow(op1, op2) - hwIntrinsic = NI_SSE2_UnpackLow; + if (broadcastOp != nullptr) + { + *broadcastOp = gtNewSimdCreateBroadcastNode(simdType, *broadcastOp, baseType, simdSize, + /* isSimdAsHWIntrinsic */ true); } - assert(hwIntrinsic != NI_Illegal); + switch (baseType) + { + case TYP_SHORT: + case TYP_USHORT: + case TYP_INT: + case TYP_UINT: + { + hwIntrinsic = NI_AVX2_MultiplyLow; + break; + } + + case TYP_FLOAT: + case TYP_DOUBLE: + { + hwIntrinsic = NI_AVX_Multiply; + break; + } + + default: + { + unreached(); + } + } + + assert(hwIntrinsic != NI_Illegal); return gtNewSimdAsHWIntrinsicNode(retType, op1, op2, hwIntrinsic, baseType, simdSize); } + #elif defined(TARGET_ARM64) case NI_Vector2_CreateBroadcast: case NI_Vector3_CreateBroadcast: @@ -970,6 +1067,83 @@ GenTree* Compiler::impSimdAsHWIntrinsicSpecial(NamedIntrinsic intrinsic, // result = ConditionalSelect(op1, op1Dup, op2Dup) return impSimdAsHWIntrinsicCndSel(clsHnd, retType, baseType, simdSize, op1, op1Dup, op2Dup); } + + case NI_VectorT128_op_Multiply: + { + NamedIntrinsic hwIntrinsic = NI_Illegal; + NamedIntrinsic scalarIntrinsic = NI_Illegal; + GenTree** scalarOp = nullptr; + + if (varTypeIsArithmetic(op1->TypeGet())) + { + // MultiplyByScalar requires the scalar op to be op2 + std::swap(op1, op2); + + scalarOp = &op2; + } + else if (varTypeIsArithmetic(op2->TypeGet())) + { + scalarOp = &op2; + } + + switch (baseType) + { + case TYP_BYTE: + case TYP_UBYTE: + { + if (scalarOp != nullptr) + { + *scalarOp = gtNewSimdCreateBroadcastNode(simdType, *scalarOp, baseType, simdSize, + /* isSimdAsHWIntrinsic */ true); + } + + hwIntrinsic = NI_AdvSimd_Multiply; + break; + } + + case TYP_SHORT: + case TYP_USHORT: + case TYP_INT: + case TYP_UINT: + case TYP_FLOAT: + { + if (scalarOp != nullptr) + { + hwIntrinsic = NI_AdvSimd_MultiplyByScalar; + *scalarOp = gtNewSimdAsHWIntrinsicNode(TYP_SIMD8, *scalarOp, + NI_Vector64_CreateScalarUnsafe, baseType, 8); + } + else + { + hwIntrinsic = NI_AdvSimd_Multiply; + } + break; + } + + case TYP_DOUBLE: + { + if (scalarOp != nullptr) + { + hwIntrinsic = NI_AdvSimd_Arm64_MultiplyByScalar; + *scalarOp = + gtNewSimdAsHWIntrinsicNode(TYP_SIMD8, *scalarOp, NI_Vector64_Create, baseType, 8); + } + else + { + hwIntrinsic = NI_AdvSimd_Arm64_Multiply; + } + break; + } + + default: + { + unreached(); + } + } + + assert(hwIntrinsic != NI_Illegal); + return gtNewSimdAsHWIntrinsicNode(retType, op1, op2, hwIntrinsic, baseType, simdSize); + } #else #error Unsupported platform #endif // !TARGET_XARCH && !TARGET_ARM64 diff --git a/src/coreclr/jit/simdashwintrinsiclistarm64.h b/src/coreclr/jit/simdashwintrinsiclistarm64.h index 494377bc7172f..fc75eca9f3fc3 100644 --- a/src/coreclr/jit/simdashwintrinsiclistarm64.h +++ b/src/coreclr/jit/simdashwintrinsiclistarm64.h @@ -128,7 +128,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Equality, SIMD_AS_HWINTRINSIC_ID(VectorT128, op_ExclusiveOr, 2, {NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor, NI_AdvSimd_Xor}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Explicit, 1, {NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Inequality, 2, {NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Multiply, 2, {NI_AdvSimd_Multiply, NI_AdvSimd_Multiply, NI_AdvSimd_Multiply, NI_AdvSimd_Multiply, NI_AdvSimd_Multiply, NI_AdvSimd_Multiply, NI_Illegal, NI_Illegal, NI_AdvSimd_Multiply, NI_AdvSimd_Arm64_Multiply}, SimdAsHWIntrinsicFlag::None) +SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Multiply, 2, {NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_Illegal, NI_Illegal, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Subtraction, 2, {NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Subtract, NI_AdvSimd_Arm64_Subtract}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, SquareRoot, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Arm64_Sqrt, NI_AdvSimd_Arm64_Sqrt}, SimdAsHWIntrinsicFlag::None) diff --git a/src/coreclr/jit/simdashwintrinsiclistxarch.h b/src/coreclr/jit/simdashwintrinsiclistxarch.h index 65584c3abb09e..99e5c29ff8a9c 100644 --- a/src/coreclr/jit/simdashwintrinsiclistxarch.h +++ b/src/coreclr/jit/simdashwintrinsiclistxarch.h @@ -128,7 +128,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Equality, SIMD_AS_HWINTRINSIC_ID(VectorT128, op_ExclusiveOr, 2, {NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE2_Xor, NI_SSE_Xor, NI_SSE2_Xor}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Explicit, 1, {NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit, NI_VectorT128_op_Explicit}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Inequality, 2, {NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality, NI_Vector128_op_Inequality}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_SSE2_MultiplyLow, NI_Illegal, NI_VectorT128_op_Multiply, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE_Multiply, NI_SSE2_Multiply}, SimdAsHWIntrinsicFlag::None) +SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply, NI_Illegal, NI_Illegal, NI_VectorT128_op_Multiply, NI_VectorT128_op_Multiply}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, op_Subtraction, 2, {NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE2_Subtract, NI_SSE_Subtract, NI_SSE2_Subtract}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT128, SquareRoot, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE_Sqrt, NI_SSE2_Sqrt}, SimdAsHWIntrinsicFlag::None) @@ -165,7 +165,7 @@ SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Equality, SIMD_AS_HWINTRINSIC_ID(VectorT256, op_ExclusiveOr, 2, {NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX2_Xor, NI_AVX_Xor, NI_AVX_Xor}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Explicit, 1, {NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit, NI_VectorT256_op_Explicit}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Inequality, 2, {NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality, NI_Vector256_op_Inequality}, SimdAsHWIntrinsicFlag::None) -SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_AVX2_MultiplyLow, NI_Illegal, NI_AVX2_MultiplyLow, NI_Illegal, NI_Illegal, NI_Illegal, NI_AVX_Multiply, NI_AVX_Multiply}, SimdAsHWIntrinsicFlag::None) +SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Multiply, 2, {NI_Illegal, NI_Illegal, NI_VectorT256_op_Multiply, NI_VectorT256_op_Multiply, NI_VectorT256_op_Multiply, NI_VectorT256_op_Multiply, NI_Illegal, NI_Illegal, NI_VectorT256_op_Multiply, NI_VectorT256_op_Multiply}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, op_Subtraction, 2, {NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX2_Subtract, NI_AVX_Subtract, NI_AVX_Subtract}, SimdAsHWIntrinsicFlag::None) SIMD_AS_HWINTRINSIC_ID(VectorT256, SquareRoot, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AVX_Sqrt, NI_AVX_Sqrt}, SimdAsHWIntrinsicFlag::None) diff --git a/src/coreclr/jit/simdcodegenxarch.cpp b/src/coreclr/jit/simdcodegenxarch.cpp index 6897dd01954f6..f85e8b6cd4f0b 100644 --- a/src/coreclr/jit/simdcodegenxarch.cpp +++ b/src/coreclr/jit/simdcodegenxarch.cpp @@ -1486,7 +1486,6 @@ void CodeGen::genSIMDIntrinsicBinOp(GenTreeSIMD* simdNode) regNumber targetReg = simdNode->GetRegNum(); assert(targetReg != REG_NA); var_types targetType = simdNode->TypeGet(); - SIMDLevel level = compiler->getSIMDSupportLevel(); genConsumeOperands(simdNode); regNumber op1Reg = op1->GetRegNum(); diff --git a/src/coreclr/jit/typelist.h b/src/coreclr/jit/typelist.h index 5f129106fcafd..37a1db9f7ba4a 100644 --- a/src/coreclr/jit/typelist.h +++ b/src/coreclr/jit/typelist.h @@ -3,7 +3,8 @@ #define GCS EA_GCREF #define BRS EA_BYREF -#define PS EA_PTRSIZE +#define EPS EA_PTRSIZE +#define PS TARGET_POINTER_SIZE #define PST (TARGET_POINTER_SIZE / sizeof(int)) #ifdef TARGET_64BIT @@ -43,8 +44,8 @@ DEF_TP(USHORT ,"ushort" , TYP_INT, TI_SHORT, 2, 2, 4, 1, 2, VTF_INT|VT DEF_TP(INT ,"int" , TYP_INT, TI_INT, 4, 4, 4, 1, 4, VTF_INT|VTF_I32, TYPE_REF_INT) DEF_TP(UINT ,"uint" , TYP_INT, TI_INT, 4, 4, 4, 1, 4, VTF_INT|VTF_UNS|VTF_I32,TYPE_REF_INT) // Only used in GT_CAST nodes -DEF_TP(LONG ,"long" , TYP_LONG, TI_LONG, 8, PS, PS, 2, 8, VTF_INT|VTF_I64, TYPE_REF_LNG) -DEF_TP(ULONG ,"ulong" , TYP_LONG, TI_LONG, 8, PS, PS, 2, 8, VTF_INT|VTF_UNS|VTF_I64,TYPE_REF_LNG) // Only used in GT_CAST nodes +DEF_TP(LONG ,"long" , TYP_LONG, TI_LONG, 8,EPS,EPS, 2, 8, VTF_INT|VTF_I64, TYPE_REF_LNG) +DEF_TP(ULONG ,"ulong" , TYP_LONG, TI_LONG, 8,EPS,EPS, 2, 8, VTF_INT|VTF_UNS|VTF_I64,TYPE_REF_LNG) // Only used in GT_CAST nodes DEF_TP(FLOAT ,"float" , TYP_FLOAT, TI_FLOAT, 4, 4, 4, 1, 4, VTF_FLT, TYPE_REF_FLT) DEF_TP(DOUBLE ,"double" , TYP_DOUBLE, TI_DOUBLE,8, 8, 8, 2, 8, VTF_FLT, TYPE_REF_DBL) @@ -69,6 +70,7 @@ DEF_TP(UNKNOWN ,"unknown" ,TYP_UNKNOWN, TI_ERROR, 0, 0, 0, 0, 0, VTF_ANY, #undef GCS #undef BRS +#undef EPS #undef PS #undef PST #undef VTF_I32 diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index dafa816193cb7..26914f10112f9 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1707,6 +1707,11 @@ MethodSet::MethodSet(const WCHAR* filename, HostAllocator alloc) : m_pInfos(null } } + if (fclose(methodSetFile)) + { + JITDUMP("Unable to close %ws\n", filename); + } + if (m_pInfos == nullptr) { JITDUMP("No methods read from %ws\n", filename); @@ -1792,8 +1797,47 @@ bool MethodSet::IsActiveMethod(const char* methodName, int methodHash) return false; } +//------------------------------------------------------------------------ +// CachedCyclesPerSecond - Return the cached value of CycleTimer::CyclesPerSecond(). +// +// Calling CycleTimer::CyclesPerSecond() can be expensive: it runs a loop of non-empty +// code to compute its value. So call it once and cache the result. +// +double CachedCyclesPerSecond() +{ + static volatile LONG s_CachedCyclesPerSecondInitialized = 0; + static double s_CachedCyclesPerSecond = 0.0; + static CritSecObject s_CachedCyclesPerSecondLock; + + if (s_CachedCyclesPerSecondInitialized == 1) + { + return s_CachedCyclesPerSecond; + } + + // It wasn't initialized yet, so initialize it. There might be a race, + // so lock the update. + + CritSecHolder cachedCyclesPerSecondLock(s_CachedCyclesPerSecondLock); + + if (s_CachedCyclesPerSecondInitialized == 1) + { + // Someone else initialized it first. + return s_CachedCyclesPerSecond; + } + + s_CachedCyclesPerSecond = CycleTimer::CyclesPerSecond(); + + LONG originalInitializedValue = InterlockedCompareExchange(&s_CachedCyclesPerSecondInitialized, 1, 0); + if (originalInitializedValue == 1) + { + // This is unexpected; the critical section should have protected us. + } + + return s_CachedCyclesPerSecond; +} + #ifdef FEATURE_JIT_METHOD_PERF -CycleCount::CycleCount() : cps(CycleTimer::CyclesPerSecond()) +CycleCount::CycleCount() : cps(CachedCyclesPerSecond()) { } diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index 3f08f3e25618d..e22320bb1ef63 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -766,4 +766,10 @@ int64_t GetSigned64Magic(int64_t d, int* shift /*out*/); #endif } +// +// Profiling helpers +// + +double CachedCyclesPerSecond(); + #endif // _UTILS_H_ diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index a128ae6f92959..2d7e127666ba2 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -737,17 +737,6 @@ T ValueNumStore::EvalOpSpecialized(VNFunc vnf, T v0, T v1) break; } } - else // must be a VNF_ function - { - switch (vnf) - { - // Here we handle those that are the same for all integer types. - - default: - // For any other value of 'vnf', we will assert below - break; - } - } noway_assert(!"Unhandled operation in EvalOpSpecialized - binary"); return v0; @@ -1911,7 +1900,9 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V assert(arg0VN != NoVN && arg1VN != NoVN); assert(arg0VN == VNNormalValue(arg0VN)); // Arguments carry no exceptions. assert(arg1VN == VNNormalValue(arg1VN)); // Arguments carry no exceptions. - assert(VNFuncArity(func) == 2); + + // Some SIMD functions with variable number of arguments are defined with zero arity + assert((VNFuncArity(func) == 0) || (VNFuncArity(func) == 2)); assert(func != VNF_MapSelect); // Precondition: use the special function VNForMapSelect defined for that. ValueNum resultVN; @@ -8149,12 +8140,31 @@ void Compiler::fgValueNumberTree(GenTree* tree) if (tree->gtFlags & GTF_IND_INVARIANT) { assert(!isVolatile); // We don't expect both volatile and invariant - tree->gtVNPair = - ValueNumPair(vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, ValueNumStore::VNForROH(), - addrNvnp.GetLiberal()), - vnStore->VNForMapSelect(VNK_Conservative, TYP_REF, ValueNumStore::VNForROH(), - addrNvnp.GetConservative())); - tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp); + + // Is this invariant indirect expected to always return a non-null value? + if ((tree->gtFlags & GTF_IND_NONNULL) != 0) + { + assert(tree->gtFlags & GTF_IND_NONFAULTING); + tree->gtVNPair = vnStore->VNPairForFunc(tree->TypeGet(), VNF_NonNullIndirect, addrNvnp); + if (addr->IsCnsIntOrI()) + { + assert(addrXvnp.BothEqual() && (addrXvnp.GetLiberal() == ValueNumStore::VNForEmptyExcSet())); + } + else + { + assert(false && "it's not expected to be hit at the moment, but can be allowed."); + // tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp); + } + } + else + { + tree->gtVNPair = + ValueNumPair(vnStore->VNForMapSelect(VNK_Liberal, TYP_REF, ValueNumStore::VNForROH(), + addrNvnp.GetLiberal()), + vnStore->VNForMapSelect(VNK_Conservative, TYP_REF, ValueNumStore::VNForROH(), + addrNvnp.GetConservative())); + tree->gtVNPair = vnStore->VNPWithExc(tree->gtVNPair, addrXvnp); + } } else if (isVolatile) { @@ -8910,26 +8920,24 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree) assert(hwIntrinsicNode != nullptr); // For safety/correctness we must mutate the global heap valuenumber - // for any HW intrinsic that performs a memory store operation + // for any HW intrinsic that performs a memory store operation if (hwIntrinsicNode->OperIsMemoryStore()) { fgMutateGcHeap(tree DEBUGARG("HWIntrinsic - MemoryStore")); } - // Check for any intrintics that have variable number of args or more than 2 args - // For now we will generate a unique value number for these cases. - // - if ((HWIntrinsicInfo::lookupNumArgs(hwIntrinsicNode->gtHWIntrinsicId) == -1) || - ((tree->AsOp()->gtOp1 != nullptr) && (tree->AsOp()->gtOp1->OperIs(GT_LIST)))) + if ((tree->AsOp()->gtOp1 != nullptr) && tree->gtGetOp1()->OperIs(GT_LIST)) { - // We have a HWINTRINSIC node in the GT_LIST form with 3 or more args - // Or the numArgs was specified as -1 in the numArgs column - - // Generate unique VN + // TODO-CQ: allow intrinsics with GT_LIST to be properly VN'ed, it will + // allow use to process things like Vector128.Create(1,2,3,4) etc. + // Generate unique VN for now. tree->gtVNPair.SetBoth(vnStore->VNForExpr(compCurBB, tree->TypeGet())); return; } + // We don't expect GT_LIST to be in the second op + assert((tree->AsOp()->gtOp2 == nullptr) || !tree->gtGetOp2()->OperIs(GT_LIST)); + VNFunc func = GetVNFuncForNode(tree); bool isMemoryLoad = hwIntrinsicNode->OperIsMemoryLoad(); @@ -8982,9 +8990,14 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree) #endif } + const bool isVariableNumArgs = HWIntrinsicInfo::lookupNumArgs(hwIntrinsicNode->gtHWIntrinsicId) == -1; + // There are some HWINTRINSICS operations that have zero args, i.e. NI_Vector128_Zero if (tree->AsOp()->gtOp1 == nullptr) { + // Currently we don't have intrinsics with variable number of args with a parameter-less option. + assert(!isVariableNumArgs); + if (encodeResultType) { // There are zero arg HWINTRINSICS operations that encode the result type, i.e. Vector128_AllBitSet @@ -9010,12 +9023,12 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree) if (encodeResultType) { normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, resvnp); - assert(vnStore->VNFuncArity(func) == 2); + assert((vnStore->VNFuncArity(func) == 2) || isVariableNumArgs); } else { normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp); - assert(vnStore->VNFuncArity(func) == 1); + assert((vnStore->VNFuncArity(func) == 1) || isVariableNumArgs); } } else @@ -9028,12 +9041,12 @@ void Compiler::fgValueNumberHWIntrinsic(GenTree* tree) if (encodeResultType) { normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, op2vnp, resvnp); - assert(vnStore->VNFuncArity(func) == 3); + assert((vnStore->VNFuncArity(func) == 3) || isVariableNumArgs); } else { normalPair = vnStore->VNPairForFunc(tree->TypeGet(), func, op1vnp, op2vnp); - assert(vnStore->VNFuncArity(func) == 2); + assert((vnStore->VNFuncArity(func) == 2) || isVariableNumArgs); } } } @@ -9609,7 +9622,7 @@ VNFunc Compiler::fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc) break; case CORINFO_HELP_STRCNS: - vnf = VNF_StrCns; + vnf = VNF_LazyStrCns; break; case CORINFO_HELP_CHKCASTCLASS: diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index 1d3906d92ddec..b8f5f74e59955 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -143,7 +143,8 @@ ValueNumFuncDef(JitReadyToRunNewArr, 3, false, true, false) ValueNumFuncDef(Box, 3, false, false, false) ValueNumFuncDef(BoxNullable, 3, false, false, false) -ValueNumFuncDef(StrCns, 2, false, true, false) +ValueNumFuncDef(LazyStrCns, 2, false, true, false) // lazy-initialized string literal (helper) +ValueNumFuncDef(NonNullIndirect, 1, false, true, false) // this indirect is expected to always return a non-null value ValueNumFuncDef(Unbox, 2, false, true, false) ValueNumFuncDef(LT_UN, 2, false, false, false) // unsigned or unordered comparisons diff --git a/src/coreclr/jit/vartype.h b/src/coreclr/jit/vartype.h index d7a42c9e9cdd5..d6cca01919723 100644 --- a/src/coreclr/jit/vartype.h +++ b/src/coreclr/jit/vartype.h @@ -335,20 +335,23 @@ inline bool varTypeUsesFloatArgReg(T vt) template inline bool varTypeIsValidHfaType(T vt) { -#ifdef FEATURE_HFA - bool isValid = (TypeGet(vt) != TYP_UNDEF); - if (isValid) + if (GlobalJitOptions::compFeatureHfa) { + bool isValid = (TypeGet(vt) != TYP_UNDEF); + if (isValid) + { #ifdef TARGET_ARM64 - assert(varTypeUsesFloatReg(vt)); + assert(varTypeUsesFloatReg(vt)); #else // !TARGET_ARM64 - assert(varTypeIsFloating(vt)); + assert(varTypeIsFloating(vt)); #endif // !TARGET_ARM64 + } + return isValid; + } + else + { + return false; } - return isValid; -#else // !FEATURE_HFA - return false; -#endif // !FEATURE_HFA } /*****************************************************************************/ diff --git a/src/coreclr/md/ceefilegen/cceegen.cpp b/src/coreclr/md/ceefilegen/cceegen.cpp index 707d45ab83f86..adddcddb20645 100644 --- a/src/coreclr/md/ceefilegen/cceegen.cpp +++ b/src/coreclr/md/ceefilegen/cceegen.cpp @@ -18,7 +18,7 @@ HRESULT STDMETHODCALLTYPE CreateICeeGen(REFIID riid, void **pCeeGen) { - if (riid != IID_ICeeGen) + if (riid != IID_ICeeGenInternal) return E_NOTIMPL; if (!pCeeGen) return E_POINTER; @@ -67,9 +67,7 @@ STDMETHODIMP CCeeGen::QueryInterface(REFIID riid, void** ppv) *ppv = NULL; if (riid == IID_IUnknown) - *ppv = (IUnknown*)(ICeeGen*)this; - else if (riid == IID_ICeeGen) - *ppv = (ICeeGen*)this; + *ppv = (IUnknown*)(ICeeGenInternal*)this; else if (riid == IID_ICeeGenInternal) *ppv = (ICeeGenInternal*)this; if (*ppv == NULL) @@ -226,16 +224,6 @@ STDMETHODIMP CCeeGen::GenerateCeeFile () return E_NOTIMPL; } -STDMETHODIMP CCeeGen::GenerateCeeMemoryImage (void **) -{ - BEGIN_ENTRYPOINT_NOTHROW; - - _ASSERTE(!"E_NOTIMPL"); - END_ENTRYPOINT_NOTHROW; - - return E_NOTIMPL; -} - STDMETHODIMP CCeeGen::GetIlSection ( HCEESECTION *section) { @@ -315,18 +303,6 @@ STDMETHODIMP CCeeGen::GetSectionBlock ( return NOERROR; } -STDMETHODIMP CCeeGen::TruncateSection ( - HCEESECTION section, - ULONG len) -{ - BEGIN_ENTRYPOINT_NOTHROW; - - _ASSERTE(!"E_NOTIMPL"); - END_ENTRYPOINT_NOTHROW; - return E_NOTIMPL; -} - - CCeeGen::CCeeGen() // protected ctor { @@ -386,7 +362,6 @@ HRESULT CCeeGen::Init() // not-virtual, protected m_metaIdx = m_textIdx; // meta section is actually in .text m_ilIdx = m_textIdx; // il section is actually in .text m_corHdrIdx = -1; - m_encMode = FALSE; LExit: if (FAILED(hr)) { @@ -396,24 +371,6 @@ HRESULT CCeeGen::Init() // not-virtual, protected return hr; } -// For EnC mode, generate strings into .rdata section rather than .text section -HRESULT CCeeGen::setEnCMode() -{ - PESection *section = NULL; - HRESULT hr = m_peSectionMan->getSectionCreate(".rdata", sdExecute, §ion); - TESTANDRETURNHR(hr); - CeeSection *ceeSection = new CeeSectionString(*this, *section); - if (ceeSection == NULL) - { - return E_OUTOFMEMORY; - } - hr = addSection(ceeSection, &m_stringIdx); - if (SUCCEEDED(hr)) - m_encMode = TRUE; - return hr; -} - - HRESULT CCeeGen::cloneInstance(CCeeGen *destination) { //public, virtual _ASSERTE(destination); @@ -493,7 +450,7 @@ HRESULT CCeeGen::getSectionCreate (const char *name, DWORD flags, CeeSection **s name = ".text"; else if (strcmp(name, ".meta") == 0) name = ".text"; - else if (strcmp(name, ".rdata") == 0 && !m_encMode) + else if (strcmp(name, ".rdata") == 0) name = ".text"; for (int i=0; iname(), name) == 0) { @@ -549,12 +506,6 @@ HRESULT CCeeGen::emitMetaData(IMetaDataEmit *emitter, CeeSection* section, DWORD IfFailGoto((HRESULT)(metaStream->Stat(&statStg, STATFLAG_NONAME)), Exit); buffLen = statStg.cbSize.u.LowPart; - if(m_objSwitch) - { - CeeSection* pSect; - DWORD flags = IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE | IMAGE_SCN_ALIGN_1BYTES; // 0x00100A00 - IfFailGoto(getSectionCreate(".cormeta",flags,&pSect,&m_metaIdx), Exit); - } buffer = (BYTE *)section->getBlock(buffLen, sizeof(DWORD)); IfNullGoto(buffer, Exit); offset = getMetaSection().dataLen() - buffLen; diff --git a/src/coreclr/md/compiler/assemblymd.cpp b/src/coreclr/md/compiler/assemblymd.cpp index d778c636722ab..759c7dc69d89a 100644 --- a/src/coreclr/md/compiler/assemblymd.cpp +++ b/src/coreclr/md/compiler/assemblymd.cpp @@ -444,7 +444,7 @@ STDMETHODIMP RegMeta::EnumExportedTypes( // S_OK or error BEGIN_ENTRYPOINT_NOTHROW; HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); - HENUMInternal *pEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumExportedTypes(%#08x, %#08x, %#08x, %#08x)\n", phEnum, rExportedTypes, cMax, pcTokens)); @@ -488,14 +488,14 @@ STDMETHODIMP RegMeta::EnumExportedTypes( // S_OK or error // set the output parameter. *ppmdEnum = pEnum; + pEnum = NULL; } - else - pEnum = *ppmdEnum; // we can only fill the minimum of what the caller asked for or what we have left. - IfFailGo(HENUMInternal::EnumWithCount(pEnum, cMax, rExportedTypes, pcTokens)); + IfFailGo(HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rExportedTypes, pcTokens)); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumExportedTypes); END_ENTRYPOINT_NOTHROW; diff --git a/src/coreclr/md/compiler/custattr_import.cpp b/src/coreclr/md/compiler/custattr_import.cpp index cc38921992e4e..ec05e2663df4a 100644 --- a/src/coreclr/md/compiler/custattr_import.cpp +++ b/src/coreclr/md/compiler/custattr_import.cpp @@ -108,7 +108,7 @@ STDMETHODIMP RegMeta::EnumCustomAttributes( HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); RID ridStart; RID ridEnd; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; CustomAttributeRec *pRec; ULONG index; @@ -117,7 +117,7 @@ STDMETHODIMP RegMeta::EnumCustomAttributes( START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -220,13 +220,15 @@ STDMETHODIMP RegMeta::EnumCustomAttributes( // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rCustomAttributes, pcCustomAttributes); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rCustomAttributes, pcCustomAttributes); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumCustomAttributes); END_ENTRYPOINT_NOTHROW; diff --git a/src/coreclr/md/compiler/import.cpp b/src/coreclr/md/compiler/import.cpp index c0abc8f7f6397..6559b2b31e9eb 100644 --- a/src/coreclr/md/compiler/import.cpp +++ b/src/coreclr/md/compiler/import.cpp @@ -44,7 +44,7 @@ STDMETHODIMP RegMeta::EnumMembers( // S_OK, S_FALSE, or error. RID index; RID indexField; TypeDefRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumMembers(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, cl, rMembers, cMax, pcTokens)); @@ -52,7 +52,7 @@ STDMETHODIMP RegMeta::EnumMembers( // S_OK, S_FALSE, or error. START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -95,13 +95,15 @@ STDMETHODIMP RegMeta::EnumMembers( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMembers, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMembers, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumMembers); @@ -132,7 +134,7 @@ STDMETHODIMP RegMeta::EnumMembersWithName( // S_OK, S_FALSE, or error. TypeDefRec *pRec; MethodRec *pMethod; FieldRec *pField; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LPUTF8 szNameUtf8; UTF8STR(szName, szNameUtf8); LPCUTF8 szNameUtf8Tmp; @@ -143,7 +145,7 @@ STDMETHODIMP RegMeta::EnumMembersWithName( // S_OK, S_FALSE, or error. START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -214,13 +216,15 @@ STDMETHODIMP RegMeta::EnumMembersWithName( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMembers, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMembers, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumMembersWithName); END_ENTRYPOINT_NOTHROW; @@ -246,7 +250,7 @@ STDMETHODIMP RegMeta::EnumMethods( RID ridStart; RID ridEnd; TypeDefRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumMethods(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, td, rMethods, cMax, pcTokens)); @@ -256,7 +260,7 @@ STDMETHODIMP RegMeta::EnumMethods( START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -308,13 +312,15 @@ STDMETHODIMP RegMeta::EnumMethods( // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMethods, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumMethods); END_ENTRYPOINT_NOTHROW; @@ -346,7 +352,7 @@ STDMETHODIMP RegMeta::EnumMethodsWithName( // S_OK, S_FALSE, or error. RID index; TypeDefRec *pRec; MethodRec *pMethod; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LPUTF8 szNameUtf8; UTF8STR(szName, szNameUtf8); LPCUTF8 szNameUtf8Tmp; @@ -360,7 +366,7 @@ STDMETHODIMP RegMeta::EnumMethodsWithName( // S_OK, S_FALSE, or error. LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -410,13 +416,15 @@ STDMETHODIMP RegMeta::EnumMethodsWithName( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMethods, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumMethodsWithName); END_ENTRYPOINT_NOTHROW; @@ -445,7 +453,7 @@ RegMeta::EnumFields( RID ridStart; RID ridEnd; TypeDefRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumFields(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, td, rFields, cMax, pcTokens)); @@ -453,7 +461,7 @@ RegMeta::EnumFields( START_MD_PERF(); LOCKREAD(); - if (pEnum == NULL) + if (*ppmdEnum == NULL) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -505,13 +513,15 @@ RegMeta::EnumFields( // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rFields, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rFields, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumFields); END_ENTRYPOINT_NOTHROW; @@ -542,7 +552,7 @@ STDMETHODIMP RegMeta::EnumFieldsWithName( // S_OK, S_FALSE, or error. ULONG index; TypeDefRec *pRec; FieldRec *pField; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LPUTF8 szNameUtf8; UTF8STR(szName, szNameUtf8); LPCUTF8 szNameUtf8Tmp; @@ -555,7 +565,7 @@ STDMETHODIMP RegMeta::EnumFieldsWithName( // S_OK, S_FALSE, or error. START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -604,13 +614,15 @@ STDMETHODIMP RegMeta::EnumFieldsWithName( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rFields, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rFields, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumFieldsWithName); END_ENTRYPOINT_NOTHROW; @@ -637,7 +649,7 @@ STDMETHODIMP RegMeta::EnumParams( // S_OK, S_FALSE, or error. RID ridStart; RID ridEnd; MethodRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumParams(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, mb, rParams, cMax, pcTokens)); @@ -645,7 +657,7 @@ STDMETHODIMP RegMeta::EnumParams( // S_OK, S_FALSE, or error. LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -674,13 +686,15 @@ STDMETHODIMP RegMeta::EnumParams( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rParams, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rParams, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumParams); END_ENTRYPOINT_NOTHROW; @@ -708,7 +722,7 @@ STDMETHODIMP RegMeta::EnumMemberRefs( // S_OK, S_FALSE, or error. ULONG ridEnd; ULONG index; MemberRefRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumMemberRefs(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, tkParent, rMemberRefs, cMax, pcTokens)); @@ -718,7 +732,7 @@ STDMETHODIMP RegMeta::EnumMemberRefs( // S_OK, S_FALSE, or error. START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -748,13 +762,15 @@ STDMETHODIMP RegMeta::EnumMemberRefs( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + *ppmdEnum = 0; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMemberRefs, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMemberRefs, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumMemberRefs); @@ -781,7 +797,7 @@ STDMETHODIMP RegMeta::EnumMethodImpls( // S_OK, S_FALSE, or error HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); MethodImplRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; HENUMInternal hEnum; @@ -795,7 +811,7 @@ STDMETHODIMP RegMeta::EnumMethodImpls( // S_OK, S_FALSE, or error HENUMInternal::ZeroEnum(&hEnum); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -824,13 +840,15 @@ STDMETHODIMP RegMeta::EnumMethodImpls( // S_OK, S_FALSE, or error // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethodBody, rMethodDecl, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMethodBody, rMethodDecl, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); HENUMInternal::ClearEnum(&hEnum); STOP_MD_PERF(EnumMethodImpls); @@ -861,7 +879,7 @@ STDMETHODIMP RegMeta::EnumPermissionSets( // S_OK, S_FALSE, or error. RID ridEnd; RID index; DeclSecurityRec *pRec; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; bool fCompareParent = false; mdToken typ = TypeFromToken(tk); mdToken tkParent; @@ -872,7 +890,7 @@ STDMETHODIMP RegMeta::EnumPermissionSets( // S_OK, S_FALSE, or error. START_MD_PERF(); LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // Does this token type even have security? if (tk != 0 && @@ -943,13 +961,15 @@ STDMETHODIMP RegMeta::EnumPermissionSets( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rPermission, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rPermission, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumPermissionSets); END_ENTRYPOINT_NOTHROW; @@ -1317,7 +1337,7 @@ STDMETHODIMP RegMeta::EnumProperties( // S_OK, S_FALSE, or error. RID ridStart = 0; RID ridEnd = 0; RID ridMax = 0; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumProperties(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, td, rProperties, cMax, pcProperties)); @@ -1336,7 +1356,7 @@ STDMETHODIMP RegMeta::EnumProperties( // S_OK, S_FALSE, or error. _ASSERTE(TypeFromToken(td) == mdtTypeDef); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -1391,13 +1411,15 @@ STDMETHODIMP RegMeta::EnumProperties( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rProperties, pcProperties); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rProperties, pcProperties); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumProperties); @@ -1426,7 +1448,7 @@ STDMETHODIMP RegMeta::EnumEvents( // S_OK, S_FALSE, or error. RID ridStart = 0; RID ridEnd = 0; RID ridMax = 0; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumEvents(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, td, rEvents, cMax, pcEvents)); @@ -1437,7 +1459,7 @@ STDMETHODIMP RegMeta::EnumEvents( // S_OK, S_FALSE, or error. _ASSERTE(TypeFromToken(td) == mdtTypeDef); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -1492,14 +1514,15 @@ STDMETHODIMP RegMeta::EnumEvents( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rEvents, pcEvents); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rEvents, pcEvents); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); - + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumEvents); END_ENTRYPOINT_NOTHROW; @@ -1638,7 +1661,7 @@ STDMETHODIMP RegMeta::EnumMethodSemantics( // S_OK, S_FALSE, or error. HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); ULONG ridEnd; ULONG index; - HENUMInternal *pEnum = *ppmdEnum; + HENUMInternal *pEnum = NULL; MethodSemanticsRec *pRec; LOG((LOGMD, "MD RegMeta::EnumMethodSemantics(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", @@ -1648,7 +1671,7 @@ STDMETHODIMP RegMeta::EnumMethodSemantics( // S_OK, S_FALSE, or error. LOCKREAD(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -1670,14 +1693,15 @@ STDMETHODIMP RegMeta::EnumMethodSemantics( // S_OK, S_FALSE, or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rEventProp, pcEventProp); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rEventProp, pcEventProp); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); - + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumMethodSemantics); END_ENTRYPOINT_NOTHROW; @@ -2298,7 +2322,7 @@ STDMETHODIMP RegMeta::EnumUnresolvedMethods( // S_OK or error. uint32_t index; // For counting methods on a TypeDef. uint32_t indexTypeDef; // For counting TypeDefs. bool bIsInterface; // Is a given TypeDef an interface? - HENUMInternal * pEnum = *ppmdEnum; // Enum we're working with. + HENUMInternal * pEnum = NULL; // Enum we're working with. CMiniMdRW * pMiniMd = &(m_pStgdb->m_MiniMd); LOG((LOGMD, "MD RegMeta::EnumUnresolvedMethods(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", @@ -2310,7 +2334,7 @@ STDMETHODIMP RegMeta::EnumUnresolvedMethods( // S_OK or error. // same time. Ref to Def map may be calculated incorrectly. LOCKWRITE(); - if ( pEnum == 0 ) + if ( *ppmdEnum == 0 ) { // instantiating a new ENUM MethodRec *pMethodRec; @@ -2403,13 +2427,15 @@ STDMETHODIMP RegMeta::EnumUnresolvedMethods( // S_OK or error. // set the output parameter *ppmdEnum = pEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rMethods, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rMethods, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumUnresolvedMethods); END_ENTRYPOINT_NOTHROW; @@ -2664,7 +2690,7 @@ STDMETHODIMP RegMeta::EnumUserStrings( // S_OK or error. BEGIN_ENTRYPOINT_NOTHROW; HENUMInternal **ppEnum = reinterpret_cast (phEnum); - HENUMInternal *pEnum = *ppEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "MD RegMeta::EnumUserStrings(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, rStrings, cmax, pcStrings)); @@ -2672,7 +2698,7 @@ STDMETHODIMP RegMeta::EnumUserStrings( // S_OK or error. START_MD_PERF(); LOCKREAD(); - if (pEnum == NULL) + if (*ppEnum == NULL) { // instantiating a new ENUM. CMiniMdRW *pMiniMd = &(m_pStgdb->m_MiniMd); @@ -2712,14 +2738,15 @@ STDMETHODIMP RegMeta::EnumUserStrings( // S_OK or error. // set the output parameter. *ppEnum = pEnum; + pEnum = NULL; } // fill the output token buffer. - hr = HENUMInternal::EnumWithCount(pEnum, cmax, rStrings, pcStrings); + hr = HENUMInternal::EnumWithCount(*ppEnum, cmax, rStrings, pcStrings); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppEnum); - + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumUserStrings); END_ENTRYPOINT_NOTHROW; diff --git a/src/coreclr/md/compiler/regmeta_import.cpp b/src/coreclr/md/compiler/regmeta_import.cpp index a305c1dd2866e..e93cc2aeeb663 100644 --- a/src/coreclr/md/compiler/regmeta_import.cpp +++ b/src/coreclr/md/compiler/regmeta_import.cpp @@ -175,7 +175,7 @@ STDMETHODIMP RegMeta::EnumTypeDefs( BEGIN_ENTRYPOINT_NOTHROW; HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); - HENUMInternal *pEnum; + HENUMInternal *pEnum = NULL; LOG((LOGMD, "RegMeta::EnumTypeDefs(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", phEnum, rTypeDefs, cMax, pcTypeDefs)); @@ -219,17 +219,15 @@ STDMETHODIMP RegMeta::EnumTypeDefs( // set the output parameter *ppmdEnum = pEnum; - } - else - { - pEnum = *ppmdEnum; + pEnum = NULL; } // we can only fill the minimun of what caller asked for or what we have left - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rTypeDefs, pcTypeDefs); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rTypeDefs, pcTypeDefs); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumTypeDefs); @@ -255,7 +253,7 @@ STDMETHODIMP RegMeta::EnumInterfaceImpls( HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); RID ridStart; RID ridEnd; - HENUMInternal *pEnum; + HENUMInternal *pEnum = NULL; InterfaceImplRec *pRec; RID index; @@ -298,17 +296,15 @@ STDMETHODIMP RegMeta::EnumInterfaceImpls( // set the output parameter *ppmdEnum = pEnum; - } - else - { - pEnum = *ppmdEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMax, rImpls, pcImpls); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMax, rImpls, pcImpls); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); STOP_MD_PERF(EnumInterfaceImpls); @@ -327,7 +323,7 @@ STDMETHODIMP RegMeta::EnumGenericParams(HCORENUM *phEnum, mdToken tkOwner, HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); RID ridStart; RID ridEnd; - HENUMInternal *pEnum; + HENUMInternal *pEnum = NULL; GenericParamRec *pRec; RID index; CMiniMdRW *pMiniMd = NULL; @@ -394,17 +390,16 @@ STDMETHODIMP RegMeta::EnumGenericParams(HCORENUM *phEnum, mdToken tkOwner, // set the output parameter *ppmdEnum = pEnum; - } - else - { - pEnum = *ppmdEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMaxTokens, rTokens, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); + STOP_MD_PERF(EnumGenericPars); END_ENTRYPOINT_NOTHROW; @@ -425,7 +420,7 @@ STDMETHODIMP RegMeta::EnumMethodSpecs( HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); RID ridStart; RID ridEnd; - HENUMInternal *pEnum; + HENUMInternal *pEnum = NULL; MethodSpecRec *pRec; RID index; CMiniMdRW *pMiniMd = NULL; @@ -499,17 +494,16 @@ STDMETHODIMP RegMeta::EnumMethodSpecs( } // set the output parameter *ppmdEnum = pEnum; - } - else - { - pEnum = *ppmdEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMaxTokens, rTokens, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); + STOP_MD_PERF(EnumMethodSpecs); END_ENTRYPOINT_NOTHROW; @@ -530,7 +524,7 @@ STDMETHODIMP RegMeta::EnumGenericParamConstraints( HENUMInternal **ppmdEnum = reinterpret_cast (phEnum); RID ridStart; RID ridEnd; - HENUMInternal *pEnum; + HENUMInternal *pEnum = NULL; GenericParamConstraintRec *pRec; RID index; CMiniMdRW *pMiniMd = NULL; @@ -588,17 +582,16 @@ STDMETHODIMP RegMeta::EnumGenericParamConstraints( // set the output parameter *ppmdEnum = pEnum; - } - else - { - pEnum = *ppmdEnum; + pEnum = NULL; } // fill the output token buffer - hr = HENUMInternal::EnumWithCount(pEnum, cMaxTokens, rTokens, pcTokens); + hr = HENUMInternal::EnumWithCount(*ppmdEnum, cMaxTokens, rTokens, pcTokens); ErrExit: HENUMInternal::DestroyEnumIfEmpty(ppmdEnum); + HENUMInternal::DestroyEnum(pEnum); + STOP_MD_PERF(EnumGenericParamConstraints); END_ENTRYPOINT_NOTHROW; diff --git a/src/coreclr/md/debug_metadata.h b/src/coreclr/md/debug_metadata.h index 7f98c4c182b56..6dfb79931490d 100644 --- a/src/coreclr/md/debug_metadata.h +++ b/src/coreclr/md/debug_metadata.h @@ -33,7 +33,7 @@ #pragma once -// Include for REGUTIL class used in Debug_ReportError +// Include for CLRConfig class used in Debug_ReportError #include // -------------------------------------------------------------------------------------- @@ -60,7 +60,7 @@ #define Debug_ReportError(strMessage) \ do { \ - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) \ + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat)) \ { _ASSERTE_MSG(FALSE, (strMessage)); } \ } while(0) #define Debug_ReportInternalError(strMessage) _ASSERTE_MSG(FALSE, (strMessage)) diff --git a/src/coreclr/md/enc/mdinternalrw.cpp b/src/coreclr/md/enc/mdinternalrw.cpp index 629300415ae2a..c051bb405fd9d 100644 --- a/src/coreclr/md/enc/mdinternalrw.cpp +++ b/src/coreclr/md/enc/mdinternalrw.cpp @@ -720,7 +720,7 @@ ULONG MDInternalRW::GetCountWithTokenKind( // return hresult break; default: #ifdef _DEBUG - if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) + if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) _ASSERTE(!"Invalid Blob Offset"); #endif ulCount = 0; @@ -2367,7 +2367,7 @@ MDInternalRW::GetSigFromToken( // not a known token type. #ifdef _DEBUG - if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) + if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat, 1)) _ASSERTE(!"Unexpected token type"); #endif *pcbSig = 0; diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h index a3a615fc6cd88..e3206a589ad99 100644 --- a/src/coreclr/md/inc/metamodel.h +++ b/src/coreclr/md/inc/metamodel.h @@ -1368,7 +1368,7 @@ template class CMiniMdTemplate : public CMiniMdBase break; case mdtString: default: - if(REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, 0)) + if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat)) _ASSERTE(!"Unexpected token type in FindCustomAttributeByName"); hr = COR_E_BADIMAGEFORMAT; goto ErrExit; diff --git a/src/coreclr/md/runtime/mdinternalro.cpp b/src/coreclr/md/runtime/mdinternalro.cpp index 327eb8648a7f7..7f8005a1fafc7 100644 --- a/src/coreclr/md/runtime/mdinternalro.cpp +++ b/src/coreclr/md/runtime/mdinternalro.cpp @@ -3323,9 +3323,8 @@ HRESULT _FillMDDefaultValue( #if BIGENDIAN { // We need to allocate and swap the string if we're on a big endian + // This allocation will be freed by the MDDefaultValue destructor. pMDDefaultValue->m_wzValue = new WCHAR[(cbValue + 1) / sizeof (WCHAR)]; - _ASSERTE(FALSE); // Nothing ever free's this newly allocated array. Inserting assert so that if we ever actually - // use this code path, we'll fix it then. (Don't want to fix something I can't test.) IfNullGo(pMDDefaultValue->m_wzValue); memcpy(const_cast(pMDDefaultValue->m_wzValue), pValue, cbValue); _ASSERTE(cbValue % sizeof(WCHAR) == 0); diff --git a/src/coreclr/nativeresources/CMakeLists.txt b/src/coreclr/nativeresources/CMakeLists.txt index ab3e53863e3fc..6959ca2497ad5 100644 --- a/src/coreclr/nativeresources/CMakeLists.txt +++ b/src/coreclr/nativeresources/CMakeLists.txt @@ -5,4 +5,4 @@ add_library_clr(nativeresourcestring resourcestring.cpp ) -_install (TARGETS nativeresourcestring DESTINATION lib) +install_clr (TARGETS nativeresourcestring DESTINATIONS lib) diff --git a/src/coreclr/pal/CMakeLists.txt b/src/coreclr/pal/CMakeLists.txt index dec1191a323db..4509e9fc0f8b5 100644 --- a/src/coreclr/pal/CMakeLists.txt +++ b/src/coreclr/pal/CMakeLists.txt @@ -10,8 +10,4 @@ add_compile_options(-fexceptions) add_definitions(-DUSE_STL) add_subdirectory(src) - -if(CLR_CMAKE_BUILD_TESTS) - add_subdirectory(tests) -endif(CLR_CMAKE_BUILD_TESTS) - +add_subdirectory(tests) diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 05e1deb003e5d..2f42a2c4fdaf0 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -324,11 +324,7 @@ PAL_IsDebuggerPresent(); #ifndef PAL_STDCPP_COMPAT -#if HOST_64BIT || _MSC_VER >= 1400 typedef __int64 time_t; -#else -typedef long time_t; -#endif #define _TIME_T_DEFINED #endif // !PAL_STDCPP_COMPAT diff --git a/src/coreclr/pal/prebuilt/inc/CMakeLists.txt b/src/coreclr/pal/prebuilt/inc/CMakeLists.txt index 79f8e541e02a8..7fc6cb8bc9a52 100644 --- a/src/coreclr/pal/prebuilt/inc/CMakeLists.txt +++ b/src/coreclr/pal/prebuilt/inc/CMakeLists.txt @@ -1,4 +1,4 @@ project(COREPAL) -_install (FILES corerror.h corprof.h DESTINATION inc) +install (FILES corerror.h corprof.h DESTINATION inc) diff --git a/src/coreclr/pal/prebuilt/inc/clrprivbinding.h b/src/coreclr/pal/prebuilt/inc/clrprivbinding.h index ea4cf7417074a..3c810676aa36c 100644 --- a/src/coreclr/pal/prebuilt/inc/clrprivbinding.h +++ b/src/coreclr/pal/prebuilt/inc/clrprivbinding.h @@ -55,7 +55,6 @@ typedef interface ICLRPrivAssembly ICLRPrivAssembly; /* header files for imported files */ #include "unknwn.h" #include "objidl.h" -#include "fusion.h" #ifdef __cplusplus extern "C"{ @@ -67,8 +66,6 @@ extern "C"{ -typedef LPCSTR LPCUTF8; - extern RPC_IF_HANDLE __MIDL_itf_clrprivbinding_0000_0000_v0_0_c_ifspec; @@ -90,7 +87,7 @@ EXTERN_C const IID IID_ICLRPrivBinder; { public: virtual HRESULT STDMETHODCALLTYPE BindAssemblyByName( - /* [in] */ IAssemblyName *pAssemblyName, + /* [in] */ struct AssemblyNameData *pAssemblyNameData, /* [retval][out] */ ICLRPrivAssembly **ppAssembly) = 0; virtual HRESULT STDMETHODCALLTYPE GetBinderID( @@ -122,7 +119,7 @@ EXTERN_C const IID IID_ICLRPrivBinder; HRESULT ( STDMETHODCALLTYPE *BindAssemblyByName )( ICLRPrivBinder * This, - /* [in] */ IAssemblyName *pAssemblyName, + /* [in] */ struct AssemblyNameData *pAssemblyNameData, /* [retval][out] */ ICLRPrivAssembly **ppAssembly); HRESULT ( STDMETHODCALLTYPE *GetBinderID )( @@ -156,8 +153,8 @@ EXTERN_C const IID IID_ICLRPrivBinder; ( (This)->lpVtbl -> Release(This) ) -#define ICLRPrivBinder_BindAssemblyByName(This,pAssemblyName,ppAssembly) \ - ( (This)->lpVtbl -> BindAssemblyByName(This,pAssemblyName,ppAssembly) ) +#define ICLRPrivBinder_BindAssemblyByName(This,pAssemblyNameData,ppAssembly) \ + ( (This)->lpVtbl -> BindAssemblyByName(This,pAssemblyNameData,ppAssembly) ) #define ICLRPrivBinder_GetBinderID(This,pBinderId) \ ( (This)->lpVtbl -> GetBinderID(This,pBinderId) ) @@ -233,7 +230,7 @@ EXTERN_C const IID IID_ICLRPrivAssembly; HRESULT ( STDMETHODCALLTYPE *BindAssemblyByName )( ICLRPrivAssembly * This, - /* [in] */ IAssemblyName *pAssemblyName, + /* [in] */ struct AssemblyNameData *pAssemblyNameData, /* [retval][out] */ ICLRPrivAssembly **ppAssembly); HRESULT ( STDMETHODCALLTYPE *GetBinderID )( @@ -271,8 +268,8 @@ EXTERN_C const IID IID_ICLRPrivAssembly; ( (This)->lpVtbl -> Release(This) ) -#define ICLRPrivAssembly_BindAssemblyByName(This,pAssemblyName,ppAssembly) \ - ( (This)->lpVtbl -> BindAssemblyByName(This,pAssemblyName,ppAssembly) ) +#define ICLRPrivAssembly_BindAssemblyByName(This,pAssemblyNameData,ppAssembly) \ + ( (This)->lpVtbl -> BindAssemblyByName(This,pAssemblyNameData,ppAssembly) ) #define ICLRPrivAssembly_GetBinderID(This,pBinderId) \ ( (This)->lpVtbl -> GetBinderID(This,pBinderId) ) diff --git a/src/coreclr/pal/prebuilt/inc/corerror.h b/src/coreclr/pal/prebuilt/inc/corerror.h index 0fe7c75d2d249..ac6b15163fa48 100644 --- a/src/coreclr/pal/prebuilt/inc/corerror.h +++ b/src/coreclr/pal/prebuilt/inc/corerror.h @@ -383,6 +383,7 @@ #define CORDBG_E_DATA_TARGET_ERROR EMAKEHR(0x1c61) #define CORDBG_E_NO_IMAGE_AVAILABLE EMAKEHR(0x1c64) #define CORDBG_E_UNSUPPORTED_DELEGATE EMAKEHR(0x1c68) +#define CORDBG_E_ASSEMBLY_UPDATES_APPLIED EMAKEHR(0x1c69) #define PEFMT_E_64BIT EMAKEHR(0x1d02) #define PEFMT_E_32BIT EMAKEHR(0x1d0b) #define CLDB_E_INTERNALERROR EMAKEHR(0x1fff) diff --git a/src/coreclr/pal/prebuilt/inc/fusion.h b/src/coreclr/pal/prebuilt/inc/fusion.h deleted file mode 100644 index 3129a9b1bc44e..0000000000000 --- a/src/coreclr/pal/prebuilt/inc/fusion.h +++ /dev/null @@ -1,247 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - - - -/* this ALWAYS GENERATED file contains the definitions for the interfaces */ - - - /* File created by MIDL compiler version 8.01.0622 */ -/* @@MIDL_FILE_HEADING( ) */ - -#pragma warning( disable: 4049 ) /* more than 64k source lines */ - - -/* verify that the version is high enough to compile this file*/ -#ifndef __REQUIRED_RPCNDR_H_VERSION__ -#define __REQUIRED_RPCNDR_H_VERSION__ 475 -#endif - -#include "rpc.h" -#include "rpcndr.h" - -#ifndef __RPCNDR_H_VERSION__ -#error this stub requires an updated version of -#endif /* __RPCNDR_H_VERSION__ */ - -#ifndef COM_NO_WINDOWS_H -#include "windows.h" -#include "ole2.h" -#endif /*COM_NO_WINDOWS_H*/ - -#ifndef __fusion_h__ -#define __fusion_h__ - -#if defined(_MSC_VER) && (_MSC_VER >= 1020) -#pragma once -#endif - -/* Forward Declarations */ - -#ifndef __IAssemblyName_FWD_DEFINED__ -#define __IAssemblyName_FWD_DEFINED__ -typedef interface IAssemblyName IAssemblyName; - -#endif /* __IAssemblyName_FWD_DEFINED__ */ - - -/* header files for imported files */ -#include "objidl.h" - -#ifdef __cplusplus -extern "C"{ -#endif - - -/* interface __MIDL_itf_fusion_0000_0000 */ -/* [local] */ - - -#ifdef _MSC_VER -#pragma comment(lib,"uuid.lib") -#endif - -//---------------------------------------------------------------------------= -// Fusion Interfaces. - -#ifdef _MSC_VER -#pragma once -#endif -typedef -enum _tagAssemblyContentType - { - AssemblyContentType_Default = 0, - AssemblyContentType_WindowsRuntime = 0x1, - AssemblyContentType_Invalid = 0xffffffff - } AssemblyContentType; - -// {CD193BC0-B4BC-11d2-9833-00C04FC31D2E} -EXTERN_GUID(IID_IAssemblyName, 0xCD193BC0, 0xB4BC, 0x11d2, 0x98, 0x33, 0x00, 0xC0, 0x4F, 0xC3, 0x1D, 0x2E); - - -extern RPC_IF_HANDLE __MIDL_itf_fusion_0000_0000_v0_0_c_ifspec; -extern RPC_IF_HANDLE __MIDL_itf_fusion_0000_0000_v0_0_s_ifspec; - -#ifndef __IAssemblyName_INTERFACE_DEFINED__ -#define __IAssemblyName_INTERFACE_DEFINED__ - -/* interface IAssemblyName */ -/* [unique][uuid][object][local] */ - -typedef /* [unique] */ IAssemblyName *LPASSEMBLYNAME; - -typedef /* [public] */ -enum __MIDL_IAssemblyName_0001 - { - ASM_NAME_PUBLIC_KEY = 0, - ASM_NAME_PUBLIC_KEY_TOKEN = ( ASM_NAME_PUBLIC_KEY + 1 ) , - ASM_NAME_HASH_VALUE = ( ASM_NAME_PUBLIC_KEY_TOKEN + 1 ) , - ASM_NAME_NAME = ( ASM_NAME_HASH_VALUE + 1 ) , - ASM_NAME_MAJOR_VERSION = ( ASM_NAME_NAME + 1 ) , - ASM_NAME_MINOR_VERSION = ( ASM_NAME_MAJOR_VERSION + 1 ) , - ASM_NAME_BUILD_NUMBER = ( ASM_NAME_MINOR_VERSION + 1 ) , - ASM_NAME_REVISION_NUMBER = ( ASM_NAME_BUILD_NUMBER + 1 ) , - ASM_NAME_CULTURE = ( ASM_NAME_REVISION_NUMBER + 1 ) , - ASM_NAME_PROCESSOR_ID_ARRAY = ( ASM_NAME_CULTURE + 1 ) , - ASM_NAME_OSINFO_ARRAY = ( ASM_NAME_PROCESSOR_ID_ARRAY + 1 ) , - ASM_NAME_HASH_ALGID = ( ASM_NAME_OSINFO_ARRAY + 1 ) , - ASM_NAME_ALIAS = ( ASM_NAME_HASH_ALGID + 1 ) , - ASM_NAME_CODEBASE_URL = ( ASM_NAME_ALIAS + 1 ) , - ASM_NAME_CODEBASE_LASTMOD = ( ASM_NAME_CODEBASE_URL + 1 ) , - ASM_NAME_NULL_PUBLIC_KEY = ( ASM_NAME_CODEBASE_LASTMOD + 1 ) , - ASM_NAME_NULL_PUBLIC_KEY_TOKEN = ( ASM_NAME_NULL_PUBLIC_KEY + 1 ) , - ASM_NAME_CUSTOM = ( ASM_NAME_NULL_PUBLIC_KEY_TOKEN + 1 ) , - ASM_NAME_NULL_CUSTOM = ( ASM_NAME_CUSTOM + 1 ) , - ASM_NAME_MVID = ( ASM_NAME_NULL_CUSTOM + 1 ) , - ASM_NAME_FILE_MAJOR_VERSION = ( ASM_NAME_MVID + 1 ) , - ASM_NAME_FILE_MINOR_VERSION = ( ASM_NAME_FILE_MAJOR_VERSION + 1 ) , - ASM_NAME_FILE_BUILD_NUMBER = ( ASM_NAME_FILE_MINOR_VERSION + 1 ) , - ASM_NAME_FILE_REVISION_NUMBER = ( ASM_NAME_FILE_BUILD_NUMBER + 1 ) , - ASM_NAME_RETARGET = ( ASM_NAME_FILE_REVISION_NUMBER + 1 ) , - ASM_NAME_SIGNATURE_BLOB = ( ASM_NAME_RETARGET + 1 ) , - ASM_NAME_CONFIG_MASK = ( ASM_NAME_SIGNATURE_BLOB + 1 ) , - ASM_NAME_ARCHITECTURE = ( ASM_NAME_CONFIG_MASK + 1 ) , - ASM_NAME_CONTENT_TYPE = ( ASM_NAME_ARCHITECTURE + 1 ) , - ASM_NAME_MAX_PARAMS = ( ASM_NAME_CONTENT_TYPE + 1 ) - } ASM_NAME; - -typedef /* [public] */ -enum __MIDL_IAssemblyName_0002 - { - ASM_DISPLAYF_VERSION = 0x1, - ASM_DISPLAYF_CULTURE = 0x2, - ASM_DISPLAYF_PUBLIC_KEY_TOKEN = 0x4, - ASM_DISPLAYF_PUBLIC_KEY = 0x8, - ASM_DISPLAYF_CUSTOM = 0x10, - ASM_DISPLAYF_PROCESSORARCHITECTURE = 0x20, - ASM_DISPLAYF_LANGUAGEID = 0x40, - ASM_DISPLAYF_RETARGET = 0x80, - ASM_DISPLAYF_CONFIG_MASK = 0x100, - ASM_DISPLAYF_MVID = 0x200, - ASM_DISPLAYF_CONTENT_TYPE = 0x400, - ASM_DISPLAYF_FULL = ( ( ( ( ( ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE ) | ASM_DISPLAYF_PUBLIC_KEY_TOKEN ) | ASM_DISPLAYF_RETARGET ) | ASM_DISPLAYF_PROCESSORARCHITECTURE ) | ASM_DISPLAYF_CONTENT_TYPE ) - } ASM_DISPLAY_FLAGS; - - -EXTERN_C const IID IID_IAssemblyName; - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("CD193BC0-B4BC-11d2-9833-00C04FC31D2E") - IAssemblyName : public IUnknown - { - public: - virtual HRESULT STDMETHODCALLTYPE SetProperty( - /* [in] */ DWORD PropertyId, - /* [in] */ const void *pvProperty, - /* [in] */ DWORD cbProperty) = 0; - - virtual HRESULT STDMETHODCALLTYPE GetProperty( - /* [in] */ DWORD PropertyId, - /* [out] */ LPVOID pvProperty, - /* [out][in] */ LPDWORD pcbProperty) = 0; - - }; - - -#else /* C style interface */ - - typedef struct IAssemblyNameVtbl - { - BEGIN_INTERFACE - - HRESULT ( STDMETHODCALLTYPE *QueryInterface )( - IAssemblyName * This, - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - _COM_Outptr_ void **ppvObject); - - ULONG ( STDMETHODCALLTYPE *AddRef )( - IAssemblyName * This); - - ULONG ( STDMETHODCALLTYPE *Release )( - IAssemblyName * This); - - HRESULT ( STDMETHODCALLTYPE *SetProperty )( - IAssemblyName * This, - /* [in] */ DWORD PropertyId, - /* [in] */ const void *pvProperty, - /* [in] */ DWORD cbProperty); - - HRESULT ( STDMETHODCALLTYPE *GetProperty )( - IAssemblyName * This, - /* [in] */ DWORD PropertyId, - /* [out] */ LPVOID pvProperty, - /* [out][in] */ LPDWORD pcbProperty); - - END_INTERFACE - } IAssemblyNameVtbl; - - interface IAssemblyName - { - CONST_VTBL struct IAssemblyNameVtbl *lpVtbl; - }; - - - -#ifdef COBJMACROS - - -#define IAssemblyName_QueryInterface(This,riid,ppvObject) \ - ( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) ) - -#define IAssemblyName_AddRef(This) \ - ( (This)->lpVtbl -> AddRef(This) ) - -#define IAssemblyName_Release(This) \ - ( (This)->lpVtbl -> Release(This) ) - - -#define IAssemblyName_SetProperty(This,PropertyId,pvProperty,cbProperty) \ - ( (This)->lpVtbl -> SetProperty(This,PropertyId,pvProperty,cbProperty) ) - -#define IAssemblyName_GetProperty(This,PropertyId,pvProperty,pcbProperty) \ - ( (This)->lpVtbl -> GetProperty(This,PropertyId,pvProperty,pcbProperty) ) - -#endif /* COBJMACROS */ - - -#endif /* C style interface */ - - - - -#endif /* __IAssemblyName_INTERFACE_DEFINED__ */ - - -/* Additional Prototypes for ALL interfaces */ - -/* end of Additional Prototypes */ - -#ifdef __cplusplus -} -#endif - -#endif - - diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index cde932098692d..0c2021dab02d9 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -345,4 +345,4 @@ if(FEATURE_EVENT_TRACE) endif(FEATURE_EVENT_TRACE) # Install the static PAL library for VS -_install (TARGETS coreclrpal DESTINATION lib) +install_clr (TARGETS coreclrpal DESTINATIONS lib) diff --git a/src/coreclr/pal/src/configure.cmake b/src/coreclr/pal/src/configure.cmake index 28f8d708afdd6..ee4a3241196ea 100644 --- a/src/coreclr/pal/src/configure.cmake +++ b/src/coreclr/pal/src/configure.cmake @@ -1402,4 +1402,4 @@ check_prototype_definition( ${STATFS_INCLUDES} HAVE_NON_LEGACY_STATFS) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/src/coreclr/pal/src/cruntime/misc.cpp b/src/coreclr/pal/src/cruntime/misc.cpp index 4ba36c60a2ada..7a2690f132233 100644 --- a/src/coreclr/pal/src/cruntime/misc.cpp +++ b/src/coreclr/pal/src/cruntime/misc.cpp @@ -172,7 +172,12 @@ PAL_time(PAL_time_t *tloc) PERF_ENTRY(time); ENTRY( "time( tloc=%p )\n",tloc ); - result = time(tloc); + time_t t; + result = time(&t); + if (tloc != NULL) + { + *tloc = t; + } LOGEXIT( "time returning %#lx\n",result ); PERF_EXIT(time); diff --git a/src/coreclr/pal/src/eventprovider/lttngprovider/CMakeLists.txt b/src/coreclr/pal/src/eventprovider/lttngprovider/CMakeLists.txt index 5c3a04d84197e..8773cd797bcfb 100644 --- a/src/coreclr/pal/src/eventprovider/lttngprovider/CMakeLists.txt +++ b/src/coreclr/pal/src/eventprovider/lttngprovider/CMakeLists.txt @@ -52,10 +52,9 @@ add_library(eventprovider eventproviderhelpers.cpp ) -add_library(coreclrtraceptprovider +add_library_clr(coreclrtraceptprovider SHARED ${TRACEPOINT_PROVIDER_SOURCES} - ${VERSION_FILE_PATH} ) add_dependencies(eventprovider generated_eventing_headers) @@ -70,6 +69,8 @@ target_link_libraries(coreclrtraceptprovider set_target_properties(coreclrtraceptprovider PROPERTIES LINKER_LANGUAGE CXX) # Install the static eventprovider library -_install(TARGETS eventprovider DESTINATION lib) +install_clr(TARGETS eventprovider DESTINATIONS lib COMPONENT runtime) # Install the static coreclrtraceptprovider library -install_clr(TARGETS coreclrtraceptprovider ADDITIONAL_DESTINATIONS sharedFramework paltests) +install_clr(TARGETS coreclrtraceptprovider DESTINATIONS . sharedFramework COMPONENT runtime) +install(TARGETS coreclrtraceptprovider DESTINATION paltests COMPONENT paltests EXCLUDE_FROM_ALL) +add_dependencies(paltests_install coreclrtraceptprovider) diff --git a/src/coreclr/pal/src/exception/remote-unwind.cpp b/src/coreclr/pal/src/exception/remote-unwind.cpp index 5bdf10e6764dc..2d00f09377a7a 100644 --- a/src/coreclr/pal/src/exception/remote-unwind.cpp +++ b/src/coreclr/pal/src/exception/remote-unwind.cpp @@ -1451,15 +1451,16 @@ StepWithCompactEncodingArm64(const libunwindInfo* info, compact_unwind_encoding_ context->Sp = callerSp; - // return address is stored in Lr - context->Pc = context->Lr; - unw_word_t addr = callerSp; if (hasFrame && !ReadCompactEncodingRegisterPair(info, &addr, &context->Lr, &context->Fp)) { return false; } + + // unwound return address is stored in Lr + context->Pc = context->Lr; + if (compactEncoding & UNWIND_ARM64_FRAME_X19_X20_PAIR && !ReadCompactEncodingRegisterPair(info, &addr, &context->X[19], &context->X[20])) { return false; diff --git a/src/coreclr/pal/src/libunwind/CMakeLists.txt b/src/coreclr/pal/src/libunwind/CMakeLists.txt index 5b5f904634334..44e9bc6c35eed 100644 --- a/src/coreclr/pal/src/libunwind/CMakeLists.txt +++ b/src/coreclr/pal/src/libunwind/CMakeLists.txt @@ -145,8 +145,6 @@ if(CLR_CMAKE_HOST_WIN32) add_compile_options(-wd4311) # pointer truncation from 'unw_word_t *' to 'long' add_compile_options(-wd4475) # 'fprintf' : length modifier 'L' cannot be used add_compile_options(-wd4477) # fprintf argument type - - add_compile_options(-wd9025) # overriding '/TP' with '/TC' endif (CLR_CMAKE_HOST_WIN32) if(CLR_CMAKE_TARGET_ARCH_ARM) diff --git a/src/coreclr/pal/src/libunwind/configure.cmake b/src/coreclr/pal/src/libunwind/configure.cmake index 8e9c00abfbeb9..e721db79f403b 100644 --- a/src/coreclr/pal/src/libunwind/configure.cmake +++ b/src/coreclr/pal/src/libunwind/configure.cmake @@ -69,7 +69,7 @@ int main(void) return result; }" HAVE_STDALIGN_ALIGNAS) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/config.h) +configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/config.h) add_definitions(-DHAVE_CONFIG_H=1) configure_file(include/libunwind-common.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/libunwind-common.h) diff --git a/src/coreclr/pal/src/synchmgr/synchmanager.cpp b/src/coreclr/pal/src/synchmgr/synchmanager.cpp index b1fdb82adf76a..55a343de4d669 100644 --- a/src/coreclr/pal/src/synchmgr/synchmanager.cpp +++ b/src/coreclr/pal/src/synchmgr/synchmanager.cpp @@ -4623,7 +4623,11 @@ namespace CorUnix ptsAbsTmo->tv_nsec = tv.tv_usec * tccMicroSecondsToNanoSeconds; } #else - #error "Don't know how to get hi-res current time on this platform" +#ifdef DBI_COMPONENT_MONO + return ERROR_INTERNAL_ERROR; +#else + #error "Don't know how to get hi-res current time on this platform" +#endif #endif // HAVE_WORKING_CLOCK_GETTIME, HAVE_WORKING_GETTIMEOFDAY #if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK } diff --git a/src/coreclr/pal/tests/CMakeLists.txt b/src/coreclr/pal/tests/CMakeLists.txt index d6f9fa55543a8..d3192dedec99b 100644 --- a/src/coreclr/pal/tests/CMakeLists.txt +++ b/src/coreclr/pal/tests/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.14.5) - # Compile options add_definitions(-DLP64COMPATIBLE) add_definitions(-DCORECLR) diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index f59e6f43f9619..f58757a1f6d5f 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -32,7 +32,8 @@ if(FEATURE_EVENT_TRACE) add_subdirectory(eventprovider) endif(FEATURE_EVENT_TRACE) -_add_executable(paltests +add_executable_clr(paltests + EXCLUDE_FROM_ALL paltests.cpp common/palsuite.cpp #composite/object_management/event/nonshared/event.cpp @@ -918,7 +919,6 @@ _add_executable(paltests threading/WaitForSingleObject/WFSOSemaphoreTest/WFSOSemaphoreTest.cpp threading/WaitForSingleObject/WFSOThreadTest/WFSOThreadTest.cpp threading/YieldProcessor/test1/test1.cpp - ) add_dependencies(paltests coreclrpal) @@ -927,5 +927,6 @@ target_link_libraries(paltests ${COMMON_TEST_LIBRARIES} ) -_install (TARGETS paltests DESTINATION paltests) -_install (PROGRAMS runpaltests.sh runpaltestshelix.sh DESTINATION paltests) +install (TARGETS paltests DESTINATION paltests COMPONENT paltests EXCLUDE_FROM_ALL) +add_dependencies(paltests_install paltests) +install (PROGRAMS runpaltests.sh runpaltestshelix.sh DESTINATION paltests COMPONENT paltests EXCLUDE_FROM_ALL) diff --git a/src/coreclr/pal/tests/palsuite/eventprovider/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/eventprovider/CMakeLists.txt index ccb40b05444a2..fdf326edb8378 100644 --- a/src/coreclr/pal/tests/palsuite/eventprovider/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/eventprovider/CMakeLists.txt @@ -20,7 +20,8 @@ if(TARGET_UNIX) include_directories(${COREPAL_SOURCE_DIR}/inc/rt) endif(TARGET_UNIX) -_add_executable(eventprovidertest +add_executable_clr(eventprovidertest + EXCLUDE_FROM_ALL ${SOURCES} ) set(EVENT_PROVIDER_DEPENDENCIES "") @@ -39,4 +40,5 @@ endif(FEATURE_EVENT_TRACE) target_link_libraries(eventprovidertest ${EVENT_PROVIDER_DEPENDENCIES} coreclrpal) add_dependencies(eventprovidertest eventing_headers) -_install (TARGETS eventprovidertest DESTINATION paltests/eventprovider) \ No newline at end of file +install (TARGETS eventprovidertest DESTINATION paltests/eventprovider COMPONENT paltests EXCLUDE_FROM_ALL) +add_dependencies(paltests_install eventprovidertest) diff --git a/src/coreclr/pal/tests/palsuite/exception_handling/pal_sxs/test1/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/exception_handling/pal_sxs/test1/CMakeLists.txt index aec809d3fbc70..08de84f25e8de 100644 --- a/src/coreclr/pal/tests/palsuite/exception_handling/pal_sxs/test1/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/exception_handling/pal_sxs/test1/CMakeLists.txt @@ -23,7 +23,7 @@ if(CLR_CMAKE_HOST_UNIX) endif(CLR_CMAKE_HOST_UNIX) set(DLL1SOURCES dlltest1.cpp) -_add_library(paltest_pal_sxs_test1_dll1 SHARED ${DLL1SOURCES}) +add_library_clr(paltest_pal_sxs_test1_dll1 SHARED EXCLUDE_FROM_ALL ${DLL1SOURCES}) add_custom_target(dlltest1_exports DEPENDS ${EXPORTS_FILE1}) set_property(TARGET paltest_pal_sxs_test1_dll1 APPEND_STRING PROPERTY LINK_FLAGS ${EXPORTS_LINKER_OPTION1}) set_property(TARGET paltest_pal_sxs_test1_dll1 APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE1}) @@ -55,7 +55,7 @@ if(CLR_CMAKE_TARGET_OSX) endif(CLR_CMAKE_TARGET_OSX) set(DLL2SOURCES dlltest2.cpp) -_add_library(paltest_pal_sxs_test1_dll2 SHARED ${DLL2SOURCES}) +add_library_clr(paltest_pal_sxs_test1_dll2 SHARED EXCLUDE_FROM_ALL ${DLL2SOURCES}) add_custom_target(dlltest2_exports DEPENDS ${EXPORTS_FILE2}) set_property(TARGET paltest_pal_sxs_test1_dll2 APPEND_STRING PROPERTY LINK_FLAGS ${EXPORTS_LINKER_OPTION2}) set_property(TARGET paltest_pal_sxs_test1_dll2 APPEND_STRING PROPERTY LINK_DEPENDS ${EXPORTS_FILE2}) @@ -72,7 +72,7 @@ target_link_libraries(paltest_pal_sxs_test1_dll2 set(TESTSOURCES exceptionsxs.cpp) -_add_executable(paltest_pal_sxs_test1 ${TESTSOURCES}) +add_executable_clr(paltest_pal_sxs_test1 EXCLUDE_FROM_ALL ${TESTSOURCES}) add_dependencies(paltest_pal_sxs_test1 paltest_pal_sxs_test1_dll1 @@ -84,6 +84,7 @@ target_link_libraries(paltest_pal_sxs_test1 paltest_pal_sxs_test1_dll2 ) -_install (TARGETS paltest_pal_sxs_test1 DESTINATION paltests/exception_handling/pal_sxs/test1) -_install (TARGETS paltest_pal_sxs_test1_dll1 DESTINATION paltests/exception_handling/pal_sxs/test1) -_install (TARGETS paltest_pal_sxs_test1_dll2 DESTINATION paltests/exception_handling/pal_sxs/test1) \ No newline at end of file +install (TARGETS paltest_pal_sxs_test1 DESTINATION paltests/exception_handling/pal_sxs/test1 COMPONENT paltests EXCLUDE_FROM_ALL) +install (TARGETS paltest_pal_sxs_test1_dll1 DESTINATION paltests/exception_handling/pal_sxs/test1 COMPONENT paltests EXCLUDE_FROM_ALL) +install (TARGETS paltest_pal_sxs_test1_dll2 DESTINATION paltests/exception_handling/pal_sxs/test1 COMPONENT paltests EXCLUDE_FROM_ALL) +add_dependencies(paltests_install paltest_pal_sxs_test1 paltest_pal_sxs_test1_dll1 paltest_pal_sxs_test1_dll2) diff --git a/src/coreclr/palrt/CMakeLists.txt b/src/coreclr/palrt/CMakeLists.txt index a4c6fdd5ae831..f3042f2875352 100644 --- a/src/coreclr/palrt/CMakeLists.txt +++ b/src/coreclr/palrt/CMakeLists.txt @@ -17,4 +17,4 @@ add_library_clr(palrt ) # Install the static PAL library for VS -_install (TARGETS palrt DESTINATION lib) +install_clr(TARGETS palrt DESTINATIONS lib) diff --git a/src/coreclr/run-cppcheck.sh b/src/coreclr/run-cppcheck.sh index 454ab34430138..a02a4e19948b3 100755 --- a/src/coreclr/run-cppcheck.sh +++ b/src/coreclr/run-cppcheck.sh @@ -42,8 +42,7 @@ SloccountOutput="sloccount.sc" # processors available to a single process. platform="$(uname)" if [ "$platform" = "FreeBSD" ]; then - output=("$(sysctl hw.ncpu)") - NumProc="$((output[1] + 1))" + NumProc=$(($(sysctl -n hw.ncpu)+1)) elif [ "$platform" = "NetBSD" || "$platform" = "SunOS" ]; then NumProc=$(($(getconf NPROCESSORS_ONLN)+1)) else diff --git a/src/coreclr/runtime.proj b/src/coreclr/runtime.proj index 502090b0c5f89..7eb627e56efc5 100644 --- a/src/coreclr/runtime.proj +++ b/src/coreclr/runtime.proj @@ -26,16 +26,21 @@ '$(PgoInstrument)' != 'true'" Include="-enforcepgo" /> <_CoreClrBuildArg Condition="$([MSBuild]::IsOsPlatform(Windows)) and '$(CrossDac)' != ''" Include="-$(CrossDac)dac" /> - <_CoreClrBuildArg Condition="'$(Ninja)' == 'true'" Include="-ninja" /> - <_CoreClrBuildArg Condition="'$(ClrRuntimeSubset)' != 'true'" Include="-skipruntime" /> - <_CoreClrBuildArg Condition="'$(ClrJitSubset)' != 'true'" Include="-skipjit" /> - <_CoreClrBuildArg Condition="'$(ClrPalTestsSubset)' == 'true'" Include="-paltests" /> - <_CoreClrBuildArg Condition="'$(ClrAllJitsSubset)' != 'true'" Include="-skipalljits" /> + <_CoreClrBuildArg Condition="'$(Ninja)' == 'true' and !$([MSBuild]::IsOsPlatform(Windows))" Include="-ninja" /> + <_CoreClrBuildArg Condition="'$(Ninja)' == 'false' and $([MSBuild]::IsOsPlatform(Windows))" Include="-msbuild" /> <_CoreClrBuildArg Condition="'$(PgoInstrument)' == 'true'" Include="-pgoinstrument" /> <_CoreClrBuildArg Condition="'$(NoPgoOptimize)' == 'true' or '$(PgoInstrument)' == 'true'" Include="-nopgooptimize" /> <_CoreClrBuildArg Condition="'$(OfficialBuildId)' != ''" Include="/p:OfficialBuildId=$(OfficialBuildId)" /> + + <_CoreClrBuildArg Condition="'$(ClrRuntimeSubset)' == 'true'" Include="-component runtime" /> + <_CoreClrBuildArg Condition="'$(ClrJitSubset)' == 'true'" Include="-component jit" /> + <_CoreClrBuildArg Condition="'$(ClrPalTestsSubset)' == 'true'" Include="-component paltests" /> + <_CoreClrBuildArg Condition="'$(ClrAllJitsSubset)' == 'true'" Include="-component alljits" /> + <_CoreClrBuildArg Condition="'$(ClrILToolsSubset)' == 'true'" Include="-component iltools" /> + + <_CoreClrBuildScript Condition="$([MSBuild]::IsOsPlatform(Windows))">build-runtime.cmd <_CoreClrBuildScript Condition="!$([MSBuild]::IsOsPlatform(Windows))">build-runtime.sh diff --git a/src/coreclr/scripts/genEventPipe.py b/src/coreclr/scripts/genEventPipe.py index 101476c2d4686..60624eaf0cc9d 100644 --- a/src/coreclr/scripts/genEventPipe.py +++ b/src/coreclr/scripts/genEventPipe.py @@ -178,9 +178,19 @@ def generateWriteEventBody(template, providerName, eventName): bool success = true; """ % (template.estimated_size, template.estimated_size) - fnSig = template.signature pack_list = [] + + # Write out replacement for any instances of NULL for UnicodeString type input. + # ETW translates these to "NULL", so we are doing the same for EventPipe. + for paramName in fnSig.paramlist: + parameter = fnSig.getParam(paramName) + + if parameter.winType == "win:UnicodeString": + pack_list.append( + " if (!%s) { %s = W(\"NULL\"); }" % + (parameter.name, parameter.name)) + for paramName in fnSig.paramlist: parameter = fnSig.getParam(paramName) @@ -288,7 +298,7 @@ def generateEventPipeHelperFile(etwmanifest, eventpipe_directory, extern, dryRun bool WriteToBuffer(const BYTE *src, size_t len, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if(!src) return true; + if (!src) return true; if (offset + len > size) { if (!ResizeBuffer(buffer, size, offset, size + len, fixedBuffer)) @@ -302,7 +312,7 @@ def generateEventPipeHelperFile(etwmanifest, eventpipe_directory, extern, dryRun bool WriteToBuffer(PCWSTR str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if(!str) return true; + if (!str) return true; size_t byteCount = (wcslen(str) + 1) * sizeof(*str); if (offset + byteCount > size) @@ -318,7 +328,7 @@ def generateEventPipeHelperFile(etwmanifest, eventpipe_directory, extern, dryRun bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer) { - if(!str) return true; + if (!str) return true; size_t len = strlen(str) + 1; if (offset + len > size) { diff --git a/src/coreclr/scripts/genRuntimeEventSources.py b/src/coreclr/scripts/genRuntimeEventSources.py index a220217570fd6..96da3877b1472 100644 --- a/src/coreclr/scripts/genRuntimeEventSources.py +++ b/src/coreclr/scripts/genRuntimeEventSources.py @@ -300,7 +300,7 @@ def generateKeywordsClass(providerNode, outputFile): keywordsNode = node break; - writeOutput(outputFile, "public class Keywords\n") + writeOutput(outputFile, "public static class Keywords\n") writeOutput(outputFile, "{\n") increaseTabLevel() diff --git a/src/coreclr/scripts/superpmi.py b/src/coreclr/scripts/superpmi.py index 6909171afa31f..a98d3bbe0beb9 100755 --- a/src/coreclr/scripts/superpmi.py +++ b/src/coreclr/scripts/superpmi.py @@ -93,6 +93,10 @@ Upload a collection to SuperPMI Azure storage. """ +upload_private_description = """\ +Upload a collection to a local file system path. +""" + download_description = """\ Download collections from SuperPMI Azure storage. Normally, collections are automatically downloaded to a local cache @@ -147,6 +151,7 @@ spmi_location_help = """\ Directory in which to put SuperPMI files, such as downloaded MCH files, asm diffs, and repro .MC files. Optional. Default is 'spmi' within the repo 'artifacts' directory. +If 'SUPERPMI_CACHE_DIRECTORY' environment variable is set to a path, it will use that directory. """ superpmi_collect_help = """\ @@ -161,6 +166,11 @@ the Azure Storage MCH file store. UNC paths will be downloaded and cached locally. """ +private_store_help = """\ +Specify the path to one or more private SuperPMI data stores. Default: use the semicolon separated +value of the SUPERPMI_PRIVATE_STORE environment variable, if it exists. +""" + filter_help = """\ Specify one or more filters to restrict the set of MCH files to download or use from the local cache. A filter is a simple case-insensitive substring search against the MCH file path. If multiple filter @@ -218,7 +228,7 @@ core_root_parser.add_argument("-log_file", help=log_file_help) core_root_parser.add_argument("-spmi_location", help=spmi_location_help) -# Create a set of arguments common to target specification. Used for replay, upload, download, list-collections. +# Create a set of arguments common to target specification. Used for replay, upload, upload-private, download, list-collections. target_parser = argparse.ArgumentParser(add_help=False) @@ -237,7 +247,6 @@ superpmi_common_parser.add_argument("-spmi_log_file", help=spmi_log_file_help) superpmi_common_parser.add_argument("-jit_name", help="Specify the filename of the jit to use, e.g., 'clrjit_win_arm64_x64.dll'. Default is clrjit.dll/libclrjit.so") superpmi_common_parser.add_argument("--altjit", action="store_true", help="Set the altjit variables on replay.") -superpmi_common_parser.add_argument("-jitoption", action="append", help="Pass option through to the jit. Format is key=value, where key is the option name without leading COMPlus_") # subparser for collect collect_parser = subparsers.add_parser("collect", description=collect_description, parents=[core_root_parser, target_parser, superpmi_common_parser]) @@ -276,17 +285,17 @@ replay_common_parser.add_argument("-product_location", help=product_location_help) replay_common_parser.add_argument("--force_download", action="store_true", help=force_download_help) replay_common_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) +replay_common_parser.add_argument("-private_store", action="append", help=private_store_help) # subparser for replay replay_parser = subparsers.add_parser("replay", description=replay_description, parents=[core_root_parser, target_parser, superpmi_common_parser, replay_common_parser]) -# Add required arguments replay_parser.add_argument("-jit_path", help="Path to clrjit. Defaults to Core_Root JIT.") +replay_parser.add_argument("-jitoption", action="append", help="Pass option through to the jit. Format is key=value, where key is the option name without leading COMPlus_") # subparser for asmdiffs asm_diff_parser = subparsers.add_parser("asmdiffs", description=asm_diff_description, parents=[core_root_parser, target_parser, superpmi_common_parser, replay_common_parser]) -# Add required arguments asm_diff_parser.add_argument("-base_jit_path", help="Path to baseline clrjit. Defaults to baseline JIT from rolling build, by computing baseline git hash.") asm_diff_parser.add_argument("-diff_jit_path", help="Path to diff clrjit. Defaults to Core_Root JIT.") asm_diff_parser.add_argument("-git_hash", help="Use this git hash as the current hash for use to find a baseline JIT. Defaults to current git hash of source tree.") @@ -294,16 +303,26 @@ asm_diff_parser.add_argument("--diff_jit_dump", action="store_true", help="Generate JitDump output for diffs. Default: only generate asm, not JitDump.") asm_diff_parser.add_argument("-temp_dir", help="Specify a temporary directory used for a previous ASM diffs run (for which --skip_cleanup was used) to view the results. The replay command is skipped.") asm_diff_parser.add_argument("--gcinfo", action="store_true", help="Include GC info in disassembly (sets COMPlus_JitGCDump/COMPlus_NgenGCDump; requires instructions to be prefixed by offsets).") +asm_diff_parser.add_argument("-base_jit_option", action="append", help="Option to pass to the baseline JIT. Format is key=value, where key is the option name without leading COMPlus_...") +asm_diff_parser.add_argument("-diff_jit_option", action="append", help="Option to pass to the diff JIT. Format is key=value, where key is the option name without leading COMPlus_...") +asm_diff_parser.add_argument("-tag", help="Specify a word to add to the directory name where the asm diffs will be placed") # subparser for upload upload_parser = subparsers.add_parser("upload", description=upload_description, parents=[core_root_parser, target_parser]) upload_parser.add_argument("-mch_files", metavar="MCH_FILE", required=True, nargs='+', help=upload_mch_files_help) upload_parser.add_argument("-az_storage_key", help="Key for the clrjit Azure Storage location. Default: use the value of the CLRJIT_AZ_KEY environment variable.") -upload_parser.add_argument("-jit_location", help="Location for the base clrjit. If not passed this will be assumed to be from the Core_Root.") upload_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) upload_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) +# subparser for upload-private +upload_private_parser = subparsers.add_parser("upload-private", description=upload_private_description, parents=[core_root_parser, target_parser]) + +upload_private_parser.add_argument("-mch_files", metavar="MCH_FILE", required=True, nargs='+', help=upload_mch_files_help) +upload_private_parser.add_argument("-private_store", required=True, help="Target directory root of the private store in which to place the files.") +upload_private_parser.add_argument("-jit_ee_version", help=jit_ee_version_help) +upload_private_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) + # subparser for download download_parser = subparsers.add_parser("download", description=download_description, parents=[core_root_parser, target_parser]) @@ -312,6 +331,7 @@ download_parser.add_argument("--skip_cleanup", action="store_true", help=skip_cleanup_help) download_parser.add_argument("--force_download", action="store_true", help=force_download_help) download_parser.add_argument("-mch_files", metavar="MCH_FILE", nargs='+', help=replay_mch_files_help) +download_parser.add_argument("-private_store", action="append", help=private_store_help) # subparser for list-collections list_collections_parser = subparsers.add_parser("list-collections", description=list_collections_description, parents=[core_root_parser, target_parser]) @@ -646,7 +666,7 @@ def run_and_log(command, log_level=logging.DEBUG): Process return code """ - logging.debug("Invoking: %s", " ".join(command)) + logging.log(log_level, "Invoking: %s", " ".join(command)) proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout_output, _ = proc.communicate() for line in stdout_output.decode('utf-8', errors='replace').splitlines(): # There won't be any stderr output since it was piped to stdout @@ -695,6 +715,46 @@ def check_target_arch(coreclr_args, target_arch): def check_mch_arch(coreclr_args, mch_arch): return (mch_arch is not None) and (mch_arch in coreclr_args.valid_arches) + +def create_artifacts_base_name(coreclr_args, mch_file): + """ Create an appropriate "base" name for use creating a directory name related to MCH file playback. + This will later be prepended by "asm." or "jitdump.", for example, and + create_unique_directory_name() should be called on the final name to ensure it is unique. + + Use the MCH file base name as the main part of the directory name, removing + the trailing ".mch", if any. + + If there is a tag specified (for asm diffs), prepend the tag. + + Args: + coreclr_args : the parsed arguments + mch_file (str) : the MCH file name that is being replayed. + + Returns: + A directory name to be used. + """ + artifacts_base_name = os.path.basename(mch_file) + if artifacts_base_name.lower().endswith(".mch"): + artifacts_base_name = artifacts_base_name[:-4] + if hasattr(coreclr_args, "tag") and coreclr_args.tag is not None: + artifacts_base_name = "{}.{}".format(coreclr_args.tag, artifacts_base_name) + return artifacts_base_name + + +def is_url(path): + """ Return True if this looks like a URL + + Args: + path (str) : name to check + + Returns: + True it it looks like an URL, False otherwise. + """ + # Probably could use urllib.parse to be more precise. + # If it doesn't look like an URL, treat it like a file, possibly a UNC file. + return path.lower().startswith("http:") or path.lower().startswith("https:") + + ################################################################################ # Helper classes ################################################################################ @@ -813,6 +873,7 @@ def run_to_completion(self, async_callback, *extra_args): reset_env = os.environ.copy() loop = asyncio.get_event_loop() loop.run_until_complete(self.__run_to_completion__(async_callback, *extra_args)) + os.environ.clear() os.environ.update(reset_env) ################################################################################ @@ -1109,7 +1170,7 @@ async def run_pmi(print_prefix, assembly, self): helper = AsyncSubprocessHelper(assemblies, verbose=True) helper.run_to_completion(run_pmi, self) - # Review: does this delete the items that weren't there before we updated with the PMI variables? + os.environ.clear() os.environ.update(old_env) ################################################################################################ end of "self.coreclr_args.pmi is True" @@ -1187,7 +1248,7 @@ async def run_crossgen(print_prefix, assembly, self): helper = AsyncSubprocessHelper(assemblies, verbose=True) helper.run_to_completion(run_crossgen, self) - # Review: does this delete the items that weren't there before we updated with the crossgen variables? + os.environ.clear() os.environ.update(old_env) ################################################################################################ end of "self.coreclr_args.crossgen is True" @@ -1309,7 +1370,7 @@ async def run_crossgen2(print_prefix, assembly, self): helper = AsyncSubprocessHelper(assemblies, verbose=True) helper.run_to_completion(run_crossgen2, self) - # Review: does this delete the items that weren't there before we updated with the crossgen2 variables? + os.environ.clear() os.environ.update(old_env) ################################################################################################ end of "self.coreclr_args.crossgen2 is True" @@ -1468,14 +1529,14 @@ def print_fail_mcl_file_method_numbers(fail_mcl_file): logging.debug(line) -def save_repro_mc_files(temp_location, coreclr_args, repro_base_command_line): +def save_repro_mc_files(temp_location, coreclr_args, artifacts_base_name, repro_base_command_line): """ For commands that use the superpmi "-r" option to create "repro" .mc files, copy these to a location where they are saved (and not in a "temp" directory) for easy use by the user. """ # If there are any .mc files, drop them into artifacts/repro/../*.mc mc_files = [os.path.join(temp_location, item) for item in os.listdir(temp_location) if item.endswith(".mc")] if len(mc_files) > 0: - repro_location = create_unique_directory_name(coreclr_args.spmi_location, "repro.{}.{}.{}".format(coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type)) + repro_location = create_unique_directory_name(coreclr_args.spmi_location, "repro.{}".format(artifacts_base_name)) repro_files = [] for item in mc_files: @@ -1598,11 +1659,11 @@ def replay(self): logging.info("Running SuperPMI replay of %s", mch_file) - flags = common_flags + flags = common_flags.copy() fail_mcl_file = os.path.join(temp_location, os.path.basename(mch_file) + "_fail.mcl") flags += [ - "-f", fail_mcl_file, # Failing mc List + "-f", fail_mcl_file # Failing mc List ] command = [self.superpmi_path] + flags + [self.jit_path, mch_file] @@ -1620,7 +1681,8 @@ def replay(self): logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file") print_fail_mcl_file_method_numbers(fail_mcl_file) repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(repro_flags), self.jit_path) - save_repro_mc_files(temp_location, self.coreclr_args, repro_base_command_line) + artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file) + save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line) if not self.coreclr_args.skip_cleanup: if os.path.isfile(fail_mcl_file): @@ -1715,6 +1777,12 @@ def replay_with_asm_diffs(self): "COMPlus_JitDump": "*", "COMPlus_NgenDump": "*" }) + asm_complus_vars_full_env = os.environ.copy() + asm_complus_vars_full_env.update(asm_complus_vars) + + jit_dump_complus_vars_full_env = os.environ.copy() + jit_dump_complus_vars_full_env.update(jit_dump_complus_vars) + target_flags = [] if self.coreclr_args.arch != self.coreclr_args.target_arch: target_flags += [ "-target", self.coreclr_args.target_arch ] @@ -1722,6 +1790,19 @@ def replay_with_asm_diffs(self): altjit_asm_diffs_flags = target_flags altjit_replay_flags = target_flags + base_option_flags = [] + if self.coreclr_args.base_jit_option: + for o in self.coreclr_args.base_jit_option: + base_option_flags += "-jitoption", o + base_option_flags_for_diff_artifact = base_option_flags + + diff_option_flags = [] + diff_option_flags_for_diff_artifact = [] + if self.coreclr_args.diff_jit_option: + for o in self.coreclr_args.diff_jit_option: + diff_option_flags += "-jit2option", o + diff_option_flags_for_diff_artifact += "-jitoption", o + if self.coreclr_args.altjit: altjit_asm_diffs_flags += [ "-jitoption", "force", "AltJit=*", @@ -1771,6 +1852,8 @@ def replay_with_asm_diffs(self): "-r", os.path.join(temp_location, "repro") # Repro name, create .mc repro files ] flags += altjit_asm_diffs_flags + flags += base_option_flags + flags += diff_option_flags if not self.coreclr_args.sequential: flags += [ "-p" ] @@ -1797,13 +1880,15 @@ def replay_with_asm_diffs(self): files_with_replay_failures.append(mch_file) result = False + artifacts_base_name = create_artifacts_base_name(self.coreclr_args, mch_file) + if is_nonzero_length_file(fail_mcl_file): # Unclean replay. Examine the contents of the fail.mcl file to dig into failures. if return_code == 0: logging.warning("Warning: SuperPMI returned a zero exit code, but generated a non-zero-sized mcl file") print_fail_mcl_file_method_numbers(fail_mcl_file) repro_base_command_line = "{} {} {}".format(self.superpmi_path, " ".join(altjit_asm_diffs_flags), self.diff_jit_path) - save_repro_mc_files(temp_location, self.coreclr_args, repro_base_command_line) + save_repro_mc_files(temp_location, self.coreclr_args, artifacts_base_name, repro_base_command_line) # There were diffs. Go through each method that created diffs and # create a base/diff asm file with diffable asm. In addition, create @@ -1823,7 +1908,7 @@ def replay_with_asm_diffs(self): mcl_lines = [item.strip() for item in mcl_lines] self.diff_mcl_contents = mcl_lines - asm_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "asm.{}.{}.{}".format(self.coreclr_args.host_os, self.coreclr_args.arch, self.coreclr_args.build_type)) + asm_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "asm.{}".format(artifacts_base_name)) base_asm_location = os.path.join(asm_root_dir, "base") diff_asm_location = os.path.join(asm_root_dir, "diff") os.makedirs(base_asm_location) @@ -1831,7 +1916,7 @@ def replay_with_asm_diffs(self): if self.coreclr_args.diff_jit_dump: # If JIT dumps are requested, create a diff and baseline directory for JIT dumps - jitdump_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "jitdump.{}.{}.{}".format(self.coreclr_args.host_os, self.coreclr_args.arch, self.coreclr_args.build_type)) + jitdump_root_dir = create_unique_directory_name(self.coreclr_args.spmi_location, "jitdump.{}".format(artifacts_base_name)) base_dump_location = os.path.join(jitdump_root_dir, "base") diff_dump_location = os.path.join(jitdump_root_dir, "diff") os.makedirs(base_dump_location) @@ -1840,7 +1925,7 @@ def replay_with_asm_diffs(self): text_differences = queue.Queue() jit_dump_differences = queue.Queue() - async def create_replay_artifacts(print_prefix, item, self, mch_file, env_vars, jit_differences_queue, base_location, diff_location, extension): + async def create_replay_artifacts(print_prefix, item, self, mch_file, env, jit_differences_queue, base_location, diff_location, extension): """ Run superpmi over an MC to create JIT asm or JIT dumps for the method. """ # Setup flags to call SuperPMI for both the diff jit and the base jit @@ -1851,29 +1936,26 @@ async def create_replay_artifacts(print_prefix, item, self, mch_file, env_vars, ] flags += altjit_replay_flags - # Add in all the COMPlus variables we need - os.environ.update(env_vars) - # Change the working directory to the core root we will call SuperPMI from. # This is done to allow libcoredistools to be loaded correctly on unix # as the LoadLibrary path will be relative to the current directory. with ChangeDir(self.coreclr_args.core_root): - async def create_one_artifact(jit_path: str, location: str) -> str: + async def create_one_artifact(jit_path: str, location: str, flags: list[str]) -> str: command = [self.superpmi_path] + flags + [jit_path, mch_file] item_path = os.path.join(location, "{}{}".format(item, extension)) with open(item_path, 'w') as file_handle: logging.debug("%sGenerating %s", print_prefix, item_path) logging.debug("%sInvoking: %s", print_prefix, " ".join(command)) - proc = await asyncio.create_subprocess_shell(" ".join(command), stdout=file_handle, stderr=asyncio.subprocess.PIPE) + proc = await asyncio.create_subprocess_shell(" ".join(command), stdout=file_handle, stderr=asyncio.subprocess.PIPE, env=env) await proc.communicate() with open(item_path, 'r') as file_handle: generated_txt = file_handle.read() return generated_txt # Generate diff and base JIT dumps - base_txt = await create_one_artifact(self.base_jit_path, base_location) - diff_txt = await create_one_artifact(self.diff_jit_path, diff_location) + base_txt = await create_one_artifact(self.base_jit_path, base_location, flags + base_option_flags_for_diff_artifact) + diff_txt = await create_one_artifact(self.diff_jit_path, diff_location, flags + diff_option_flags_for_diff_artifact) if base_txt != diff_txt: jit_differences_queue.put_nowait(item) @@ -1883,13 +1965,13 @@ async def create_one_artifact(jit_path: str, location: str) -> str: for item in self.diff_mcl_contents: diff_items.append(item) - logging.info("Creating dasm files") + logging.info("Creating dasm files: %s %s", base_asm_location, diff_asm_location) subproc_helper = AsyncSubprocessHelper(diff_items, verbose=True) - subproc_helper.run_to_completion(create_replay_artifacts, self, mch_file, asm_complus_vars, text_differences, base_asm_location, diff_asm_location, ".dasm") + subproc_helper.run_to_completion(create_replay_artifacts, self, mch_file, asm_complus_vars_full_env, text_differences, base_asm_location, diff_asm_location, ".dasm") if self.coreclr_args.diff_jit_dump: - logging.info("Creating JitDump files") - subproc_helper.run_to_completion(create_replay_artifacts, self, mch_file, jit_dump_complus_vars, jit_dump_differences, base_dump_location, diff_dump_location, ".txt") + logging.info("Creating JitDump files: %s %s", base_dump_location, diff_dump_location) + subproc_helper.run_to_completion(create_replay_artifacts, self, mch_file, jit_dump_complus_vars_full_env, jit_dump_differences, base_dump_location, diff_dump_location, ".txt") logging.info("Differences found. To replay SuperPMI use:") logging.info("") @@ -2240,7 +2322,7 @@ def determine_jit_ee_version(coreclr_args): NOTE: When using mcs, we need to run the tool. So we need a version that will run. If a user specifies an "-arch" argument that creates a Core_Root path that won't run, like an arm32 Core_Root on an - x64 machine, this won't work. This could happen if doing "upload" or "list-collections" on + x64 machine, this won't work. This could happen if doing "upload", "upload-private", or "list-collections" on collections from a machine that didn't create the native collections. We should create a "native" Core_Root and use that in case there are "cross-arch" scenarios. @@ -2412,35 +2494,27 @@ def list_superpmi_collections_container(path_filter=lambda unused: True): return list_superpmi_collections_container_via_rest_api(path_filter) -def process_mch_files_arg(coreclr_args): - """ Process the -mch_files argument. If the argument is empty, then download files from Azure Storage. - If the argument is non-empty, check it for UNC paths and download/cache those files, replacing - them with a reference to the newly cached local paths (this is on Windows only). +def process_local_mch_files(coreclr_args, mch_files, mch_cache_dir): + """ Process the MCH files to use. Args: coreclr_args (CoreclrArguments): parsed args + mch_files (list): list of MCH files locations. Normally, this comes from the `-mch_files` argument, but it can + also come from the `private_store` argument. It can be a list of files or directories or both. + mch_cache_dir (str): the directory to cache any downloads. Returns: - nothing - - coreclr_args.mch_files is updated - + list of full paths of locally cached MCH files to use """ - if coreclr_args.mch_files is None: - coreclr_args.mch_files = download_mch(coreclr_args, include_baseline_jit=True) - return - # Create the cache location. Note that we'll create it even if we end up not copying anything. - default_mch_root_dir = os.path.join(coreclr_args.spmi_location, "mch") - default_mch_dir = os.path.join(default_mch_root_dir, "{}.{}.{}".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) - if not os.path.isdir(default_mch_dir): - os.makedirs(default_mch_dir) + if not os.path.isdir(mch_cache_dir): + os.makedirs(mch_cache_dir) # Process the mch_files list. Download and cache UNC and HTTP files. urls = [] local_mch_files = [] - for item in coreclr_args.mch_files: + for item in mch_files: # On Windows only, see if any of the mch_files are UNC paths (i.e., "\\server\share\..."). # If so, download and cache all the files found there to our usual local cache location, to avoid future network access. if coreclr_args.host_os == "windows" and item.startswith("\\\\"): @@ -2448,30 +2522,30 @@ def process_mch_files_arg(coreclr_args): # This happens naturally if a directory is passed and we search for all .mch and .mct files in that directory. mch_file = os.path.abspath(item) if os.path.isfile(mch_file) and mch_file.endswith(".mch"): - files = [ mch_file ] + urls.append(mch_file) mct_file = mch_file + ".mct" if os.path.isfile(mct_file): - files.append(mct_file) + urls.append(mct_file) else: - files = get_files_from_path(mch_file, match_func=lambda path: any(path.endswith(extension) for extension in [".mch", ".mct"])) - - for file in files: - # Download file to cache, and report that as the file to use. - cache_file = os.path.join(default_mch_dir, os.path.basename(file)) - logging.info("Cache %s => %s", file, cache_file) - local_mch_file = shutil.copy2(file, cache_file) - local_mch_files.append(local_mch_file) + urls += get_files_from_path(mch_file, match_func=lambda path: any(path.lower().endswith(extension) for extension in [".mch", ".mct", ".zip"])) elif item.lower().startswith("http:") or item.lower().startswith("https:"): # probably could use urllib.parse to be more precise urls.append(item) else: # Doesn't appear to be a UNC path (on Windows) or a URL, so just use it as-is. local_mch_files.append(item) + # Now apply any filtering we've been asked to do. + def filter_local_path(path): + path = path.lower() + return (coreclr_args.filter is None) or any((filter_item.lower() in path) for filter_item in coreclr_args.filter) + + urls = [url for url in urls if filter_local_path(url)] + # Download all the urls at once, and add the local cache filenames to our accumulated list of local file names. if len(urls) != 0: - local_mch_files += download_urls(urls, default_mch_dir) + local_mch_files += download_files(urls, mch_cache_dir) - # Special case: walk the URLs list list and for every ".mch" or ".mch.zip" file, check to see that either the associated ".mct" file is already + # Special case: walk the URLs list and for every ".mch" or ".mch.zip" file, check to see that either the associated ".mct" file is already # in the list, or add it to a new list to attempt to download (but don't fail the download if it doesn't exist). mct_urls = [] for url in urls: @@ -2480,36 +2554,76 @@ def process_mch_files_arg(coreclr_args): if mct_url not in urls: mct_urls.append(mct_url) if len(mct_urls) != 0: - local_mch_files += download_urls(mct_urls, default_mch_dir, fail_if_not_found=False) + local_mch_files += download_files(mct_urls, mch_cache_dir, fail_if_not_found=False) - coreclr_args.mch_files = local_mch_files + # Even though we might have downloaded MCT files, only return the set of MCH files. + local_mch_files = [file for file in local_mch_files if any(file.lower().endswith(extension) for extension in [".mch"])] + return local_mch_files -def download_mch(coreclr_args, include_baseline_jit=False): - """ Download the mch files. This can be called to re-download files and - overwrite them in the target location. + +def process_mch_files_arg(coreclr_args): + """ Process the -mch_files argument. If the argument is not specified, then download files + from Azure Storage and any specified private MCH stores. + + Any files on UNC (i.e., "\\server\share" paths on Windows) or Azure Storage stores, + even if specified via the `-mch_files` argument, will be downloaded and cached locally, + replacing the paths with a reference to the newly cached local paths. + + If the `-mch_files` argument is specified, files are always either used directly or copied and + cached locally. These will be the only files used. + + If the `-mch_files` argument is not specified, and there exists a cache, then only files already + in the cache are used and no MCH stores are consulted, unless the `--force_download` option is + specified, in which case normal MCH store processing is done. This behavior is to avoid + touching the network unless required. Args: coreclr_args (CoreclrArguments): parsed args - include_baseline_jit (bool): If True, also download the baseline jit Returns: - list containing the directory to which the files were downloaded - + list of local full paths of MCH files or directories to use """ - default_mch_root_dir = os.path.join(coreclr_args.spmi_location, "mch") - default_mch_dir = os.path.join(default_mch_root_dir, "{}.{}.{}".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) + mch_cache_dir = os.path.join(coreclr_args.spmi_location, "mch", "{}.{}.{}".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch)) + + # If an `-mch_files` argument was given, then use exactly that set of files. + if coreclr_args.mch_files is not None: + return process_local_mch_files(coreclr_args, coreclr_args.mch_files, mch_cache_dir) - if os.path.isdir(default_mch_dir) and not coreclr_args.force_download: + # Otherwise, use both Azure Storage, and optionally, private stores. + # See if the cache directory already exists. If so, we just use it (unless `--force_download` is passed). + + if os.path.isdir(mch_cache_dir) and not coreclr_args.force_download: # The cache directory is already there, and "--force_download" was passed, so just # assume it's got what we want. # NOTE: a different solution might be to verify that everything we would download is # already in the cache, and simply not download if it is. However, that would # require hitting the network, and currently once you've cached these, you # don't need to do that. - logging.info("Found download cache directory \"%s\" and --force_download not set; skipping download", default_mch_dir) - return [ default_mch_dir ] + logging.info("Found download cache directory \"%s\" and --force_download not set; skipping download", mch_cache_dir) + return [ mch_cache_dir ] + + local_mch_paths = download_mch_from_azure(coreclr_args, mch_cache_dir) + + # Add the private store files + if coreclr_args.private_store is not None: + local_mch_paths += process_local_mch_files(coreclr_args, coreclr_args.private_store, mch_cache_dir) + + return local_mch_paths + + +def download_mch_from_azure(coreclr_args, target_dir): + """ Download the mch files. This can be called to re-download files and + overwrite them in the target location. + + Args: + coreclr_args (CoreclrArguments): parsed args + target_dir (str): target directory to download the files + + Returns: + list containing the local path of files downloaded + """ blob_filter_string = "{}/{}/{}/".format(coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch).lower() @@ -2520,50 +2634,58 @@ def download_mch(coreclr_args, include_baseline_jit=False): # If there are filters, only download those matching files. def filter_superpmi_collections(path): path = path.lower() - if "clrjit" in path and not include_baseline_jit: - return False return path.startswith(blob_filter_string) and ((coreclr_args.filter is None) or any((filter_item.lower() in path) for filter_item in coreclr_args.filter)) paths = list_superpmi_collections_container(filter_superpmi_collections) if paths is None or len(paths) == 0: - print("No MCH files to download from {}".format(blob_filter_string)) + print("No Azure Storage MCH files to download from {}".format(blob_filter_string)) return [] blob_url_prefix = "{}/{}/".format(az_blob_storage_superpmi_container_uri, az_collections_root_folder) urls = [blob_url_prefix + path for path in paths] - download_urls(urls, default_mch_dir) - return [ default_mch_dir ] + return download_files(urls, target_dir) -def download_urls(urls, target_dir, verbose=True, fail_if_not_found=True): - """ Download a set of files, specified as URLs, to a target directory. - If the URLs are to .ZIP files, then uncompress them and copy all contents - to the target directory. +def download_files(paths, target_dir, verbose=True, fail_if_not_found=True): + """ Download a set of files, specified as URLs or paths (such as Windows UNC paths), + to a target directory. If a file is a .ZIP file, then uncompress the file and + copy all its contents to the target directory. Args: - urls (list): the URLs to download - target_dir (str): target directory where files are copied. Directory must exist + paths (list): the URLs and paths to download + target_dir (str): target directory where files are copied. + verbse (bool): if True, do verbose logging. fail_if_not_found (bool): if True, fail if a download fails due to file not found (HTTP error 404). Otherwise, ignore the failure. Returns: - list of local filenames of downloaded files + list of full paths of local filenames of downloaded files in the target directory """ + if len(paths) == 0: + logging.warning("No files specified to download") + return None + if verbose: logging.info("Downloading:") - for url in urls: - logging.info(" %s", url) + for item_path in paths: + logging.info(" %s", item_path) - local_files = [] + # Create the target directory now, if it doesn't already exist. + target_dir = os.path.abspath(target_dir) + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + + local_paths = [] # In case we'll need a temp directory for ZIP file processing, create it first. with TempDir() as temp_location: - for url in urls: - item_name = url.split("/")[-1] + for item_path in paths: + is_item_url = is_url(item_path) + item_name = item_path.split("/")[-1] if is_item_url else os.path.basename(item_path) - if url.lower().endswith(".zip"): + if item_path.lower().endswith(".zip"): # Delete everything in the temp_location (from previous iterations of this loop, so previous URL downloads). temp_location_items = [os.path.join(temp_location, item) for item in os.listdir(temp_location)] for item in temp_location_items: @@ -2573,9 +2695,15 @@ def download_urls(urls, target_dir, verbose=True, fail_if_not_found=True): os.remove(item) download_path = os.path.join(temp_location, item_name) - ok = download_one_url(url, download_path, fail_if_not_found) - if not ok: - continue + if is_item_url: + ok = download_one_url(item_path, download_path, fail_if_not_found) + if not ok: + continue + else: + if fail_if_not_found or os.path.isfile(item_path): + if verbose: + logging.info("Download: %s -> %s", item_path, download_path) + shutil.copy2(item_path, download_path) if verbose: logging.info("Uncompress %s", download_path) @@ -2583,33 +2711,33 @@ def download_urls(urls, target_dir, verbose=True, fail_if_not_found=True): file_handle.extractall(temp_location) # Copy everything that was extracted to the target directory. - if not os.path.isdir(target_dir): - os.makedirs(target_dir) items = [ os.path.join(temp_location, item) for item in os.listdir(temp_location) if not item.endswith(".zip") ] for item in items: target_path = os.path.join(target_dir, os.path.basename(item)) if verbose: logging.info("Copy %s -> %s", item, target_path) shutil.copy2(item, target_dir) - local_files.append(target_path) + local_paths.append(target_path) else: # Not a zip file; download directory to target directory - if not os.path.isdir(target_dir): - os.makedirs(target_dir) download_path = os.path.join(target_dir, item_name) - ok = download_one_url(url, download_path, fail_if_not_found) - if not ok: - continue - local_files.append(download_path) + if is_item_url: + ok = download_one_url(item_path, download_path, fail_if_not_found) + if not ok: + continue + else: + if fail_if_not_found or os.path.isfile(item_path): + if verbose: + logging.info("Download: %s -> %s", item_path, download_path) + shutil.copy2(item_path, download_path) + local_paths.append(download_path) - return local_files + return local_paths def upload_mch(coreclr_args): """ Upload a set of MCH files. Each MCH file is first ZIP compressed to save data space and upload/download time. - TODO: Upload baseline altjits or cross-compile JITs? - Args: coreclr_args (CoreclrArguments): parsed args """ @@ -2673,23 +2801,62 @@ def upload_blob(file, blob_name): logging.info("Uploading: %s (%s) -> %s", file, zip_path, az_blob_storage_superpmi_container_uri + "/" + blob_name) upload_blob(zip_path, blob_name) - # Upload a JIT matching the MCH files just collected. - # Consider: rename uploaded JIT to include build_type + logging.info("Uploaded {:n} bytes".format(total_bytes_uploaded)) - jit_location = coreclr_args.jit_location - if jit_location is None: - jit_name = determine_jit_name(coreclr_args) - jit_location = os.path.join(coreclr_args.core_root, jit_name) - assert os.path.isfile(jit_location) +def upload_private_mch(coreclr_args): + """ Upload a set of MCH files. Each MCH file is first ZIP compressed to save data space and upload/download time. - jit_name = os.path.basename(jit_location) - jit_blob_name = "{}/{}".format(blob_folder_name, jit_name) - logging.info("Uploading: %s -> %s", jit_location, az_blob_storage_superpmi_container_uri + "/" + jit_blob_name) - upload_blob(jit_location, jit_blob_name) + Args: + coreclr_args (CoreclrArguments): parsed args + """ - jit_stat_result = os.stat(jit_location) - total_bytes_uploaded += jit_stat_result.st_size + files = [] + for item in coreclr_args.mch_files: + files += get_files_from_path(item, match_func=lambda path: any(path.endswith(extension) for extension in [".mch"])) + + files_to_upload = [] + # Special case: walk the files list and for every ".mch" file, check to see that either the associated ".mct" file is already + # in the list, or add it if the ".mct" file exists. + for file in files.copy(): + if file.endswith(".mch") and os.stat(file).st_size > 0: + files_to_upload.append(file) + mct_file = file + ".mct" + if os.path.isfile(mct_file) and os.stat(mct_file).st_size > 0: + files_to_upload.append(mct_file) + + logging.info("Uploading:") + for item in files_to_upload: + logging.info(" %s", item) + + file_folder_name = os.path.join(coreclr_args.private_store, coreclr_args.jit_ee_version, coreclr_args.target_os, coreclr_args.mch_arch) + if not os.path.isdir(file_folder_name): + os.makedirs(file_folder_name) + + total_bytes_uploaded = 0 + + with TempDir() as temp_location: + for file in files_to_upload: + # Zip compress the file we will upload + zip_name = os.path.basename(file) + ".zip" + zip_path = os.path.join(temp_location, zip_name) + logging.info("Compress %s -> %s", file, zip_path) + with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file: + zip_file.write(file, os.path.basename(file)) + + original_stat_result = os.stat(file) + zip_stat_result = os.stat(zip_path) + logging.info("Compressed {:n} to {:n} bytes".format(original_stat_result.st_size, zip_stat_result.st_size)) + total_bytes_uploaded += zip_stat_result.st_size + + target_path = os.path.join(file_folder_name, zip_name) + logging.info("Uploading: %s (%s) -> %s", file, zip_path, target_path) + + if os.path.exists(target_path): + logging.warning("Warning: replacing existing file '%s'!", target_path) + os.remove(target_path) + + shutil.copy2(zip_path, target_path) logging.info("Uploaded {:n} bytes".format(total_bytes_uploaded)) @@ -2706,7 +2873,7 @@ def list_collections_command(coreclr_args): # Determine if a URL in Azure Storage should be allowed. The URL looks like: # https://clrjit.blob.core.windows.net/superpmi/jit-ee-guid/Linux/x64/Linux.x64.Checked.frameworks.mch.zip # By default, filter to just the current jit-ee-guid, OS, and architecture. - # Only include MCH files, not clrjit.dll or MCT (TOC) files. + # Only include MCH files, not MCT (TOC) files. def filter_superpmi_collections(path: str): path = path.lower() return (path.endswith(".mch") or path.endswith(".mch.zip")) and (coreclr_args.all or path.startswith(blob_filter_string)) @@ -2748,7 +2915,7 @@ def list_collections_local_command(coreclr_args): # Determine if a file should be allowed. The filenames look like: # c:\gh\runtime\artifacts\spmi\mch\a5eec3a4-4176-43a7-8c2b-a05b551d4f49.windows.x64\corelib.windows.x64.Checked.mch # c:\gh\runtime\artifacts\spmi\mch\a5eec3a4-4176-43a7-8c2b-a05b551d4f49.windows.x64\corelib.windows.x64.Checked.mch.mct - # Only include MCH files, not clrjit.dll or MCT (TOC) files. + # Only include MCH files, not MCT (TOC) files. def filter_superpmi_collections(path: str): return path.lower().endswith(".mch") @@ -2812,28 +2979,30 @@ def merge_mch(coreclr_args): return True -def get_mch_files_for_replay(coreclr_args): - """ Given the argument `mch_files`, and any specified filters, find all the MCH files to - use for replay. +def get_mch_files_for_replay(local_mch_paths, filters): + """ Given a list of local MCH files, and any specified filters (in coreclr_args.filter), + find all the MCH files to use for replay. Note that `local_mch_paths` can contain + both files and directories. Args: - coreclr_args (CoreclrArguments) : parsed args + local_mch_paths (list) : list of local files and directories to use to find MCH files to use + filters (list) : list of strings, one of which must match each candidate MCH path Returns: - None if error (with an error message already printed), else a list of MCH files. + None if error (with an error message already printed), else a filtered list of full paths of MCH files. """ - if coreclr_args.mch_files is None: + if local_mch_paths is None: logging.error("No MCH files specified") return None mch_files = [] - for item in coreclr_args.mch_files: + for item in local_mch_paths: # If there are specified filters, only run those matching files. mch_files += get_files_from_path(item, match_func=lambda path: any(path.endswith(extension) for extension in [".mch"]) - and ((coreclr_args.filter is None) or any(filter_item.lower() in path for filter_item in coreclr_args.filter))) + and ((filters is None) or any(filter_item.lower() in path for filter_item in filters))) if len(mch_files) == 0: logging.error("No MCH files found to replay") @@ -2880,6 +3049,7 @@ def process_base_jit_path_arg(coreclr_args): if coreclr_args.base_jit_path is not None: if not os.path.isfile(coreclr_args.base_jit_path): raise RuntimeError("Specified -base_jit_path does not point to a file") + coreclr_args.base_jit_path = os.path.abspath(coreclr_args.base_jit_path) return # We cache baseline jits under the following directory. Note that we can't create the full directory path @@ -2957,7 +3127,7 @@ def process_base_jit_path_arg(coreclr_args): blob_folder_name = "{}/{}/{}/{}/{}/{}".format(az_builds_root_folder, git_hash, coreclr_args.host_os, coreclr_args.arch, coreclr_args.build_type, jit_name) blob_uri = "{}/{}".format(az_blob_storage_jitrollingbuild_container_uri, blob_folder_name) urls = [ blob_uri ] - local_files = download_urls(urls, basejit_dir, verbose=False, fail_if_not_found=False) + local_files = download_files(urls, basejit_dir, verbose=False, fail_if_not_found=False) if len(local_files) > 0: if hashnum > 1: @@ -3023,7 +3193,13 @@ def setup_args(args): "Unable to set log_file.") def setup_spmi_location_arg(spmi_location): - return os.path.abspath(os.path.join(coreclr_args.artifacts_location, "spmi")) if spmi_location is None else spmi_location + if spmi_location is None: + if "SUPERPMI_CACHE_DIRECTORY" in os.environ: + spmi_location = os.environ["SUPERPMI_CACHE_DIRECTORY"] + spmi_location = os.path.abspath(spmi_location) + else: + spmi_location = os.path.abspath(os.path.join(coreclr_args.artifacts_location, "spmi")) + return spmi_location coreclr_args.verify(args, "spmi_location", @@ -3130,11 +3306,6 @@ def verify_superpmi_common_args(): lambda unused: True, "Unable to set spmi_log_file.") - coreclr_args.verify(args, - "jitoption", - lambda unused: True, - "Unable to set jitoption") - if coreclr_args.spmi_log_file is not None and not coreclr_args.sequential: print("-spmi_log_file requires --sequential") sys.exit(1) @@ -3168,6 +3339,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set mch_files") + coreclr_args.verify(args, + "private_store", + lambda item: True, + "Specify private_store or set environment variable SUPERPMI_PRIVATE_STORE to use a private store.", + modify_arg=lambda arg: os.environ["SUPERPMI_PRIVATE_STORE"].split(";") if arg is None and "SUPERPMI_PRIVATE_STORE" in os.environ else arg) + if coreclr_args.mode == "collect": verify_target_args() @@ -3183,6 +3360,11 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set altjit.") + coreclr_args.verify(args, + "jitoption", # The replay code checks this, so make sure it's set + lambda unused: True, + "Unable to set jitoption") + coreclr_args.verify(args, "collection_command", lambda unused: True, @@ -3350,6 +3532,11 @@ def verify_replay_common_args(): "Error: JIT not found at jit_path {}".format, modify_arg=setup_jit_path_arg) + coreclr_args.verify(args, + "jitoption", + lambda unused: True, + "Unable to set jitoption") + jit_in_product_location = False if coreclr_args.product_location.lower() in coreclr_args.jit_path.lower(): jit_in_product_location = True @@ -3431,6 +3618,22 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set diff_jit_dump.") + coreclr_args.verify(args, + "base_jit_option", + lambda unused: True, + "Unable to set base_jit_option.") + + coreclr_args.verify(args, + "diff_jit_option", + lambda unused: True, + "Unable to set diff_jit_option.") + + coreclr_args.verify(args, + "tag", + lambda unused: True, + "Unable to set tag.", + modify_arg=lambda arg: make_safe_filename(arg) if arg is not None else arg) + process_base_jit_path_arg(coreclr_args) jit_in_product_location = False @@ -3494,15 +3697,35 @@ def verify_replay_common_args(): modify_arg=lambda arg: os.environ["CLRJIT_AZ_KEY"] if arg is None and "CLRJIT_AZ_KEY" in os.environ else arg) coreclr_args.verify(args, - "jit_location", + "mch_files", lambda unused: True, - "Unable to set jit_location.") + "Unable to set mch_files") + + elif coreclr_args.mode == "upload-private": + + verify_target_args() + verify_jit_ee_version_arg() coreclr_args.verify(args, "mch_files", lambda unused: True, "Unable to set mch_files") + coreclr_args.verify(args, + "private_store", + lambda unused: True, + "Unable to set private_store") + + if not os.path.isdir(coreclr_args.private_store): + print("Error: private store directory '" + coreclr_args.private_store + "' not found.") + sys.exit(1) + + # Safety measure: don't allow CLRJIT_AZ_KEY to be set if we are uploading to a private store. + # Note that this should be safe anyway, since we're publishing something private, not public. + if "CLRJIT_AZ_KEY" in os.environ: + print("Error: environment variable CLRJIT_AZ_KEY is set, but command is `upload-private`, not `upload`. That is not allowed.") + sys.exit(1) + elif coreclr_args.mode == "download": verify_target_args() @@ -3523,6 +3746,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set mch_files") + coreclr_args.verify(args, + "private_store", + lambda item: True, + "Specify private_store or set environment variable SUPERPMI_PRIVATE_STORE to use a private store.", + modify_arg=lambda arg: os.environ["SUPERPMI_PRIVATE_STORE"].split(";") if arg is None and "SUPERPMI_PRIVATE_STORE" in os.environ else arg) + elif coreclr_args.mode == "list-collections": verify_target_args() @@ -3551,6 +3780,12 @@ def verify_replay_common_args(): lambda unused: True, "Unable to set pattern") + if coreclr_args.mode == "replay" or coreclr_args.mode == "asmdiffs" or coreclr_args.mode == "download": + if hasattr(coreclr_args, "private_store") and coreclr_args.private_store is not None: + logging.info("Using private stores:") + for path in coreclr_args.private_store: + logging.info(" %s", path) + return coreclr_args ################################################################################ @@ -3603,8 +3838,8 @@ def main(args): elif coreclr_args.mode == "replay": # Start a new SuperPMI Replay - process_mch_files_arg(coreclr_args) - mch_files = get_mch_files_for_replay(coreclr_args) + local_mch_paths = process_mch_files_arg(coreclr_args) + mch_files = get_mch_files_for_replay(local_mch_paths, coreclr_args.filter) if mch_files is None: return 1 @@ -3634,8 +3869,8 @@ def main(args): elif coreclr_args.mode == "asmdiffs": # Start a new SuperPMI Replay with AsmDiffs - process_mch_files_arg(coreclr_args) - mch_files = get_mch_files_for_replay(coreclr_args) + local_mch_paths = process_mch_files_arg(coreclr_args) + mch_files = get_mch_files_for_replay(local_mch_paths, coreclr_args.filter) if mch_files is None: return 1 @@ -3680,6 +3915,22 @@ def main(args): logging.debug("Finish time: %s", end_time.strftime("%H:%M:%S")) logging.debug("Elapsed time: %s", elapsed_time) + elif coreclr_args.mode == "upload-private": + + begin_time = datetime.datetime.now() + + logging.info("SuperPMI upload-private") + logging.debug("------------------------------------------------------------") + logging.debug("Start time: %s", begin_time.strftime("%H:%M:%S")) + + upload_private_mch(coreclr_args) + + end_time = datetime.datetime.now() + elapsed_time = end_time - begin_time + + logging.debug("Finish time: %s", end_time.strftime("%H:%M:%S")) + logging.debug("Elapsed time: %s", elapsed_time) + elif coreclr_args.mode == "download": begin_time = datetime.datetime.now() diff --git a/src/coreclr/setup_vs_tools.cmd b/src/coreclr/setup_vs_tools.cmd deleted file mode 100644 index 055a5dd2fb4f6..0000000000000 --- a/src/coreclr/setup_vs_tools.cmd +++ /dev/null @@ -1,42 +0,0 @@ -@if not defined _echo @echo off - -REM This script is responsible for setting up the vs2017 or vs2019 env -REM All passed arguments are ignored -REM Script will return with 0 if pass, 1 if there is a failure to find either -REM vs2017 or vs2019 - -:: Default to highest Visual Studio version available -:: -:: For VS2017 and later, multiple instances can be installed on the same box SxS and VS1*0COMNTOOLS -:: is no longer set as a global environment variable and is instead only set if the user -:: has launched the Visual Studio Developer Command Prompt. -:: -:: Following this logic, we will default to the Visual Studio toolset assocated with the active -:: Developer Command Prompt. Otherwise, we will query VSWhere to locate the later version of -:: Visual Studio available on the machine. Finally, we will fail the script if not supported -:: instance can be found. - -if defined VisualStudioVersion ( - if not defined __VSVersion echo %__MsgPrefix%Detected Visual Studio %VisualStudioVersion% developer command ^prompt environment - goto skip_setup -) - -echo %__MsgPrefix%Searching ^for Visual Studio installation -set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -if exist %_VSWHERE% ( - for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools - goto call_vs -) - -:call_vs -if not exist "%_VSCOMNTOOLS%" ( - echo %__MsgPrefix%Error: Visual Studio 2019 required. - echo Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md for build instructions. - exit /b 1 -) -echo %__MsgPrefix%"%_VSCOMNTOOLS%\VsDevCmd.bat" -call "%_VSCOMNTOOLS%\VsDevCmd.bat" - -:skip_setup - -exit /b 0 diff --git a/src/coreclr/tools/CMakeLists.txt b/src/coreclr/tools/CMakeLists.txt index 350a100df159e..e2e3083136be4 100644 --- a/src/coreclr/tools/CMakeLists.txt +++ b/src/coreclr/tools/CMakeLists.txt @@ -3,7 +3,5 @@ if (CLR_CMAKE_TARGET_WIN32 AND NOT CLR_CMAKE_CROSS_ARCH) add_subdirectory(GenClrDebugResource) add_subdirectory(InjectResource) - if (CLR_CROSS_COMPONENTS_BUILD) - install(EXPORT dactabletools DESTINATION dactabletools) - endif() + install(EXPORT dactabletools DESTINATION dactabletools COMPONENT crosscomponents) endif() diff --git a/src/coreclr/tools/Common/Compiler/SingleMethodRootProvider.cs b/src/coreclr/tools/Common/Compiler/SingleMethodRootProvider.cs index 33a469347089c..afd542d7e1fe2 100644 --- a/src/coreclr/tools/Common/Compiler/SingleMethodRootProvider.cs +++ b/src/coreclr/tools/Common/Compiler/SingleMethodRootProvider.cs @@ -19,7 +19,11 @@ public SingleMethodRootProvider(MethodDesc method) public void AddCompilationRoots(IRootingServiceProvider rootProvider) { - rootProvider.AddCompilationRoot(_method, "Single method root"); + rootProvider.AddCompilationRoot(_method, +#if READYTORUN + rootMinimalDependencies: false, +#endif + reason: "Single method root"); } } } diff --git a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs index e75f982abcdc2..c9418961afd9b 100644 --- a/src/coreclr/tools/Common/Compiler/TypeExtensions.cs +++ b/src/coreclr/tools/Common/Compiler/TypeExtensions.cs @@ -32,7 +32,7 @@ public static DefType GetClosestDefType(this TypeDesc type) { if (!type.IsArrayTypeWithoutGenericInterfaces()) { - MetadataType arrayShadowType = type.Context.SystemModule.GetType("System", "Array`1", throwIfNotFound: false); + MetadataType arrayShadowType = type.Context.SystemModule.GetType("System", "Array`1", NotFoundBehavior.ReturnNull); if (arrayShadowType != null) { return arrayShadowType.MakeInstantiatedType(((ArrayType)type).ElementType); diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 6f30285a53e5a..feacb0ba06d5d 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants public const uint Signature = 0x00525452; // 'RTR' public const ushort CurrentMajorVersion = 5; - public const ushort CurrentMinorVersion = 2; + public const ushort CurrentMinorVersion = 3; } #pragma warning disable 0169 @@ -66,6 +66,7 @@ public enum ReadyToRunSectionType ComponentAssemblies = 115, // Added in 4.1 OwnerCompositeExecutable = 116, // Added in 4.1 PgoInstrumentationData = 117, // Added in 5.2 + ManifestAssemblyMvids = 118, // Added in 5.3 // // CoreRT ReadyToRun sections diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index fcaaef55030db..d99c51d236950 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -21,6 +21,7 @@ using Internal.TypeSystem.Ecma; using Internal.TypeSystem.Interop; using Internal.CorConstants; +using Internal.Pgo; using ILCompiler; using ILCompiler.DependencyAnalysis; @@ -61,6 +62,15 @@ private enum ImageFileMachine private ExceptionDispatchInfo _lastException; + private struct PgoInstrumentationResults + { + public PgoInstrumentationSchema* pSchema; + public uint countSchemaItems; + public byte* pInstrumentationData; + public HRESULT hr; + } + Dictionary _pgoResults = new Dictionary(); + [DllImport(JitLibrary)] private extern static IntPtr jitStartup(IntPtr host); @@ -409,8 +419,9 @@ private void CompileMethodCleanup() _inlinedMethods = new ArrayBuilder(); _actualInstructionSetSupported = default(InstructionSetFlags); _actualInstructionSetUnsupported = default(InstructionSetFlags); - _pgoResults.Clear(); #endif + + _pgoResults.Clear(); } private Dictionary _objectToHandle = new Dictionary(); @@ -553,6 +564,7 @@ private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(Cust } bool found = false; + bool memberFunctionVariant = false; foreach (CustomAttributeTypedArgument type in callConvArray) { if (!(type.Value is DefType defType)) @@ -561,6 +573,12 @@ private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(Cust if (defType.Namespace != "System.Runtime.CompilerServices") continue; + if (defType.Name == "CallConvMemberFunction") + { + memberFunctionVariant = true; + continue; + } + CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType); if (callConvLocal.HasValue) @@ -573,17 +591,26 @@ private CorInfoCallConvExtension GetUnmanagedCallingConventionFromAttribute(Cust found = true; } } + + if (memberFunctionVariant) + { + callConv = GetMemberFunctionCallingConventionVariant(callConv); + } + return callConv; } private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signature, out CorInfoCallConvExtension callConv, out bool suppressGCTransition) { suppressGCTransition = false; + // Default to managed since in the modopt case we need to differentiate explicitly using a calling convention that matches the default + // and not specifying a calling convention at all and using the implicit default case in P/Invoke stub inlining. callConv = CorInfoCallConvExtension.Managed; if (!signature.HasEmbeddedSignatureData || signature.GetEmbeddedSignatureData() == null) return false; bool found = false; + bool memberFunctionVariant = false; foreach (EmbeddedSignatureData data in signature.GetEmbeddedSignatureData()) { if (data.kind != EmbeddedSignatureDataKind.OptionalCustomModifier) @@ -605,6 +632,11 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur suppressGCTransition = true; continue; } + else if (defType.Name == "CallConvMemberFunction") + { + memberFunctionVariant = true; + continue; + } CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType); @@ -619,6 +651,11 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur } } + if (memberFunctionVariant) + { + callConv = GetMemberFunctionCallingConventionVariant(found ? callConv : (CorInfoCallConvExtension)PlatformDefaultUnmanagedCallingConvention()); + } + return found; } @@ -633,6 +670,15 @@ private bool TryGetUnmanagedCallingConventionFromModOpt(MethodSignature signatur _ => null }; + private static CorInfoCallConvExtension GetMemberFunctionCallingConventionVariant(CorInfoCallConvExtension baseCallConv) => + baseCallConv switch + { + CorInfoCallConvExtension.C => CorInfoCallConvExtension.CMemberFunction, + CorInfoCallConvExtension.Stdcall => CorInfoCallConvExtension.StdcallMemberFunction, + CorInfoCallConvExtension.Fastcall => CorInfoCallConvExtension.FastcallMemberFunction, + var c => c + }; + private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig) { sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask); @@ -3318,11 +3364,107 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) } if (this.MethodBeingCompiled.IsNoOptimization) + { flags.Set(CorJitFlag.CORJIT_FLAG_MIN_OPT); + } + + if (this.MethodBeingCompiled.Context.Target.Abi == TargetAbi.CoreRTArmel) + { + flags.Set(CorJitFlag.CORJIT_FLAG_SOFTFP_ABI); + } return (uint)sizeof(CORJIT_FLAGS); } + private PgoSchemaElem[] getPgoInstrumentationResults(MethodDesc method) + { + return _compilation.ProfileData[method]?.SchemaData; + } + + private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref PgoInstrumentationSchema* pSchema, ref uint countSchemaItems, byte** pInstrumentationData) + { + MethodDesc methodDesc = HandleToObject(ftnHnd); + + if (!_pgoResults.TryGetValue(methodDesc, out PgoInstrumentationResults pgoResults)) + { + var pgoResultsSchemas = getPgoInstrumentationResults(methodDesc); + if (pgoResultsSchemas == null) + { + pgoResults.hr = HRESULT.E_NOTIMPL; + } + else + { + PgoInstrumentationSchema[] nativeSchemas = new PgoInstrumentationSchema[pgoResultsSchemas.Length]; + MemoryStream msInstrumentationData = new MemoryStream(); + BinaryWriter bwInstrumentationData = new BinaryWriter(msInstrumentationData); + for (int i = 0; i < nativeSchemas.Length; i++) + { + if ((bwInstrumentationData.BaseStream.Position % 8) == 4) + { + bwInstrumentationData.Write(0); + } + + Debug.Assert((bwInstrumentationData.BaseStream.Position % 8) == 0); + nativeSchemas[i].Offset = new IntPtr(checked((int)bwInstrumentationData.BaseStream.Position)); + nativeSchemas[i].ILOffset = pgoResultsSchemas[i].ILOffset; + nativeSchemas[i].Count = pgoResultsSchemas[i].Count; + nativeSchemas[i].Other = pgoResultsSchemas[i].Other; + nativeSchemas[i].InstrumentationKind = (PgoInstrumentationKind)pgoResultsSchemas[i].InstrumentationKind; + + if (pgoResultsSchemas[i].DataObject == null) + { + bwInstrumentationData.Write(pgoResultsSchemas[i].DataLong); + } + else + { + object dataObject = pgoResultsSchemas[i].DataObject; + if (dataObject is int[] intArray) + { + foreach (int intVal in intArray) + bwInstrumentationData.Write(intVal); + } + else if (dataObject is long[] longArray) + { + foreach (long longVal in longArray) + bwInstrumentationData.Write(longVal); + } + else if (dataObject is TypeSystemEntityOrUnknown[] typeArray) + { + foreach (TypeSystemEntityOrUnknown typeVal in typeArray) + { + IntPtr ptrVal = IntPtr.Zero; + if (typeVal.AsType != null) + ptrVal = (IntPtr)ObjectToHandle(typeVal.AsType); + else + { + // The "Unknown types are the values from 1-33 + ptrVal = new IntPtr((typeVal.AsUnknown % 32) + 1); + } + + if (IntPtr.Size == 4) + bwInstrumentationData.Write((int)ptrVal); + else + bwInstrumentationData.Write((long)ptrVal); + } + } + } + } + + bwInstrumentationData.Flush(); + pgoResults.pInstrumentationData = (byte*)GetPin(msInstrumentationData.ToArray()); + pgoResults.countSchemaItems = (uint)nativeSchemas.Length; + pgoResults.pSchema = (PgoInstrumentationSchema*)GetPin(nativeSchemas); + pgoResults.hr = HRESULT.S_OK; + } + + _pgoResults.Add(methodDesc, pgoResults); + } + + pSchema = pgoResults.pSchema; + countSchemaItems = pgoResults.countSchemaItems; + *pInstrumentationData = pgoResults.pInstrumentationData; + return pgoResults.hr; + } #if READYTORUN InstructionSetFlags _actualInstructionSetSupported; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 2b406909f2732..e787a52dc3fc1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -357,8 +357,11 @@ enum CorInfoCallConvExtension C, Stdcall, Thiscall, - Fastcall + Fastcall, // New calling conventions supported with the extensible calling convention encoding go here. + CMemberFunction, + StdcallMemberFunction, + FastcallMemberFunction } public enum CORINFO_CALLINFO_FLAGS @@ -1266,6 +1269,9 @@ public enum CorInfoTokenKind // token comes from CEE_LDVIRTFTN CORINFO_TOKENKIND_Ldvirtftn = 0x400 | CORINFO_TOKENKIND_Method, + + // token comes from devirtualizing a method + CORINFO_TOKENKIND_DevirtualizedMethod = 0x800 | CORINFO_TOKENKIND_Method, }; // These are error codes returned by CompileMethod @@ -1334,6 +1340,7 @@ public enum CorJitFlag : uint CORJIT_FLAG_TIER1 = 40, // This is the final tier (for now) for tiered compilation which should generate high quality code CORJIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method + CORJIT_FLAG_SOFTFP_ABI = 43, // On ARM should enable armel calling convention } public struct CORJIT_FLAGS diff --git a/src/coreclr/tools/Common/TypeSystem/Common/CastingHelper.cs b/src/coreclr/tools/Common/TypeSystem/Common/CastingHelper.cs index 7b21c989ec76c..1b067af21b476 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/CastingHelper.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/CastingHelper.cs @@ -271,17 +271,9 @@ private static bool CanCastParamTo(this ParameterizedType thisType, TypeDesc par else if (fromParamUnderlyingType.IsPrimitive) { TypeDesc toParamUnderlyingType = paramType.UnderlyingType; - if (toParamUnderlyingType.IsPrimitive) + if (GetNormalizedIntegralArrayElementType(fromParamUnderlyingType) == GetNormalizedIntegralArrayElementType(toParamUnderlyingType)) { - if (toParamUnderlyingType == fromParamUnderlyingType) - { - return true; - } - - if (ArePrimitveTypesEquivalentSize(fromParamUnderlyingType, toParamUnderlyingType)) - { - return true; - } + return true; } } @@ -289,59 +281,48 @@ private static bool CanCastParamTo(this ParameterizedType thisType, TypeDesc par return false; } - // Returns true of the two types are equivalent primitive types. Used by array casts. - private static bool ArePrimitveTypesEquivalentSize(TypeDesc type1, TypeDesc type2) + private static TypeFlags GetNormalizedIntegralArrayElementType(TypeDesc type) { - Debug.Assert(type1.IsPrimitive && type2.IsPrimitive); + Debug.Assert(!type.IsEnum); // Primitive types such as E_T_I4 and E_T_U4 are interchangeable // Enums with interchangeable underlying types are interchangable // BOOL is NOT interchangeable with I1/U1, neither CHAR -- with I2/U2 // Float and double are not interchangable here. - int sourcePrimitiveTypeEquivalenceSize = type1.GetIntegralTypeMatchSize(); - - // Quick check to see if the first type can be matched. - if (sourcePrimitiveTypeEquivalenceSize == 0) + TypeFlags elementType = type.Category; + switch (elementType) { - return false; + case TypeFlags.Byte: + case TypeFlags.UInt16: + case TypeFlags.UInt32: + case TypeFlags.UInt64: + case TypeFlags.UIntPtr: + return elementType - 1; } - int targetPrimitiveTypeEquivalenceSize = type2.GetIntegralTypeMatchSize(); - - return sourcePrimitiveTypeEquivalenceSize == targetPrimitiveTypeEquivalenceSize; + return elementType; } - private static int GetIntegralTypeMatchSize(this TypeDesc type) - { - Debug.Assert(type.IsPrimitive); - switch (type.Category) + public static bool IsArrayElementTypeCastableBySize(TypeDesc elementType) + { + switch (elementType.UnderlyingType.Category) { - case TypeFlags.SByte: case TypeFlags.Byte: - return 1; + case TypeFlags.SByte: case TypeFlags.UInt16: case TypeFlags.Int16: - return 2; - case TypeFlags.Int32: case TypeFlags.UInt32: - return 4; - case TypeFlags.Int64: + case TypeFlags.Int32: case TypeFlags.UInt64: - return 8; - case TypeFlags.IntPtr: + case TypeFlags.Int64: case TypeFlags.UIntPtr: - return type.Context.Target.PointerSize; - default: - return 0; + case TypeFlags.IntPtr: + return true; } - } - public static bool IsArrayElementTypeCastableBySize(TypeDesc elementType) - { - TypeDesc underlyingType = elementType.UnderlyingType; - return underlyingType.IsPrimitive && GetIntegralTypeMatchSize(underlyingType) != 0; + return false; } private static bool CanCastToClassOrInterface(this TypeDesc thisType, TypeDesc otherType, StackOverflowProtect protect) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs b/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs index a92cca2669999..3e73dcfd82117 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/DefType.FieldLayout.cs @@ -12,7 +12,7 @@ public abstract partial class DefType : TypeDesc /// /// Bit flags for layout /// - private class FieldLayoutFlags + private static class FieldLayoutFlags { /// /// True if ContainsGCPointers has been computed diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs b/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs index d9d3de41634de..dd38a715c5fe2 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs @@ -40,5 +40,6 @@ public enum ExceptionStringID // BadImageFormatException BadImageFormatGeneric, + BadImageFormatSpecific, } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 2e23688d1a062..f7372ba0d6edd 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -308,9 +308,11 @@ protected static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(Metadata { // Instance slice size is the total size of instance not including the base type. // It is calculated as the field whose offset and size add to the greatest value. + LayoutInt offsetBias = !type.IsValueType ? new LayoutInt(type.Context.Target.PointerSize) : LayoutInt.Zero; LayoutInt cumulativeInstanceFieldPos = type.HasBaseType && !type.IsValueType ? type.BaseType.InstanceByteCount : LayoutInt.Zero; LayoutInt instanceSize = cumulativeInstanceFieldPos; + cumulativeInstanceFieldPos -= offsetBias; var layoutMetadata = type.GetClassLayout(); @@ -333,7 +335,7 @@ protected static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(Metadata if (fieldAndOffset.Offset == FieldAndOffset.InvalidOffset) ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadBadFormat, type); - LayoutInt computedOffset = fieldAndOffset.Offset + cumulativeInstanceFieldPos; + LayoutInt computedOffset = fieldAndOffset.Offset + cumulativeInstanceFieldPos + offsetBias; // GC pointers MUST be aligned. // We treat byref-like structs as GC pointers too. @@ -379,29 +381,20 @@ protected static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(Metadata return computedLayout; } - private static LayoutInt AlignUpInstanceFieldOffset(TypeDesc typeWithField, LayoutInt cumulativeInstanceFieldPos, LayoutInt alignment, TargetDetails target, bool armAlignFromStartOfFields = false) + private static LayoutInt AlignUpInstanceFieldOffset(TypeDesc typeWithField, LayoutInt cumulativeInstanceFieldPos, LayoutInt alignment, TargetDetails target) { - if (!typeWithField.IsValueType && (target.Architecture == TargetArchitecture.X86 || (armAlignFromStartOfFields && target.Architecture == TargetArchitecture.ARM)) && cumulativeInstanceFieldPos != new LayoutInt(0)) - { - // Alignment of fields is relative to the start of the field list, not the start of the object - // - // The code in the VM is written as if this is the rule for all architectures, but for ARM 32bit platforms - // there is an additional adjustment via dwOffsetBias that aligns fields based on the start of the object - cumulativeInstanceFieldPos = cumulativeInstanceFieldPos - new LayoutInt(target.PointerSize); - return LayoutInt.AlignUp(cumulativeInstanceFieldPos, alignment, target) + new LayoutInt(target.PointerSize); - } - else - { - return LayoutInt.AlignUp(cumulativeInstanceFieldPos, alignment, target); - } + return LayoutInt.AlignUp(cumulativeInstanceFieldPos, alignment, target); } - protected static ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType type, int numInstanceFields) + protected ComputedInstanceFieldLayout ComputeSequentialFieldLayout(MetadataType type, int numInstanceFields) { var offsets = new FieldAndOffset[numInstanceFields]; // For types inheriting from another type, field offsets continue on from where they left off - LayoutInt cumulativeInstanceFieldPos = ComputeBytesUsedInParentType(type); + // For reference types, we calculate field alignment as if the address after the method table pointer + // has offset 0 (on 32-bit platforms, this location is guaranteed to be 8-aligned). + LayoutInt offsetBias = !type.IsValueType ? new LayoutInt(type.Context.Target.PointerSize) : LayoutInt.Zero; + LayoutInt cumulativeInstanceFieldPos = CalculateFieldBaseOffset(type, requiresAlign8: false, requiresAlignedBase: false) - offsetBias; var layoutMetadata = type.GetClassLayout(); @@ -421,18 +414,15 @@ protected static ComputedInstanceFieldLayout ComputeSequentialFieldLayout(Metada largestAlignmentRequirement = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequirement); - cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, type.Context.Target, - armAlignFromStartOfFields: true // In what appears to have been a bug in the design of the arm32 type layout code - // this portion of the layout algorithm does not layout from the start of the object - ); - offsets[fieldOrdinal] = new FieldAndOffset(field, cumulativeInstanceFieldPos); + cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, fieldSizeAndAlignment.Alignment, type.Context.Target); + offsets[fieldOrdinal] = new FieldAndOffset(field, cumulativeInstanceFieldPos + offsetBias); cumulativeInstanceFieldPos = checked(cumulativeInstanceFieldPos + fieldSizeAndAlignment.Size); fieldOrdinal++; } SizeAndAlignment instanceByteSizeAndAlignment; - var instanceSizeAndAlignment = ComputeInstanceSize(type, cumulativeInstanceFieldPos, largestAlignmentRequirement, layoutMetadata.Size, out instanceByteSizeAndAlignment); + var instanceSizeAndAlignment = ComputeInstanceSize(type, cumulativeInstanceFieldPos + offsetBias, largestAlignmentRequirement, layoutMetadata.Size, out instanceByteSizeAndAlignment); ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment; @@ -445,14 +435,12 @@ protected static ComputedInstanceFieldLayout ComputeSequentialFieldLayout(Metada return computedLayout; } - protected virtual void AlignBaseOffsetIfNecessary(MetadataType type, ref LayoutInt baseOffset, bool requiresAlign8) + protected virtual void AlignBaseOffsetIfNecessary(MetadataType type, ref LayoutInt baseOffset, bool requiresAlign8, bool requiresAlignedBase) { } protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, int numInstanceFields) { - // For types inheriting from another type, field offsets continue on from where they left off - LayoutInt cumulativeInstanceFieldPos = ComputeBytesUsedInParentType(type); TypeSystemContext context = type.Context; var layoutMetadata = type.GetClassLayout(); @@ -463,7 +451,6 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, var offsets = new FieldAndOffset[numInstanceFields]; int fieldOrdinal = 0; - // Iterate over the instance fields and keep track of the number of fields of each category // For the non-GC Pointer fields, we will keep track of the number of fields by log2(size) int maxLog2Size = CalculateLog2(TargetDetails.MaximumPrimitiveSize); @@ -552,18 +539,15 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, largestAlignmentRequired = context.Target.GetObjectAlignment(largestAlignmentRequired); bool requiresAlign8 = !largestAlignmentRequired.IsIndeterminate && largestAlignmentRequired.AsInt > 4; - if (!type.IsValueType) + // For types inheriting from another type, field offsets continue on from where they left off + // Base alignment is not always required, it's only applied when there's a version bubble boundary + // between base type and the current type. + LayoutInt cumulativeInstanceFieldPos = CalculateFieldBaseOffset(type, requiresAlign8, requiresAlignedBase: false); + LayoutInt offsetBias = LayoutInt.Zero; + if (!type.IsValueType && cumulativeInstanceFieldPos != LayoutInt.Zero && type.Context.Target.Architecture == TargetArchitecture.X86) { - DefType baseType = type.BaseType; - if (baseType != null && !baseType.IsObject) - { - if (!requiresAlign8 && baseType.RequiresAlign8()) - { - requiresAlign8 = true; - } - - AlignBaseOffsetIfNecessary(type, ref cumulativeInstanceFieldPos, requiresAlign8); - } + offsetBias = new LayoutInt(type.Context.Target.PointerSize); + cumulativeInstanceFieldPos -= offsetBias; } // We've finished placing the fields into their appropriate arrays @@ -638,7 +622,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, // Place the field j = instanceNonGCPointerFieldsCount[i]; FieldDesc field = instanceNonGCPointerFieldsArr[i][j]; - PlaceInstanceField(field, packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal); + PlaceInstanceField(field, packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias); instanceNonGCPointerFieldsCount[i]++; } @@ -655,14 +639,14 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, { for (int j = 0; j < instanceGCPointerFieldsArr.Length; j++) { - PlaceInstanceField(instanceGCPointerFieldsArr[j], packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal); + PlaceInstanceField(instanceGCPointerFieldsArr[j], packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias); } } // The start index will be the index that may have been increased in the previous optimization for (int j = instanceNonGCPointerFieldsCount[i]; j < instanceNonGCPointerFieldsArr[i].Length; j++) { - PlaceInstanceField(instanceNonGCPointerFieldsArr[i][j], packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal); + PlaceInstanceField(instanceNonGCPointerFieldsArr[i][j], packingSize, offsets, ref cumulativeInstanceFieldPos, ref fieldOrdinal, offsetBias); } } @@ -685,7 +669,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, LayoutInt AlignmentRequired = LayoutInt.Max(fieldSizeAndAlignment.Alignment, context.Target.LayoutPointerSize); cumulativeInstanceFieldPos = AlignUpInstanceFieldOffset(type, cumulativeInstanceFieldPos, AlignmentRequired, context.Target); } - offsets[fieldOrdinal] = new FieldAndOffset(instanceValueClassFieldsArr[i], cumulativeInstanceFieldPos); + offsets[fieldOrdinal] = new FieldAndOffset(instanceValueClassFieldsArr[i], cumulativeInstanceFieldPos + offsetBias); // If the field has an indeterminate size, align the cumulative field offset to the indeterminate value // Otherwise, align the cumulative field offset to the aligned-instance field size @@ -720,7 +704,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, } SizeAndAlignment instanceByteSizeAndAlignment; - var instanceSizeAndAlignment = ComputeInstanceSize(type, cumulativeInstanceFieldPos, minAlign, 0/* specified field size unused */, out instanceByteSizeAndAlignment); + var instanceSizeAndAlignment = ComputeInstanceSize(type, cumulativeInstanceFieldPos + offsetBias, minAlign, 0/* specified field size unused */, out instanceByteSizeAndAlignment); ComputedInstanceFieldLayout computedLayout = new ComputedInstanceFieldLayout(); computedLayout.FieldAlignment = instanceSizeAndAlignment.Alignment; @@ -733,12 +717,12 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type, return computedLayout; } - private static void PlaceInstanceField(FieldDesc field, int packingSize, FieldAndOffset[] offsets, ref LayoutInt instanceFieldPos, ref int fieldOrdinal) + private static void PlaceInstanceField(FieldDesc field, int packingSize, FieldAndOffset[] offsets, ref LayoutInt instanceFieldPos, ref int fieldOrdinal, LayoutInt offsetBias) { var fieldSizeAndAlignment = ComputeFieldSizeAndAlignment(field.FieldType, packingSize, out bool _); instanceFieldPos = AlignUpInstanceFieldOffset(field.OwningType, instanceFieldPos, fieldSizeAndAlignment.Alignment, field.Context.Target); - offsets[fieldOrdinal] = new FieldAndOffset(field, instanceFieldPos); + offsets[fieldOrdinal] = new FieldAndOffset(field, instanceFieldPos + offsetBias); instanceFieldPos = checked(instanceFieldPos + fieldSizeAndAlignment.Size); fieldOrdinal++; @@ -775,13 +759,22 @@ private static bool IsByValueClass(TypeDesc type) return type.IsValueType && !type.IsPrimitive && !type.IsEnum; } - private static LayoutInt ComputeBytesUsedInParentType(DefType type) + public LayoutInt CalculateFieldBaseOffset(MetadataType type, bool requiresAlign8, bool requiresAlignedBase) { LayoutInt cumulativeInstanceFieldPos = LayoutInt.Zero; if (!type.IsValueType && type.HasBaseType) { cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned; + if (!type.BaseType.InstanceByteCountUnaligned.IsIndeterminate) + { + cumulativeInstanceFieldPos = type.BaseType.InstanceByteCountUnaligned; + if (type.BaseType.IsZeroSizedReferenceType && ((MetadataType)type.BaseType).HasLayout()) + { + cumulativeInstanceFieldPos += LayoutInt.One; + } + AlignBaseOffsetIfNecessary(type, ref cumulativeInstanceFieldPos, requiresAlign8, requiresAlignedBase); + } } return cumulativeInstanceFieldPos; @@ -915,6 +908,9 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic( if ((targetArch != TargetArchitecture.ARM) && (targetArch != TargetArchitecture.ARM64)) return NotHA; + if (type.Context.Target.Abi == TargetAbi.CoreRTArmel) + return NotHA; + MetadataType metadataType = (MetadataType)type; // No HAs with explicit layout. There may be cases where explicit layout may be still diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs index b578eb6ca6abc..d8ed92df961e6 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataTypeSystemContext.cs @@ -73,7 +73,7 @@ public virtual void SetSystemModule(ModuleDesc systemModule) { // Require System.Object to be present as a minimal sanity check. // The set of required well-known types is not strictly defined since different .NET profiles implement different subsets. - MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex], typeIndex == (int)WellKnownType.Object); + MetadataType type = systemModule.GetType("System", s_wellKnownTypeNames[typeIndex], typeIndex == (int)WellKnownType.Object ? NotFoundBehavior.Throw : NotFoundBehavior.ReturnNull); if (type != null) { type.SetWellKnownType((WellKnownType)(typeIndex + 1)); diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs index c1e143dfe192c..f2d45c1550b2d 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ModuleDesc.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; namespace Internal.TypeSystem @@ -28,8 +29,13 @@ public ModuleDesc(TypeSystemContext context, IAssemblyDesc assembly) /// /// Gets a type in this module with the specified name. + /// If notFoundBehavior == NotFoundBehavior.ReturnResolutionFailure + /// then ModuleDesc.GetTypeResolutionFailure will be set to the failure, and the function will return null /// - public abstract MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true); + public abstract MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw); + + [ThreadStatic] + public static ResolutionFailure GetTypeResolutionFailure; /// /// Gets the global <Module> type. diff --git a/src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs b/src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs new file mode 100644 index 0000000000000..1403bb8e0863e --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/NotFoundBehavior.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + public enum NotFoundBehavior + { + Throw, + ReturnNull, + ReturnResolutionFailure + } +} \ No newline at end of file diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx b/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx index b0efd67b4adc5..489fa6d1e3d9f 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx +++ b/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx @@ -180,4 +180,7 @@ The format of a DLL or executable being loaded is invalid + + The format of a DLL or executable being loaded is invalid with {0} + diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs b/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs new file mode 100644 index 0000000000000..882537e27f73f --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Common/ResolutionFailure.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Internal.TypeSystem +{ + public sealed class ResolutionFailure + { + private enum FailureType + { + TypeLoadException1, + TypeLoadException2, + TypeLoadException3, + MissingMethodException1, + MissingFieldException1, + MissingAssemblyException1, + } + + private ResolutionFailure() { } + + private FailureType _failureType; + private string _namespace; + private string _name; + private string _moduleName; + private ModuleDesc _module; + private TypeDesc _owningType; + private MethodSignature _methodSignature; + + + public static ResolutionFailure GetTypeLoadResolutionFailure(string nestedTypeName, ModuleDesc module) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.TypeLoadException1; + failure._name = nestedTypeName; + failure._module = module; + return failure; + } + + public static ResolutionFailure GetTypeLoadResolutionFailure(string @namespace, string name, ModuleDesc module) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.TypeLoadException2; + failure._namespace = @namespace; + failure._name = name; + failure._module = module; + return failure; + } + + public static ResolutionFailure GetTypeLoadResolutionFailure(string @namespace, string name, string moduleName) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.TypeLoadException3; + failure._namespace = @namespace; + failure._name = name; + failure._moduleName = moduleName; + return failure; + } + + public static ResolutionFailure GetMissingMethodFailure(TypeDesc owningType, string methodName, MethodSignature signature) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.MissingMethodException1; + failure._methodSignature = signature; + failure._name = methodName; + failure._owningType = owningType; + return failure; + } + + public static ResolutionFailure GetMissingFieldFailure(TypeDesc owningType, string fieldName) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.MissingMethodException1; + failure._name = fieldName; + failure._owningType = owningType; + return failure; + } + + public static ResolutionFailure GetAssemblyResolutionFailure(string simpleName) + { + ResolutionFailure failure = new ResolutionFailure(); + failure._failureType = FailureType.MissingAssemblyException1; + failure._name = simpleName; + return failure; + } + + public void Throw() + { + switch (_failureType) + { + case FailureType.TypeLoadException1: + ThrowHelper.ThrowTypeLoadException(_name, _module); + break; + case FailureType.TypeLoadException2: + ThrowHelper.ThrowTypeLoadException(_namespace, _name, _module); + break; + case FailureType.TypeLoadException3: + ThrowHelper.ThrowTypeLoadException(_namespace, _name, _moduleName); + break; + case FailureType.MissingMethodException1: + ThrowHelper.ThrowMissingMethodException(_owningType, _name, _methodSignature); + break; + case FailureType.MissingFieldException1: + ThrowHelper.ThrowMissingFieldException(_owningType, _name); + break; + case FailureType.MissingAssemblyException1: + ThrowHelper.ThrowFileNotFoundException(ExceptionStringID.FileLoadErrorGeneric, _name); + break; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs index 8bb69209cb731..21ea040857855 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs @@ -29,6 +29,10 @@ public enum TargetAbi /// CoreRT, /// + /// model for armel execution model + /// + CoreRTArmel, + /// /// Jit runtime ABI /// Jit, diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs index bfe36b4cd8299..cf644ad14d4c9 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.Common.cs @@ -22,6 +22,11 @@ public static void ThrowTypeLoadException(string @namespace, string name, Module ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, Format.Type(@namespace, name), Format.Module(module)); } + public static void ThrowTypeLoadException(string @namespace, string name, string moduleName) + { + ThrowTypeLoadException(ExceptionStringID.ClassLoadGeneral, Format.Type(@namespace, name), moduleName); + } + [System.Diagnostics.DebuggerHidden] public static void ThrowTypeLoadException(TypeDesc type) { diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs index 094ade10facaa..90c02f6ad3aab 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ThrowHelper.cs @@ -59,6 +59,12 @@ public static void ThrowBadImageFormatException() throw new TypeSystemException.BadImageFormatException(); } + [System.Diagnostics.DebuggerHidden] + public static void ThrowBadImageFormatException(string message) + { + throw new TypeSystemException.BadImageFormatException(message); + } + private static partial class Format { public static string OwningModule(TypeDesc type) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs index 17836ca309bd0..598f0f602748e 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeSystemException.cs @@ -155,6 +155,12 @@ internal BadImageFormatException() : base(ExceptionStringID.BadImageFormatGeneric) { } + + internal BadImageFormatException(string reason) + : base(ExceptionStringID.BadImageFormatSpecific, reason) + { + + } } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs index 1b901f5e623ed..d63e92da54766 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs @@ -276,7 +276,7 @@ private static MetadataType GetType(this ModuleDesc module, string fullName, boo namespaceName = fullName.Substring(0, split); typeName = fullName.Substring(split + 1); } - return module.GetType(namespaceName, typeName, throwIfNotFound); + return module.GetType(namespaceName, typeName, throwIfNotFound ? NotFoundBehavior.Throw : NotFoundBehavior.ReturnNull); } private static AssemblyName FindAssemblyIfNamePresent(string name) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs index 31a4c8def5c50..b6d5e4fd41f8b 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaField.cs @@ -98,7 +98,7 @@ private TypeDesc InitializeFieldType() var metadataReader = MetadataReader; BlobReader signatureReader = metadataReader.GetBlobReader(metadataReader.GetFieldDefinition(_handle).Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader, NotFoundBehavior.Throw); var fieldType = parser.ParseFieldSignature(); return (_fieldType = fieldType); } @@ -264,7 +264,7 @@ public override MarshalAsDescriptor GetMarshalAsDescriptor() if ((definition.Attributes & FieldAttributes.HasFieldMarshal) != 0) { BlobReader marshalAsReader = reader.GetBlobReader(definition.GetMarshallingDescriptor()); - EcmaSignatureParser parser = new EcmaSignatureParser(_type.EcmaModule, marshalAsReader); + EcmaSignatureParser parser = new EcmaSignatureParser(_type.EcmaModule, marshalAsReader, NotFoundBehavior.Throw); return parser.ParseMarshalAsDescriptor(); } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs index da5126529e483..182a87c56765f 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaMethod.cs @@ -82,7 +82,7 @@ private MethodSignature InitializeSignature() var metadataReader = MetadataReader; BlobReader signatureReader = metadataReader.GetBlobReader(metadataReader.GetMethodDefinition(_handle).Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, signatureReader, NotFoundBehavior.Throw); var signature = parser.ParseMethodSignature(); return (_signature = signature); } @@ -573,7 +573,7 @@ private MarshalAsDescriptor GetMarshalAsDescriptor(Parameter parameter) { MetadataReader metadataReader = MetadataReader; BlobReader marshalAsReader = metadataReader.GetBlobReader(parameter.GetMarshallingDescriptor()); - EcmaSignatureParser parser = new EcmaSignatureParser(Module, marshalAsReader); + EcmaSignatureParser parser = new EcmaSignatureParser(Module, marshalAsReader, NotFoundBehavior.Throw); MarshalAsDescriptor marshalAs = parser.ParseMarshalAsDescriptor(); Debug.Assert(marshalAs != null); return marshalAs; diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs index 389f0607399b6..737b266b9b6e6 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaModule.cs @@ -96,7 +96,7 @@ protected override IEntityHandleObject CreateValueFromKey(EntityHandle handle) { MethodDefinitionHandle methodDefinitionHandle = (MethodDefinitionHandle)handle; TypeDefinitionHandle typeDefinitionHandle = _module._metadataReader.GetMethodDefinition(methodDefinitionHandle).GetDeclaringType(); - EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle); + EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle, NotFoundBehavior.Throw); item = new EcmaMethod(type, methodDefinitionHandle); } break; @@ -105,7 +105,7 @@ protected override IEntityHandleObject CreateValueFromKey(EntityHandle handle) { FieldDefinitionHandle fieldDefinitionHandle = (FieldDefinitionHandle)handle; TypeDefinitionHandle typeDefinitionHandle = _module._metadataReader.GetFieldDefinition(fieldDefinitionHandle).GetDeclaringType(); - EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle); + EcmaType type = (EcmaType)_module.GetObject(typeDefinitionHandle, NotFoundBehavior.Throw); item = new EcmaField(type, fieldDefinitionHandle); } break; @@ -149,7 +149,9 @@ protected override IEntityHandleObject CreateValueFromKey(EntityHandle handle) break; default: - throw new BadImageFormatException("Unknown metadata token type: " + handle.Kind); + ThrowHelper.ThrowBadImageFormatException("unknown metadata token type: " + handle.Kind); + item = null; + break; } switch (handle.Kind) @@ -261,7 +263,8 @@ public MethodDesc EntryPoint } // Bad metadata - throw new BadImageFormatException(); + ThrowHelper.ThrowBadImageFormatException(); + return null; } } @@ -277,7 +280,7 @@ public bool IsPlatformNeutral } } - public sealed override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public sealed override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { var stringComparer = _metadataReader.StringComparer; @@ -300,12 +303,20 @@ public sealed override MetadataType GetType(string nameSpace, string name, bool { if (exportedType.IsForwarder) { - Object implementation = GetObject(exportedType.Implementation); + Object implementation = GetObject(exportedType.Implementation, notFoundBehavior); + + if (implementation == null) + return null; if (implementation is ModuleDesc) { return ((ModuleDesc)(implementation)).GetType(nameSpace, name); } + else if (implementation is ResolutionFailure failure) + { + ModuleDesc.GetTypeResolutionFailure = failure; + return null; + } // TODO throw new NotImplementedException(); @@ -315,42 +326,57 @@ public sealed override MetadataType GetType(string nameSpace, string name, bool } } - if (throwIfNotFound) - ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); + if (notFoundBehavior != NotFoundBehavior.ReturnNull) + { + var failure = ResolutionFailure.GetTypeLoadResolutionFailure(nameSpace, name, this); + if (notFoundBehavior == NotFoundBehavior.Throw) + failure.Throw(); + + ModuleDesc.GetTypeResolutionFailure = failure; + return null; + } return null; } public TypeDesc GetType(EntityHandle handle) { - TypeDesc type = GetObject(handle) as TypeDesc; + TypeDesc type = GetObject(handle, NotFoundBehavior.Throw) as TypeDesc; if (type == null) - throw new BadImageFormatException("Type expected"); + ThrowHelper.ThrowBadImageFormatException($"type expected for handle {handle.ToString()}"); return type; } public MethodDesc GetMethod(EntityHandle handle) { - MethodDesc method = GetObject(handle) as MethodDesc; + MethodDesc method = GetObject(handle, NotFoundBehavior.Throw) as MethodDesc; if (method == null) - throw new BadImageFormatException("Method expected"); + ThrowHelper.ThrowBadImageFormatException($"method expected for handle {handle.ToString()}"); return method; } public FieldDesc GetField(EntityHandle handle) { - FieldDesc field = GetObject(handle) as FieldDesc; + FieldDesc field = GetObject(handle, NotFoundBehavior.Throw) as FieldDesc; if (field == null) - throw new BadImageFormatException("Field expected"); + ThrowHelper.ThrowBadImageFormatException($"field expected for handle {handle.ToString()}"); return field; } - public Object GetObject(EntityHandle handle) + public Object GetObject(EntityHandle handle, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) { IEntityHandleObject obj = _resolvedTokens.GetOrCreateValue(handle); if (obj is EcmaObjectLookupWrapper) { - return ((EcmaObjectLookupWrapper)obj).Object; + object result = ((EcmaObjectLookupWrapper)obj).Object; + if ((result is ResolutionFailure failure) && (notFoundBehavior != NotFoundBehavior.ReturnResolutionFailure)) + { + if (notFoundBehavior == NotFoundBehavior.ReturnNull) + return null; + else + failure.Throw(); + } + return result; } else { @@ -362,12 +388,22 @@ private Object ResolveMethodSpecification(MethodSpecificationHandle handle) { MethodSpecification methodSpecification = _metadataReader.GetMethodSpecification(handle); - MethodDesc methodDef = GetMethod(methodSpecification.Method); + object resolvedMethod = GetObject(methodSpecification.Method, NotFoundBehavior.ReturnResolutionFailure); + if (resolvedMethod is ResolutionFailure) + return resolvedMethod; + + MethodDesc methodDef = resolvedMethod as MethodDesc; + if (methodDef == null) + ThrowHelper.ThrowBadImageFormatException($"method expected for handle {handle.ToString()}"); BlobReader signatureReader = _metadataReader.GetBlobReader(methodSpecification.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); TypeDesc[] instantiation = parser.ParseMethodSpecSignature(); + + if (instantiation == null) + return parser.ResolutionFailure; + return Context.GetInstantiatedMethod(methodDef, new Instantiation(instantiation)); } @@ -375,9 +411,11 @@ private Object ResolveStandaloneSignature(StandaloneSignatureHandle handle) { StandaloneSignature signature = _metadataReader.GetStandaloneSignature(handle); BlobReader signatureReader = _metadataReader.GetBlobReader(signature.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); MethodSignature methodSig = parser.ParseMethodSignature(); + if (methodSig == null) + return parser.ResolutionFailure; return methodSig; } @@ -386,23 +424,30 @@ private Object ResolveTypeSpecification(TypeSpecificationHandle handle) TypeSpecification typeSpecification = _metadataReader.GetTypeSpecification(handle); BlobReader signatureReader = _metadataReader.GetBlobReader(typeSpecification.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); - return parser.ParseType(); + TypeDesc parsedType = parser.ParseType(); + if (parsedType == null) + return parser.ResolutionFailure; + else + return parsedType; } private Object ResolveMemberReference(MemberReferenceHandle handle) { MemberReference memberReference = _metadataReader.GetMemberReference(handle); - Object parent = GetObject(memberReference.Parent); + Object parent = GetObject(memberReference.Parent, NotFoundBehavior.ReturnResolutionFailure); + + if (parent is ResolutionFailure) + return parent; TypeDesc parentTypeDesc = parent as TypeDesc; if (parentTypeDesc != null) { BlobReader signatureReader = _metadataReader.GetBlobReader(memberReference.Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(this, signatureReader, NotFoundBehavior.ReturnResolutionFailure); string name = _metadataReader.GetString(memberReference.Name); @@ -412,11 +457,13 @@ private Object ResolveMemberReference(MemberReferenceHandle handle) if (field != null) return field; - ThrowHelper.ThrowMissingFieldException(parentTypeDesc, name); + return ResolutionFailure.GetMissingFieldFailure(parentTypeDesc, name); } else { MethodSignature sig = parser.ParseMethodSignature(); + if (sig == null) + return parser.ResolutionFailure; TypeDesc typeDescToInspect = parentTypeDesc; Instantiation substitution = default(Instantiation); @@ -460,7 +507,7 @@ private Object ResolveMemberReference(MemberReferenceHandle handle) typeDescToInspect = baseType; } while (typeDescToInspect != null); - ThrowHelper.ThrowMissingMethodException(parentTypeDesc, name, sig); + return ResolutionFailure.GetMissingMethodFailure(parentTypeDesc, name, sig); } } else if (parent is MethodDesc) @@ -472,18 +519,26 @@ private Object ResolveMemberReference(MemberReferenceHandle handle) throw new NotImplementedException("MemberRef to a global function or variable."); } - throw new BadImageFormatException(); + ThrowHelper.ThrowBadImageFormatException(); + return null; } private Object ResolveTypeReference(TypeReferenceHandle handle) { TypeReference typeReference = _metadataReader.GetTypeReference(handle); - Object resolutionScope = GetObject(typeReference.ResolutionScope); + Object resolutionScope = GetObject(typeReference.ResolutionScope, NotFoundBehavior.ReturnResolutionFailure); + if (resolutionScope is ResolutionFailure) + { + return resolutionScope; + } if (resolutionScope is ModuleDesc) { - return ((ModuleDesc)(resolutionScope)).GetType(_metadataReader.GetString(typeReference.Namespace), _metadataReader.GetString(typeReference.Name)); + object result = ((ModuleDesc)(resolutionScope)).GetType(_metadataReader.GetString(typeReference.Namespace), _metadataReader.GetString(typeReference.Name), NotFoundBehavior.ReturnResolutionFailure); + if (result == null) + result = ModuleDesc.GetTypeResolutionFailure; + return result; } else if (resolutionScope is MetadataType) @@ -495,7 +550,7 @@ private Object ResolveTypeReference(TypeReferenceHandle handle) if (result != null) return result; - ThrowHelper.ThrowTypeLoadException(typeName, ((MetadataType)resolutionScope).Module); + return ResolutionFailure.GetTypeLoadResolutionFailure(typeName, ((MetadataType)resolutionScope).Module); } // TODO @@ -523,20 +578,24 @@ private Object ResolveAssemblyReference(AssemblyReferenceHandle handle) an.CultureName = _metadataReader.GetString(assemblyReference.Culture); an.ContentType = GetContentTypeFromAssemblyFlags(assemblyReference.Flags); - return _moduleResolver.ResolveAssembly(an); + var assembly = _moduleResolver.ResolveAssembly(an, throwIfNotFound: false); + if (assembly == null) + return ResolutionFailure.GetAssemblyResolutionFailure(an.Name); + else + return assembly; } private Object ResolveExportedType(ExportedTypeHandle handle) { ExportedType exportedType = _metadataReader.GetExportedType(handle); - var implementation = GetObject(exportedType.Implementation); + var implementation = GetObject(exportedType.Implementation, NotFoundBehavior.ReturnResolutionFailure); if (implementation is ModuleDesc) { var module = (ModuleDesc)implementation; string nameSpace = _metadataReader.GetString(exportedType.Namespace); string name = _metadataReader.GetString(exportedType.Name); - return module.GetType(nameSpace, name); + return module.GetType(nameSpace, name, NotFoundBehavior.ReturnResolutionFailure); } else if (implementation is MetadataType) @@ -545,12 +604,17 @@ private Object ResolveExportedType(ExportedTypeHandle handle) string name = _metadataReader.GetString(exportedType.Name); var nestedType = type.GetNestedType(name); if (nestedType == null) - ThrowHelper.ThrowTypeLoadException(name, this); + return ResolutionFailure.GetTypeLoadResolutionFailure(name, this); return nestedType; } + else if (implementation is ResolutionFailure) + { + return implementation; + } else { - throw new BadImageFormatException("Unknown metadata token type for exported type"); + ThrowHelper.ThrowBadImageFormatException(); + return null; } } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs index 9a39fc67c1266..cb3766f98bc6a 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureParser.cs @@ -14,40 +14,75 @@ namespace Internal.TypeSystem.Ecma public struct EcmaSignatureParser { private TypeSystemContext _tsc; - private Func _typeResolver; + private Func _typeResolver; + private NotFoundBehavior _notFoundBehavior; private EcmaModule _ecmaModule; private BlobReader _reader; + private ResolutionFailure _resolutionFailure; private Stack _indexStack; private List _embeddedSignatureDataList; - public EcmaSignatureParser(TypeSystemContext tsc, Func typeResolver, BlobReader reader) + public EcmaSignatureParser(TypeSystemContext tsc, Func typeResolver, BlobReader reader, NotFoundBehavior notFoundBehavior) { + _notFoundBehavior = notFoundBehavior; _ecmaModule = null; _tsc = tsc; _typeResolver = typeResolver; _reader = reader; _indexStack = null; _embeddedSignatureDataList = null; + _resolutionFailure = null; } - public EcmaSignatureParser(EcmaModule ecmaModule, BlobReader reader) + public EcmaSignatureParser(EcmaModule ecmaModule, BlobReader reader, NotFoundBehavior notFoundBehavior) { + _notFoundBehavior = notFoundBehavior; _ecmaModule = ecmaModule; _tsc = ecmaModule.Context; _typeResolver = null; _reader = reader; _indexStack = null; _embeddedSignatureDataList = null; + _resolutionFailure = null; } + void SetResolutionFailure(ResolutionFailure failure) + { + if (_resolutionFailure == null) + _resolutionFailure = failure; + } + + public ResolutionFailure ResolutionFailure => _resolutionFailure; + private TypeDesc ResolveHandle(EntityHandle handle) { + object resolvedValue; if (_ecmaModule != null) - return _ecmaModule.GetType(handle); + { + resolvedValue = _ecmaModule.GetObject(handle, _notFoundBehavior); + } else - return _typeResolver(handle); + { + resolvedValue = _typeResolver(handle, _notFoundBehavior); + } + + if (resolvedValue == null) + return null; + if (resolvedValue is ResolutionFailure failure) + { + SetResolutionFailure(failure); + return null; + } + if (resolvedValue is TypeDesc type) + { + return type; + } + else + { + throw new BadImageFormatException("Type expected"); + } } private TypeDesc GetWellKnownType(WellKnownType wellKnownType) @@ -57,7 +92,6 @@ private TypeDesc GetWellKnownType(WellKnownType wellKnownType) private TypeDesc ParseType(SignatureTypeCode typeCode) { - if (_indexStack != null) { int was = _indexStack.Pop(); @@ -114,7 +148,12 @@ private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) case SignatureTypeCode.TypeHandle: return ResolveHandle(_reader.ReadTypeHandle()); case SignatureTypeCode.SZArray: - return _tsc.GetArrayType(ParseType()); + { + var elementType = ParseType(); + if (elementType == null) + return null; + return _tsc.GetArrayType(elementType); + } case SignatureTypeCode.Array: { var elementType = ParseType(); @@ -128,12 +167,27 @@ private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) for (int j = 0; j < lowerBoundsCount; j++) _reader.ReadCompressedInteger(); - return _tsc.GetArrayType(elementType, rank); + if (elementType != null) + return _tsc.GetArrayType(elementType, rank); + else + return null; } case SignatureTypeCode.ByReference: - return ParseType().MakeByRefType(); + { + TypeDesc byRefedType = ParseType(); + if (byRefedType != null) + return byRefedType.MakeByRefType(); + else + return null; + } case SignatureTypeCode.Pointer: - return _tsc.GetPointerType(ParseType()); + { + TypeDesc pointedAtType = ParseType(); + if (pointedAtType != null) + return _tsc.GetPointerType(pointedAtType); + else + return null; + } case SignatureTypeCode.GenericTypeParameter: return _tsc.GetSignatureVariable(_reader.ReadCompressedInteger(), false); case SignatureTypeCode.GenericMethodParameter: @@ -141,19 +195,36 @@ private TypeDesc ParseTypeImpl(SignatureTypeCode typeCode) case SignatureTypeCode.GenericTypeInstance: { TypeDesc typeDef = ParseType(); - MetadataType metadataTypeDef = typeDef as MetadataType; - if (metadataTypeDef == null) - throw new BadImageFormatException(); + MetadataType metadataTypeDef = null; + + if (typeDef != null) + { + metadataTypeDef = typeDef as MetadataType; + if (metadataTypeDef == null) + throw new BadImageFormatException(); + } TypeDesc[] instance = new TypeDesc[_reader.ReadCompressedInteger()]; for (int i = 0; i < instance.Length; i++) + { instance[i] = ParseType(); - return _tsc.GetInstantiatedType(metadataTypeDef, new Instantiation(instance)); + if (instance[i] == null) + metadataTypeDef = null; + } + + if (metadataTypeDef != null) + return _tsc.GetInstantiatedType(metadataTypeDef, new Instantiation(instance)); + else + return null; } case SignatureTypeCode.TypedReference: return GetWellKnownType(WellKnownType.TypedReference); case SignatureTypeCode.FunctionPointer: - return _tsc.GetFunctionPointerType(ParseMethodSignatureInternal(skipEmbeddedSignatureData: true)); + MethodSignature sig = ParseMethodSignatureInternal(skipEmbeddedSignatureData: true); + if (sig != null) + return _tsc.GetFunctionPointerType(sig); + else + return null; default: throw new BadImageFormatException(); } @@ -320,12 +391,19 @@ private MethodSignature ParseMethodSignatureImpl(bool skipEmbeddedSignatureData) EmbeddedSignatureData[] embeddedSignatureDataArray = (_embeddedSignatureDataList == null || _embeddedSignatureDataList.Count == 0 || skipEmbeddedSignatureData) ? null : _embeddedSignatureDataList.ToArray(); - return new MethodSignature(flags, arity, returnType, parameters, embeddedSignatureDataArray); + if (_resolutionFailure == null) + return new MethodSignature(flags, arity, returnType, parameters, embeddedSignatureDataArray); + else + return null; } public PropertySignature ParsePropertySignature() { + // As PropertySignature is a struct, we cannot return null + if (_notFoundBehavior != NotFoundBehavior.Throw) + throw new ArgumentException(); + SignatureHeader header = _reader.ReadSignatureHeader(); if (header.Kind != SignatureKind.Property) throw new BadImageFormatException(); @@ -392,7 +470,10 @@ public LocalVariableDefinition[] ParseLocalsSignature() { locals = Array.Empty(); } - return locals; + if (_resolutionFailure == null) + return locals; + else + return null; } public TypeDesc[] ParseMethodSpecSignature() @@ -410,7 +491,10 @@ public TypeDesc[] ParseMethodSpecSignature() { arguments[i] = ParseType(); } - return arguments; + if (_resolutionFailure == null) + return arguments; + else + return null; } public MarshalAsDescriptor ParseMarshalAsDescriptor() diff --git a/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs index e0142800be95d..02e5207c90a8b 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs @@ -89,7 +89,7 @@ public override LocalVariableDefinition[] GetLocals() return Array.Empty(); BlobReader signatureReader = metadataReader.GetBlobReader(metadataReader.GetStandaloneSignature(localSignature).Signature); - EcmaSignatureParser parser = new EcmaSignatureParser(_module, signatureReader); + EcmaSignatureParser parser = new EcmaSignatureParser(_module, signatureReader, NotFoundBehavior.Throw); LocalVariableDefinition[] locals = parser.ParseLocalsSignature(); Interlocked.CompareExchange(ref _locals, locals, null); @@ -131,13 +131,13 @@ public override ILExceptionRegion[] GetExceptionRegions() return _ilExceptionRegions; } - public override object GetObject(int token) + public override object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) { // UserStrings cannot be wrapped in EntityHandle if ((token & 0xFF000000) == 0x70000000) return _module.GetUserString(MetadataTokens.UserStringHandle(token)); - return _module.GetObject(MetadataTokens.EntityHandle(token)); + return _module.GetObject(MetadataTokens.EntityHandle(token), notFoundBehavior); } } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs b/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs index 0723d40b363ff..3c6d4c1d0b223 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/HelperExtensions.cs @@ -22,7 +22,7 @@ public static MetadataType GetHelperType(this TypeSystemContext context, string public static MetadataType GetOptionalHelperType(this TypeSystemContext context, string name) { - MetadataType helperType = context.SystemModule.GetType(HelperTypesNamespace, name, throwIfNotFound: false); + MetadataType helperType = context.SystemModule.GetType(HelperTypesNamespace, name, NotFoundBehavior.ReturnNull); return helperType; } @@ -111,7 +111,7 @@ public static MetadataType GetKnownNestedType(this MetadataType type, string nam /// public static MetadataType GetKnownType(this ModuleDesc module, string @namespace, string name) { - MetadataType type = module.GetType(@namespace, name, false); + MetadataType type = module.GetType(@namespace, name, NotFoundBehavior.ReturnNull); if (type == null) { throw new InvalidOperationException( diff --git a/src/coreclr/tools/Common/TypeSystem/IL/ILOpcodeHelper.cs b/src/coreclr/tools/Common/TypeSystem/IL/ILOpcodeHelper.cs index 076876c53751f..99a3fbcc9b0dd 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/ILOpcodeHelper.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/ILOpcodeHelper.cs @@ -33,6 +33,23 @@ public static bool IsValid(this ILOpcode opcode) && s_opcodeSizes[(int)opcode] != Invalid; } + public static bool IsBranch(this ILOpcode opcode) + { + if (opcode >= ILOpcode.br && opcode <= ILOpcode.blt_un) + return true; + + if (opcode >= ILOpcode.br_s && opcode <= ILOpcode.blt_un_s) + return true; + + if (opcode == ILOpcode.leave_s) + return true; + + if (opcode == ILOpcode.leave) + return true; + + return false; + } + private static readonly byte[] s_opcodeSizes = new byte[] { 1, // nop = 0x00, diff --git a/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs index 7192e547f39a6..9def3ed499b8b 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/InstantiatedMethodIL.cs @@ -89,9 +89,9 @@ public override LocalVariableDefinition[] GetLocals() return (clone == null) ? locals : clone; } - public override Object GetObject(int token) + public override Object GetObject(int token, NotFoundBehavior notFoundBehavior) { - Object o = _methodIL.GetObject(token); + Object o = _methodIL.GetObject(token, notFoundBehavior); if (o is MethodDesc) { diff --git a/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs index 83fecc15119b1..cfe1f93998b08 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/MethodIL.cs @@ -86,7 +86,7 @@ public abstract partial class MethodIL /// (typically a , , , /// or ). /// - public abstract Object GetObject(int token); + public abstract Object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw); /// /// Gets a list of exception regions this method body defines. diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs index 67f79ad809dc8..aa203289c3c6e 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/ILEmitter.cs @@ -620,7 +620,7 @@ public override LocalVariableDefinition[] GetLocals() { return _locals; } - public override Object GetObject(int token) + public override Object GetObject(int token, NotFoundBehavior notFoundBehavior) { return _tokens[(token & 0xFFFFFF) - 1]; } diff --git a/src/coreclr/tools/GenClrDebugResource/CMakeLists.txt b/src/coreclr/tools/GenClrDebugResource/CMakeLists.txt index 0db21fa5d7ccd..2ea56e1ea871d 100644 --- a/src/coreclr/tools/GenClrDebugResource/CMakeLists.txt +++ b/src/coreclr/tools/GenClrDebugResource/CMakeLists.txt @@ -6,4 +6,4 @@ target_link_libraries(GenClrDebugResource ${STATIC_MT_VCRT_LIB} ) -install(TARGETS GenClrDebugResource EXPORT dactabletools DESTINATION dactabletools) +install(TARGETS GenClrDebugResource EXPORT dactabletools DESTINATION dactabletools COMPONENT crosscomponents) diff --git a/src/coreclr/tools/ILVerification/ILVerification.projitems b/src/coreclr/tools/ILVerification/ILVerification.projitems index c68e48bdc552c..35b992f6f023c 100644 --- a/src/coreclr/tools/ILVerification/ILVerification.projitems +++ b/src/coreclr/tools/ILVerification/ILVerification.projitems @@ -46,6 +46,12 @@ TypeSystem\Common\ModuleDesc.cs + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + TypeSystem\Common\TypeSystemEntity.cs diff --git a/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs b/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs index 3a08440c4fa75..019f669a73a06 100644 --- a/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs +++ b/src/coreclr/tools/ILVerification/SimpleArrayOfTRuntimeInterfacesAlgorithm.cs @@ -35,7 +35,7 @@ public SimpleArrayOfTRuntimeInterfacesAlgorithm(ModuleDesc systemModule) int count = 0; for (int i = 0; i < s_genericRuntimeInterfacesNames.Length; ++i) { - MetadataType runtimeInterface =_systemModule.GetType("System.Collections.Generic", s_genericRuntimeInterfacesNames[i], false); + MetadataType runtimeInterface =_systemModule.GetType("System.Collections.Generic", s_genericRuntimeInterfacesNames[i], NotFoundBehavior.ReturnNull); if (runtimeInterface != null) _genericRuntimeInterfaces[count++] = runtimeInterface; }; diff --git a/src/coreclr/tools/InjectResource/CMakeLists.txt b/src/coreclr/tools/InjectResource/CMakeLists.txt index 0e987aedd1eee..51f2b19349b82 100644 --- a/src/coreclr/tools/InjectResource/CMakeLists.txt +++ b/src/coreclr/tools/InjectResource/CMakeLists.txt @@ -9,4 +9,4 @@ target_link_libraries(InjectResource ${STATIC_MT_VCRT_LIB} ) -install(TARGETS InjectResource EXPORT dactabletools DESTINATION dactabletools) +install(TARGETS InjectResource EXPORT dactabletools DESTINATION dactabletools COMPONENT crosscomponents) diff --git a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzer.cs b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzer.cs index 351b0094ebf77..2d3b7b0724633 100644 --- a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzer.cs +++ b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzer.cs @@ -32,7 +32,7 @@ public sealed class DependencyAnalyzer : De private List> _markedNodes = new List>(); private ImmutableArray> _markedNodesFinal; private List> _rootNodes = new List>(); - private List> _deferredStaticDependencies = new List>(); + private Dictionary>> _deferredStaticDependencies = new Dictionary>>(); private List> _dynamicDependencyInterestingList = new List>(); private List _markedNodesWithDynamicDependencies = new List(); private bool _newDynamicDependenciesMayHaveAppeared = false; @@ -140,6 +140,7 @@ public override sealed ImmutableArray> public override sealed event Action>> ComputeDependencyRoutine; + public override sealed event Action ComputingDependencyPhaseChange; private IEnumerable> MarkedNodesEnumerable() { @@ -211,6 +212,8 @@ private void GetStaticDependenciesImpl(DependencyNodeCore } } + int _currentDependencyPhase = 0; + private void GetStaticDependencies(DependencyNodeCore node) { if (node.StaticDependenciesAreComputed) @@ -219,7 +222,13 @@ private void GetStaticDependencies(DependencyNodeCore nod } else { - _deferredStaticDependencies.Add(node); + int dependencyPhase = Math.Max(node.DependencyPhaseForDeferredStaticComputation, _currentDependencyPhase); + if (!_deferredStaticDependencies.TryGetValue(dependencyPhase, out var deferredPerPhaseDependencies)) + { + deferredPerPhaseDependencies = new List>(); + _deferredStaticDependencies.Add(dependencyPhase, deferredPerPhaseDependencies); + } + deferredPerPhaseDependencies.Add(node); } } @@ -299,15 +308,38 @@ public override void ComputeMarkedNodes() } // Compute all dependencies which were not ready during the ProcessMarkStack step - ComputeDependencies(_deferredStaticDependencies); - foreach (DependencyNodeCore node in _deferredStaticDependencies) + _deferredStaticDependencies.TryGetValue(_currentDependencyPhase, out var deferredDependenciesInCurrentPhase); + + if (deferredDependenciesInCurrentPhase != null) { - Debug.Assert(node.StaticDependenciesAreComputed); - GetStaticDependenciesImpl(node); + ComputeDependencies(deferredDependenciesInCurrentPhase); + foreach (DependencyNodeCore node in deferredDependenciesInCurrentPhase) + { + Debug.Assert(node.StaticDependenciesAreComputed); + GetStaticDependenciesImpl(node); + } + + deferredDependenciesInCurrentPhase.Clear(); } - _deferredStaticDependencies.Clear(); - } while (_markStack.Count != 0); + if (_markStack.Count == 0) + { + // Time to move to next deferred dependency phase. + + // 1. Remove old deferred dependency list(if it exists) + if (deferredDependenciesInCurrentPhase != null) + { + _deferredStaticDependencies.Remove(_currentDependencyPhase); + } + + // 2. Increment current dependency phase + _currentDependencyPhase++; + + // 3. Notify that new dependency phase has been entered + if (ComputingDependencyPhaseChange != null) + ComputingDependencyPhaseChange(_currentDependencyPhase); + } + } while ((_markStack.Count != 0) || (_deferredStaticDependencies.Count != 0)); if (_resultSorter != null) _markedNodes.MergeSortAllowDuplicates(_resultSorter); diff --git a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzerBase.cs b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzerBase.cs index ea62d5f351f06..d4ee7616c3183 100644 --- a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzerBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzerBase.cs @@ -64,6 +64,12 @@ public abstract ImmutableArray> Marked public abstract event Action>> ComputeDependencyRoutine; + /// + /// This event is triggered when all of the deferred dependencies from a phase have finished, and + /// dependency processing is moving to the next phase. + /// + public abstract event Action ComputingDependencyPhaseChange; + /// /// Used to walk all nodes that should be emitted to a log. Not intended for other purposes. /// diff --git a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyNodeCore.cs b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyNodeCore.cs index 43d3cb6d282f2..3ef331e3698c4 100644 --- a/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyNodeCore.cs +++ b/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyNodeCore.cs @@ -124,6 +124,8 @@ public abstract bool StaticDependenciesAreComputed get; } + public virtual int DependencyPhaseForDeferredStaticComputation { get; } = 0; + public abstract IEnumerable GetStaticDependencies(DependencyContextType context); public abstract IEnumerable GetConditionalStaticDependencies(DependencyContextType context); diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs index 620fb78344183..1483cfb413814 100644 --- a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Text; @@ -73,6 +74,8 @@ public bool Equals(SymDocument other) public class PdbWriter { + private const string DiaSymReaderLibrary = "Microsoft.DiaSymReader.Native"; + string _pdbPath; PDBExtraData _pdbExtraData; @@ -86,34 +89,33 @@ public class PdbWriter UIntPtr _pdbMod; ISymNGenWriter2 _ngenWriter; - private const string DiaSymReaderModuleName32 = "Microsoft.DiaSymReader.Native.x86.dll"; - private const string DiaSymReaderModuleName64 = "Microsoft.DiaSymReader.Native.amd64.dll"; - - private const string CreateNGenPdbWriterFactoryName = "CreateNGenPdbWriter"; - - [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] - [DllImport(DiaSymReaderModuleName32, EntryPoint = CreateNGenPdbWriterFactoryName, PreserveSig = false)] - private extern static void CreateNGenPdbWriter32([MarshalAs(UnmanagedType.LPWStr)] string ngenImagePath, [MarshalAs(UnmanagedType.LPWStr)] string pdbPath, [MarshalAs(UnmanagedType.IUnknown)] out object ngenPdbWriter); - - [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] - [DllImport(DiaSymReaderModuleName64, EntryPoint = CreateNGenPdbWriterFactoryName, PreserveSig = false)] - private extern static void CreateNGenPdbWriter64([MarshalAs(UnmanagedType.LPWStr)] string ngenImagePath, [MarshalAs(UnmanagedType.LPWStr)] string pdbPath, [MarshalAs(UnmanagedType.IUnknown)] out object ngenPdbWriter); - - private static ISymNGenWriter2 CreateNGenWriter(string ngenImagePath, string pdbPath) + static PdbWriter() { - object instance; + NativeLibrary.SetDllImportResolver(typeof(PdbWriter).Assembly, DllImportResolver); + } - if (IntPtr.Size == 4) - { - CreateNGenPdbWriter32(ngenImagePath, pdbPath, out instance); - } - else + private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + IntPtr libraryHandle = IntPtr.Zero; + if (libraryName == DiaSymReaderLibrary) { - CreateNGenPdbWriter64(ngenImagePath, pdbPath, out instance); + string archSuffix = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); + if (archSuffix == "x64") + { + archSuffix = "amd64"; + } + libraryHandle = NativeLibrary.Load(DiaSymReaderLibrary + "." + archSuffix + ".dll", assembly, searchPath); } - return (ISymNGenWriter2)instance; + return libraryHandle; } + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)] + [DllImport(DiaSymReaderLibrary, PreserveSig = false)] + private extern static void CreateNGenPdbWriter( + [MarshalAs(UnmanagedType.LPWStr)] string ngenImagePath, + [MarshalAs(UnmanagedType.LPWStr)] string pdbPath, + [MarshalAs(UnmanagedType.Interface)] out ISymNGenWriter2 ngenPdbWriter); + public PdbWriter(string pdbPath, PDBExtraData pdbExtraData) { SymDocument unknownDocument = new SymDocument(); @@ -209,10 +211,10 @@ private void WritePDBDataHelper(string dllPath, IEnumerable methods) _pdbFilePath = Path.Combine(_pdbPath, dllNameWithoutExtension + ".ni.pdb"); } - // Delete any preexisting PDB file upfront otherwise CreateNGenWriter silently opens it + // Delete any preexisting PDB file upfront, otherwise CreateNGenPdbWriter silently opens it File.Delete(_pdbFilePath); - _ngenWriter = CreateNGenWriter(dllPath, _pdbFilePath); + CreateNGenPdbWriter(dllPath, _pdbFilePath, out _ngenWriter); { // PDB file is now created. Get its path and update _pdbFilePath so the PDB file diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs index 2312ecc1c4a75..e2ec6059d5dc7 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs @@ -81,6 +81,12 @@ public bool EnforceOwningType(EcmaModule module) /// public abstract bool IsInputBubble { get; } + /// + /// Returns true when the base type and derived type don't reside in the same version bubble + /// in which case the runtime aligns the field base offset. + /// + public abstract bool NeedsAlignmentBetweenBaseTypeAndDerived(MetadataType baseType, MetadataType derivedType); + /// /// List of input modules to use for the compilation. /// diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index de5cb85bfad42..079ef257595a7 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -1085,7 +1085,7 @@ public int GetNextOffset() // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI // specifies this so that vararg processing on the callee side is simplified). - if (fFloatingPoint && !IsVarArg) + if (fFloatingPoint && _transitionBlock.IsArmhfABI && !IsVarArg) { // Handle floating point (primitive) arguments. 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 d4c08fdf2243c..ef1fe0eb6f185 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 @@ -457,7 +457,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) builder.AddSymbol(this); builder.EmitBytes(result); - return builder.ToObjectData(); ; + return builder.ToObjectData(); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DeferredTillPhaseNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DeferredTillPhaseNode.cs new file mode 100644 index 0000000000000..bedb081f87447 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DeferredTillPhaseNode.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + class DeferredTillPhaseNode : DependencyNodeCore + { + private readonly int _phase; + private readonly List> _dependencies = new List>(); + private bool _dependenciesNoLongerMutable; + + public DeferredTillPhaseNode(int phase) + { + Debug.Assert(phase > 0); + _phase = phase; + } + + public void NotifyCurrentPhase(int newPhase) + { + if (newPhase >= _phase) + _dependenciesNoLongerMutable = true; + } + + public void AddDependency(DependencyNodeCore newDependency) + { + if (_dependenciesNoLongerMutable) + throw new Exception(); + + _dependencies.Add(newDependency); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + + public override bool HasDynamicDependencies => false; + + public override bool HasConditionalStaticDependencies => false; + + public override bool StaticDependenciesAreComputed => _dependenciesNoLongerMutable; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable GetStaticDependencies(NodeFactory context) + { + foreach (var dependencyNode in _dependencies) + { + yield return new DependencyNodeCore.DependencyListEntry(dependencyNode, "DeferredDependency"); + } + } + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => throw new NotImplementedException(); + protected override string GetName(NodeFactory context) => $"DeferredTillPhaseNode {_phase}"; + + public override int DependencyPhaseForDeferredStaticComputation => _phase; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs index 62b5f5ce8c18e..f993b381d8cea 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs @@ -41,22 +41,31 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) EcmaModule targetModule = factory.SignatureContext.GetTargetModule(_fieldDesc); SignatureContext innerContext = dataBuilder.EmitFixup(factory, _fixupKind, targetModule, factory.SignatureContext); + uint baseOffset = 0; + uint fieldOffset = (uint)_fieldDesc.Offset.AsInt; if (_fixupKind == ReadyToRunFixupKind.Verify_FieldOffset) { TypeDesc baseType = _fieldDesc.OwningType.BaseType; - if ((_fieldDesc.OwningType.BaseType != null) && !_fieldDesc.IsStatic && !_fieldDesc.OwningType.IsValueType) + if ((_fieldDesc.OwningType.BaseType != null) + && !_fieldDesc.IsStatic + && !_fieldDesc.OwningType.IsValueType) { - dataBuilder.EmitUInt((uint)_fieldDesc.OwningType.FieldBaseOffset().AsInt); + MetadataType owningType = (MetadataType)_fieldDesc.OwningType; + baseOffset = (uint)owningType.FieldBaseOffset().AsInt; + if (factory.CompilationModuleGroup.NeedsAlignmentBetweenBaseTypeAndDerived((MetadataType)baseType, owningType)) + { + fieldOffset -= baseOffset; + baseOffset = 0; + } } - else - dataBuilder.EmitUInt(0); + dataBuilder.EmitUInt(baseOffset); } if ((_fixupKind == ReadyToRunFixupKind.Check_FieldOffset) || (_fixupKind == ReadyToRunFixupKind.Verify_FieldOffset)) { - dataBuilder.EmitUInt((uint)_fieldDesc.Offset.AsInt); + dataBuilder.EmitUInt(fieldOffset); } dataBuilder.EmitFieldSignature(_fieldDesc, innerContext); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs index e806bdb317c94..78fb4602aeb2e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs @@ -145,7 +145,7 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde { sb.Append(" ["); sb.Append(_methodArgument.Token.MetadataReader.GetString(_methodArgument.Token.MetadataReader.GetAssemblyDefinition().Name)); - sb.Append(":"); ; + sb.Append(":"); sb.Append(((uint)_methodArgument.Token.Token).ToString("X8")); sb.Append("]"); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs new file mode 100644 index 0000000000000..b038b39a3e8ef --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + +using Internal.Text; +using Internal.TypeSystem.Ecma; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + public class ManifestAssemblyMvidHeaderNode : ObjectNode, ISymbolDefinitionNode + { + private ManifestMetadataTableNode _manifestNode; + + public ManifestAssemblyMvidHeaderNode(ManifestMetadataTableNode manifestNode) + { + _manifestNode = manifestNode; + } + + public override ObjectNodeSection Section => ObjectNodeSection.TextSection; + + public override bool IsShareable => false; + + protected internal override int Phase => (int)ObjectNodePhase.Ordered; + + public override int ClassCode => 735231445; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public int Size => _manifestNode.ManifestAssemblyMvidTableSize; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append("__ManifestAssemblyMvids"); + } + + protected override string GetName(NodeFactory nodeFactory) + { + Utf8StringBuilder sb = new Utf8StringBuilder(); + AppendMangledName(nodeFactory.NameMangler, sb); + return sb.ToString(); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + if (relocsOnly) + { + return new ObjectData(Array.Empty(), null, 0, null); + } + + byte[] manifestAssemblyMvidTable = _manifestNode.GetManifestAssemblyMvidTableData(); + return new ObjectData(manifestAssemblyMvidTable, Array.Empty(), alignment: 0, new ISymbolDefinitionNode[] { this }); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs index e7d9858175365..ff85543bded9d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.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; @@ -41,6 +41,13 @@ public class ManifestMetadataTableNode : HeaderTableNode /// private readonly Dictionary _moduleIdToAssemblyNameMap; + /// + /// MVIDs of the assemblies included in manifest metadata to be emitted as the + /// ManifestAssemblyMvid R2R header table used by the runtime to check loaded assemblies + /// and fail fast in case of mismatch. + /// + private readonly List _manifestAssemblyMvids; + /// /// Registered signature emitters. /// @@ -71,6 +78,7 @@ public ManifestMetadataTableNode(NodeFactory nodeFactory) { _assemblyRefToModuleIdMap = new Dictionary(); _moduleIdToAssemblyNameMap = new Dictionary(); + _manifestAssemblyMvids = new List(); _signatureEmitters = new List(); _nodeFactory = nodeFactory; _nextModuleId = 1; @@ -150,6 +158,7 @@ private int ModuleToIndexInternal(EcmaModule module) Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(module)); _moduleIdToAssemblyNameMap.Add(assemblyRefIndex, assemblyName); + _manifestAssemblyMvids.Add(module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid)); } return assemblyRefIndex; } @@ -241,5 +250,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) alignment: 1, definedSymbols: new ISymbolDefinitionNode[] { this }); } + + private const int GuidByteSize = 16; + + public int ManifestAssemblyMvidTableSize => GuidByteSize * _manifestAssemblyMvids.Count; + + internal byte[] GetManifestAssemblyMvidTableData() + { + byte[] manifestAssemblyMvidTable = new byte[ManifestAssemblyMvidTableSize]; + for (int i = 0; i < _manifestAssemblyMvids.Count; i++) + { + _manifestAssemblyMvids[i].TryWriteBytes(new Span(manifestAssemblyMvidTable, GuidByteSize * i, GuidByteSize)); + } + return manifestAssemblyMvidTable; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs index 88685de9ba689..a03e6a1c2d356 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs @@ -26,6 +26,7 @@ public class MethodWithGCInfo : ObjectNode, IMethodBodyNode, ISymbolDefinitionNo private DebugEHClauseInfo[] _debugEHClauseInfos; private List _fixups; private MethodDesc[] _inlinedMethods; + private bool _lateTriggeredCompilation; public MethodWithGCInfo(MethodDesc methodDesc) { @@ -34,6 +35,20 @@ public MethodWithGCInfo(MethodDesc methodDesc) _method = methodDesc; } + protected override void OnMarked(NodeFactory context) + { + // Once past phase 1, no new methods which are interesting for compilation may be marked except for methods + // specially enabled for higher phases + if (context.CompilationCurrentPhase > 1) + { + SetCode(new ObjectNode.ObjectData(Array.Empty(), null, 1, Array.Empty())); + InitializeFrameInfos(Array.Empty()); + } + _lateTriggeredCompilation = context.CompilationCurrentPhase != 0; + } + + public override int DependencyPhaseForDeferredStaticComputation => _lateTriggeredCompilation ? 2 : 0; + public void SetCode(ObjectData data) { Debug.Assert(_methodCode == null); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index bd89459beeca9..667bae1742abf 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -29,7 +29,14 @@ public static TransitionBlock FromTarget(TargetDetails target) X64UnixTransitionBlock.Instance; case TargetArchitecture.ARM: - return Arm32TransitionBlock.Instance; + if (target.Abi == TargetAbi.CoreRTArmel) + { + return Arm32ElTransitionBlock.Instance; + } + else + { + return Arm32TransitionBlock.Instance; + } case TargetArchitecture.ARM64: return Arm64TransitionBlock.Instance; @@ -57,6 +64,9 @@ public static TransitionBlock FromTarget(TargetDetails target) /// public virtual bool IsX64UnixABI => false; + public virtual bool IsArmelABI => false; + public virtual bool IsArmhfABI => false; + public abstract int PointerSize { get; } public int StackElemSize() => PointerSize; @@ -301,11 +311,17 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp throw new NotSupportedException(); case CorElementType.ELEMENT_TYPE_R4: - fpReturnSize = sizeof(float); + if (!IsArmelABI) + { + fpReturnSize = sizeof(float); + } break; case CorElementType.ELEMENT_TYPE_R8: - fpReturnSize = sizeof(double); + if (!IsArmelABI) + { + fpReturnSize = sizeof(double); + } break; case CorElementType.ELEMENT_TYPE_VALUETYPE: @@ -499,27 +515,37 @@ internal sealed class X64UnixTransitionBlock : X64TransitionBlock public override bool IsArgPassedByRef(TypeHandle th) => false; } - private sealed class Arm32TransitionBlock : TransitionBlock + private class Arm32TransitionBlock : TransitionBlock { public static TransitionBlock Instance = new Arm32TransitionBlock(); - public override TargetArchitecture Architecture => TargetArchitecture.ARM; - public override int PointerSize => 4; + public sealed override TargetArchitecture Architecture => TargetArchitecture.ARM; + public sealed override int PointerSize => 4; // R0, R1, R2, R3 - public override int NumArgumentRegisters => 4; + public sealed override int NumArgumentRegisters => 4; // R4, R5, R6, R7, R8, R9, R10, R11, R14 - public override int NumCalleeSavedRegisters => 9; + public sealed override int NumCalleeSavedRegisters => 9; // Callee-saves, argument registers - public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; - public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; + public sealed override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; + public sealed override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; // D0..D7 - public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double) + PointerSize; - public override int EnregisteredParamTypeMaxSize => 0; - public override int EnregisteredReturnTypeIntegerMaxSize => 4; + public sealed override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double) + PointerSize; + public sealed override int EnregisteredParamTypeMaxSize => 0; + public sealed override int EnregisteredReturnTypeIntegerMaxSize => 4; - public override bool IsArgPassedByRef(TypeHandle th) => false; + public override bool IsArmhfABI => true; - public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0); + public sealed override bool IsArgPassedByRef(TypeHandle th) => false; + + public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0); + } + + private class Arm32ElTransitionBlock : Arm32TransitionBlock + { + public new static TransitionBlock Instance = new Arm32ElTransitionBlock(); + + public override bool IsArmhfABI => false; + public override bool IsArmelABI => true; } private sealed class Arm64TransitionBlock : TransitionBlock diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index 87295b01d3d20..48d893ad2ccfe 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -140,7 +140,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact { DependencyList dependencies = new DependencyList(); - if (_typeDesc.HasInstantiation && !_typeDesc.IsGenericDefinition) + if (_typeDesc.HasInstantiation && !_typeDesc.IsGenericDefinition && (factory.CompilationCurrentPhase == 0)) { dependencies.Add(factory.AllMethodsOnType(_typeDesc), "Methods on generic type instantiation"); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 209a5a1af2116..4ffb3b7d618ba 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -297,6 +297,8 @@ private void CreateNodeCaches() }); } + public int CompilationCurrentPhase { get; private set; } + public SignatureContext SignatureContext; public ModuleTokenResolver Resolver; @@ -543,6 +545,8 @@ public ImportThunk ImportThunk(ReadyToRunHelper helper, ImportSectionNode contai public void AttachToDependencyGraph(DependencyAnalyzerBase graph) { + graph.ComputingDependencyPhaseChange += Graph_ComputingDependencyPhaseChange; + var compilerIdentifierNode = new CompilerIdentifierNode(Target); Header.Add(Internal.Runtime.ReadyToRunSectionType.CompilerIdentifier, compilerIdentifierNode, compilerIdentifierNode); @@ -566,6 +570,9 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); + ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable); + Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode); + AssemblyTableNode assemblyTable = null; if (CompilationModuleGroup.IsCompositeBuildMode) @@ -731,6 +738,11 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) MetadataManager.AttachToDependencyGraph(graph); } + private void Graph_ComputingDependencyPhaseChange(int newPhase) + { + CompilationCurrentPhase = newPhase; + } + private ReadyToRunHelper GetGenericStaticHelper(ReadyToRunHelperId helperId) { ReadyToRunHelper r2rHelper; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/IRootingServiceProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/IRootingServiceProvider.cs index 0c0a504751bdb..be036aa89b7e6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/IRootingServiceProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/IRootingServiceProvider.cs @@ -10,6 +10,6 @@ namespace ILCompiler /// public interface IRootingServiceProvider { - void AddCompilationRoot(MethodDesc method, string reason); + void AddCompilationRoot(MethodDesc method, bool rootMinimalDependencies, string reason); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs index 40cb6c3a68ec4..5220e13f813ee 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs @@ -38,6 +38,9 @@ public class MethodProfileData { public MethodProfileData(MethodDesc method, MethodProfilingDataFlags flags, double exclusiveWeight, Dictionary callWeights, uint scenarioMask, PgoSchemaElem[] schemaData) { + if (method == null) + throw new ArgumentNullException("method"); + Method = method; Flags = flags; ScenarioMask = scenarioMask; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index a3668a990a6b8..0573222b6791b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -23,7 +23,7 @@ namespace ILCompiler { - public abstract class Compilation : ICompilation + public abstract class Compilation : ICompilation, IDisposable { protected readonly DependencyAnalyzerBase _dependencyGraph; protected readonly NodeFactory _nodeFactory; @@ -67,6 +67,7 @@ protected Compilation( _methodILCache = new ILCache(ilProvider, NodeFactory.CompilationModuleGroup); } + public abstract void Dispose(); public abstract void Compile(string outputFileName); public abstract void WriteDependencyLog(string outputFileName); @@ -185,20 +186,30 @@ private class RootingServiceProvider : IRootingServiceProvider { private readonly NodeFactory _factory; private readonly RootAdder _rootAdder; + private readonly DeferredTillPhaseNode _deferredPhaseNode = new DeferredTillPhaseNode(1); public RootingServiceProvider(NodeFactory factory, RootAdder rootAdder) { _factory = factory; _rootAdder = rootAdder; + _rootAdder(_deferredPhaseNode, "Deferred nodes"); } - public void AddCompilationRoot(MethodDesc method, string reason) + public void AddCompilationRoot(MethodDesc method, bool rootMinimalDependencies, string reason) { MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific); if (_factory.CompilationModuleGroup.ContainsMethodBody(canonMethod, false)) { IMethodNode methodEntryPoint = _factory.CompiledMethodNode(canonMethod); - _rootAdder(methodEntryPoint, reason); + + if (rootMinimalDependencies) + { + _deferredPhaseNode.AddDependency((DependencyNodeCore)methodEntryPoint); + } + else + { + _rootAdder(methodEntryPoint, reason); + } } } } @@ -208,6 +219,7 @@ public interface ICompilation { void Compile(string outputFileName); void WriteDependencyLog(string outputFileName); + void Dispose(); } public sealed class ReadyToRunCodegenCompilation : Compilation @@ -529,6 +541,13 @@ public TypeDesc GetTypeOfRuntimeType() return TypeSystemContext.SystemModule.GetKnownType("System", "RuntimeType"); } + // Compilation is broken into phases which interact with dependency analysis + // Phase 0: All compilations which are driven by our standard heuristics and dependency expansion model + // Phase 1: A helper phase which works in tandem with the DeferredTillPhaseNode to gather work to be done in phase 2 + // Phase 2: A phase where all compilations are not allowed to add dependencies that can trigger further compilations. + // The _finishedFirstCompilationRunInPhase2 variable works in concert some checking to ensure that we don't violate any of this model + private bool _finishedFirstCompilationRunInPhase2 = false; + protected override void ComputeDependencyNodeDependencies(List> obj) { using (PerfEventSource.StartStopEvents.JitEvents()) @@ -536,6 +555,19 @@ protected override void ComputeDependencyNodeDependencies(List> compileOneMethod = (DependencyNodeCore dependency) => { MethodWithGCInfo methodCodeNodeNeedingCode = dependency as MethodWithGCInfo; + if (methodCodeNodeNeedingCode == null) + { + if (dependency is DeferredTillPhaseNode deferredPhaseNode) + { + if (Logger.IsVerbose) + _logger.Writer.WriteLine($"Moved to phase {_nodeFactory.CompilationCurrentPhase}"); + deferredPhaseNode.NotifyCurrentPhase(_nodeFactory.CompilationCurrentPhase); + return; + } + } + + Debug.Assert((_nodeFactory.CompilationCurrentPhase == 0) || ((_nodeFactory.CompilationCurrentPhase == 2) && !_finishedFirstCompilationRunInPhase2)); + MethodDesc method = methodCodeNodeNeedingCode.Method; if (Logger.IsVerbose) @@ -556,7 +588,7 @@ protected override void ComputeDependencyNodeDependencies(List new CorInfoImpl(this)); - corInfoImpl.CompileMethod(methodCodeNodeNeedingCode); + corInfoImpl.CompileMethod(methodCodeNodeNeedingCode, Logger); } } catch (TypeSystemException ex) @@ -578,6 +610,8 @@ protected override void ComputeDependencyNodeDependencies(List NodeFactory.CopiedFieldRva(field); + + public override void Dispose() + { + _corInfoImpls?.Clear(); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index ac8dfcb2d963e..910dffe43b2ae 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -82,6 +82,11 @@ public override CompilationBuilder UseBackendOptions(IEnumerable options builder.Add(new KeyValuePair(name, value)); } + if (_context.Target.Abi == TargetAbi.CoreRTArmel) + { + builder.Add(new KeyValuePair("JitSoftFP", "1")); + } + _ryujitOptions = builder.ToArray(); return this; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 2e0a760e9725a..92ef5346c770f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -267,8 +267,11 @@ private bool ModuleMatchesCompilationUnitIndex(ModuleDesc module1, ModuleDesc mo return ModuleToCompilationUnitIndex(module1) == ModuleToCompilationUnitIndex(module2); } - public bool NeedsAlignmentBetweenBaseTypeAndDerived(MetadataType baseType, MetadataType derivedType) + public override bool NeedsAlignmentBetweenBaseTypeAndDerived(MetadataType baseType, MetadataType derivedType) { + if (baseType.IsObject) + return false; + if (!ModuleMatchesCompilationUnitIndex(derivedType.Module, baseType.Module) || TypeLayoutCompilationUnits(baseType).HasMultipleCompilationUnits) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 5b8c09852f7ec..4c8f9770332ca 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -68,6 +68,13 @@ public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) } } + /// + /// This is a rough equivalent of the CoreCLR runtime method ReadyToRunInfo::GetFieldBaseOffset. + /// In contrast to the auto field layout algorithm, this method unconditionally applies alignment + /// between base and derived class (even when they reside in the same version bubble). + /// + public LayoutInt CalculateFieldBaseOffset(MetadataType type) => _r2rFieldLayoutAlgorithm.CalculateFieldBaseOffset(type, type.RequiresAlign8(), requiresAlignedBase: true); + public void SetCompilationGroup(ReadyToRunCompilationModuleGroupBase compilationModuleGroup) { _r2rFieldLayoutAlgorithm.SetCompilationGroup(compilationModuleGroup); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs index a52c872eb9e0e..daacba4ef35f6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs @@ -63,7 +63,7 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) if (!CorInfoImpl.ShouldSkipCompilation(method)) { CheckCanGenerateMethod(method); - rootProvider.AddCompilationRoot(method, "Profile triggered method"); + rootProvider.AddCompilationRoot(method, rootMinimalDependencies: true, reason: "Profile triggered method"); } } catch (TypeSystemException) @@ -116,7 +116,7 @@ private void RootMethods(TypeDesc type, string reason, IRootingServiceProvider r if (!CorInfoImpl.ShouldSkipCompilation(method)) { CheckCanGenerateMethod(methodToRoot); - rootProvider.AddCompilationRoot(methodToRoot, reason); + rootProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: reason); } } catch (TypeSystemException) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index be653fdcab1cd..1cc09466ee216 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -18,15 +18,9 @@ namespace ILCompiler { public static class ReadyToRunTypeExtensions { - public static LayoutInt FieldBaseOffset(this TypeDesc type) + public static LayoutInt FieldBaseOffset(this MetadataType type) { - LayoutInt baseOffset = type.BaseType.InstanceByteCount; - if (type.RequiresAlign8()) - { - baseOffset = LayoutInt.AlignUp(baseOffset, new LayoutInt(8), type.Context.Target); - } - - return baseOffset; + return ((ReadyToRunCompilerContext)type.Context).CalculateFieldBaseOffset(type); } } @@ -823,23 +817,14 @@ protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(Metada /// This method decides whether the type needs aligned base offset in order to have layout resilient to /// base class layout changes. /// - protected override void AlignBaseOffsetIfNecessary(MetadataType type, ref LayoutInt baseOffset, bool requiresAlign8) + protected override void AlignBaseOffsetIfNecessary(MetadataType type, ref LayoutInt baseOffset, bool requiresAlign8, bool requiresAlignedBase) { - DefType baseType = type.BaseType; - - if (!_compilationGroup.NeedsAlignmentBetweenBaseTypeAndDerived(baseType: (MetadataType)baseType, derivedType: type)) - { - // The type is defined in the module that's currently being compiled and the type layout doesn't depend on other modules - return; - } - - LayoutInt alignment = new LayoutInt(type.Context.Target.PointerSize); - - if (requiresAlign8) + if (requiresAlignedBase || _compilationGroup.NeedsAlignmentBetweenBaseTypeAndDerived(baseType: (MetadataType)type.BaseType, derivedType: type)) { - alignment = new LayoutInt(8); + bool use8Align = (requiresAlign8 || type.BaseType.RequiresAlign8()) && type.Context.Target.Architecture != TargetArchitecture.X86; + LayoutInt alignment = new LayoutInt(use8Align ? 8 : type.Context.Target.PointerSize); + baseOffset = LayoutInt.AlignUp(baseOffset, alignment, type.Context.Target); } - baseOffset = LayoutInt.AlignUp(baseOffset, alignment, type.Context.Target); } public static bool IsManagedSequentialType(TypeDesc type) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs index b26d9ab6226fb..8b419d4b3955f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/IBCProfileParser.cs @@ -106,7 +106,7 @@ public ProfileData ParseIBCDataFromModule(EcmaModule ecmaModule) case CorTokenType.mdtMethodDef: case CorTokenType.mdtMemberRef: case CorTokenType.mdtMethodSpec: - object metadataObject = ecmaModule.GetObject(System.Reflection.Metadata.Ecma335.MetadataTokens.EntityHandle((int)entry.Token)); + object metadataObject = ecmaModule.GetObject(System.Reflection.Metadata.Ecma335.MetadataTokens.EntityHandle((int)entry.Token), NotFoundBehavior.ReturnNull); if (metadataObject is MethodDesc) { associatedMethod = (MethodDesc)metadataObject; @@ -346,7 +346,7 @@ private uint LookupIbcTypeToken(ref EcmaModule externalModule, uint ibcToken, Di if (!(m is EcmaModule)) continue; - foundType = (EcmaType)m.GetType(typeNamespace, typeName, throwIfNotFound: false); + foundType = (EcmaType)m.GetType(typeNamespace, typeName, NotFoundBehavior.ReturnNull); if (foundType != null) { externalModule = foundType.EcmaModule; @@ -356,7 +356,7 @@ private uint LookupIbcTypeToken(ref EcmaModule externalModule, uint ibcToken, Di } else { - foundType = (EcmaType)externalModule.GetType(typeNamespace, typeName, throwIfNotFound: false); + foundType = (EcmaType)externalModule.GetType(typeNamespace, typeName, NotFoundBehavior.ReturnNull); } if (foundType == null) @@ -451,7 +451,7 @@ public EcmaModule GetModuleFromIndex(int index) { if (EcmaModule.MetadataReader.GetTableRowCount(TableIndex.AssemblyRef) < index) return null; - return EcmaModule.GetObject(MetadataTokens.EntityHandle(((int)CorTokenType.mdtAssemblyRef) | index)) as EcmaModule; + return EcmaModule.GetObject(MetadataTokens.EntityHandle(((int)CorTokenType.mdtAssemblyRef) | index), NotFoundBehavior.ReturnNull) as EcmaModule; } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs index 560e12a468e82..22e54a8922adb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IBC/MIbcProfileParser.cs @@ -41,7 +41,12 @@ TypeSystemEntityOrUnknown IPgoSchemaDataLoader.TypeFr // token type is 0, therefore it can't be a type return new TypeSystemEntityOrUnknown((int)token); } - return new TypeSystemEntityOrUnknown((TypeDesc)_ilBody.GetObject((int)token)); + TypeDesc foundType = _ilBody.GetObject((int)token, NotFoundBehavior.ReturnNull) as TypeDesc; + if (foundType == null) + { + return new TypeSystemEntityOrUnknown((int)token & 0x00FFFFFF); + } + return new TypeSystemEntityOrUnknown(foundType); } catch { @@ -265,7 +270,9 @@ static IEnumerable ReadMIbcGroup(TypeSystemContext tsc, EcmaM metadataObject = null; try { - metadataObject = ilBody.GetObject(token); + metadataObject = ilBody.GetObject(token, NotFoundBehavior.ReturnNull); + if (metadataObject == null) + metadataObject = metadataNotResolvable; } catch (TypeSystemException) { @@ -426,13 +433,17 @@ static IEnumerable ReadMIbcGroup(TypeSystemContext tsc, EcmaM // If no exclusive weight is found assign a non zero value that assumes the order in the pgo file is significant. exclusiveWeight = Math.Min(1000000.0 - profileEntryFound, 0.0) / 1000000.0; } - MethodProfileData mibcData = new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF, pgoSchemaData); + if (methodInProgress != null) + { + // If the method being loaded didn't have meaningful input, skip + MethodProfileData mibcData = new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF, pgoSchemaData); + yield return mibcData; + } state = MibcGroupParseState.LookingForNextMethod; exclusiveWeight = 0; weights = null; instrumentationDataLongs = null; pgoSchemaData = null; - yield return mibcData; } methodInProgress = null; break; @@ -505,7 +516,7 @@ public override MetadataType GetGlobalModuleType() throw new NotImplementedException(); } - public override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { TypeSystemContext context = Context; @@ -515,9 +526,14 @@ public override MetadataType GetType(string nameSpace, string name, bool throwIf return Context.UniversalCanonType; else { - if (throwIfNotFound) + if (notFoundBehavior != NotFoundBehavior.ReturnNull) { - throw new TypeLoadException($"{nameSpace}.{name}"); + var failure = ResolutionFailure.GetTypeLoadResolutionFailure(nameSpace, name, "System.Private.Canon"); + ModuleDesc.GetTypeResolutionFailure = failure; + if (notFoundBehavior == NotFoundBehavior.Throw) + failure.Throw(); + + return null; } return null; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 46ec240cba14e..ec9ced76c8da2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -19,7 +19,7 @@ private MethodIL TryGetIntrinsicMethodILForActivator(MethodDesc method) && method.Name == "CreateInstance") { TypeDesc type = method.Instantiation[0]; - if (type.IsValueType && type.GetDefaultConstructor() == null) + if (type.IsValueType && type.GetParameterlessConstructor() == null) { // Replace the body with implementation that just returns "default" MethodDesc createDefaultInstance = method.OwningType.GetKnownMethod("CreateDefaultInstance", method.GetTypicalMethodDefinition().Signature); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index e3bb31b8ef33f..0175a47570248 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -1,4 +1,4 @@ - + Library ILCompiler.ReadyToRun @@ -102,6 +102,8 @@ + + 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 25d86cea5eb70..56a3cce130244 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -47,17 +47,17 @@ public class MethodWithToken public readonly TypeDesc OwningType; - public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constrainedType, bool unboxing, object context) + public MethodWithToken(MethodDesc method, ModuleToken token, TypeDesc constrainedType, bool unboxing, object context, TypeDesc devirtualizedMethodOwner = null) { Debug.Assert(!method.IsUnboxingThunk()); Method = method; Token = token; ConstrainedType = constrainedType; Unboxing = unboxing; - OwningType = GetMethodTokenOwningType(this, constrainedType, context, out OwningTypeNotDerivedFromToken); + OwningType = GetMethodTokenOwningType(this, constrainedType, context, devirtualizedMethodOwner, out OwningTypeNotDerivedFromToken); } - private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, TypeDesc constrainedType, object context, out bool owningTypeNotDerivedFromToken) + private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, TypeDesc constrainedType, object context, TypeDesc devirtualizedMethodOwner, out bool owningTypeNotDerivedFromToken) { ModuleToken moduleToken = methodToken.Token; owningTypeNotDerivedFromToken = false; @@ -72,7 +72,7 @@ private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, Ty if (moduleToken.TokenType == CorTokenType.mdtMethodDef) { var methodDefinition = moduleToken.MetadataReader.GetMethodDefinition((MethodDefinitionHandle)moduleToken.Handle); - return HandleContext(moduleToken.Module, methodDefinition.GetDeclaringType(), methodToken.Method.OwningType, constrainedType, context, ref owningTypeNotDerivedFromToken); + return HandleContext(moduleToken.Module, methodDefinition.GetDeclaringType(), methodToken.Method.OwningType, constrainedType, context, devirtualizedMethodOwner, ref owningTypeNotDerivedFromToken); } // At this point moduleToken must point at a MemberRef. @@ -84,14 +84,15 @@ private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, Ty case HandleKind.TypeReference: case HandleKind.TypeSpecification: { - return HandleContext(moduleToken.Module, memberRef.Parent, methodToken.Method.OwningType, constrainedType, context, ref owningTypeNotDerivedFromToken); + Debug.Assert(devirtualizedMethodOwner == null); // Devirtualization is expected to always use a methoddef token + return HandleContext(moduleToken.Module, memberRef.Parent, methodToken.Method.OwningType, constrainedType, context, null, ref owningTypeNotDerivedFromToken); } default: return methodToken.Method.OwningType; } - TypeDesc HandleContext(EcmaModule module, EntityHandle handle, TypeDesc methodTargetOwner, TypeDesc constrainedType, object context, ref bool owningTypeNotDerivedFromToken) + TypeDesc HandleContext(EcmaModule module, EntityHandle handle, TypeDesc methodTargetOwner, TypeDesc constrainedType, object context, TypeDesc devirtualizedMethodOwner, ref bool owningTypeNotDerivedFromToken) { var tokenOnlyOwningType = module.GetType(handle); TypeDesc actualOwningType; @@ -116,7 +117,36 @@ TypeDesc HandleContext(EcmaModule module, EntityHandle handle, TypeDesc methodTa typeInstantiation = typeContext.Instantiation; } - var instantiatedOwningType = tokenOnlyOwningType.InstantiateSignature(typeInstantiation, methodInstantiation); + TypeDesc instantiatedOwningType = null; + + if (devirtualizedMethodOwner != null) + { + // We might be in a situation where we use the passed in type (devirtualization scenario) + // Check to see if devirtualizedMethodOwner actually is a type derived from the type definition in some way. + bool derivesFromTypeDefinition = false; + TypeDesc currentType = devirtualizedMethodOwner; + do + { + derivesFromTypeDefinition = currentType.GetTypeDefinition() == tokenOnlyOwningType; + currentType = currentType.BaseType; + } while(currentType != null && !derivesFromTypeDefinition); + + if (derivesFromTypeDefinition) + { + instantiatedOwningType = devirtualizedMethodOwner; + } + else + { + Debug.Assert(false); // This is expected to fire if and only if we implement devirtualization to default interface methods + throw new RequiresRuntimeJitException(methodTargetOwner.ToString()); + } + } + + if (instantiatedOwningType == null) + { + instantiatedOwningType = tokenOnlyOwningType.InstantiateSignature(typeInstantiation, methodInstantiation); + } + var canonicalizedOwningType = instantiatedOwningType.ConvertToCanonForm(CanonicalFormKind.Specific); if ((instantiatedOwningType == canonicalizedOwningType) || (constrainedType != null)) { @@ -312,15 +342,6 @@ unsafe partial class CorInfoImpl private ArrayBuilder _inlinedMethods; private UnboxingMethodDescFactory _unboxingThunkFactory = new UnboxingMethodDescFactory(); - private struct PgoInstrumentationResults - { - public PgoInstrumentationSchema* pSchema; - public uint countSchemaItems; - public byte* pInstrumentationData; - public HRESULT hr; - } - Dictionary _pgoResults = new Dictionary(); - public CorInfoImpl(ReadyToRunCodegenCompilation compilation) : this() { @@ -388,7 +409,32 @@ public static bool ShouldSkipCompilation(MethodDesc methodNeedingCode) return false; } - public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode) + private bool FunctionJustThrows(MethodIL ilBody) + { + try + { + if (ilBody.GetExceptionRegions().Length != 0) + return false; + + ILReader reader = new ILReader(ilBody.GetILBytes()); + + while (reader.HasNext) + { + var ilOpcode = reader.ReadILOpcode(); + if (ilOpcode == ILOpcode.throw_) + return true; + if (ilOpcode.IsBranch() || ilOpcode == ILOpcode.switch_) + return false; + reader.Skip(ilOpcode); + } + } + catch + { } + + return false; + } + + public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode, Logger logger) { bool codeGotPublished = false; _methodCodeNode = methodCodeNodeNeedingCode; @@ -398,10 +444,19 @@ public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode) if (!ShouldSkipCompilation(MethodBeingCompiled) && !MethodSignatureIsUnstable(MethodBeingCompiled.Signature, out var _)) { MethodIL methodIL = _compilation.GetMethodIL(MethodBeingCompiled); + if (methodIL != null) { - CompileMethodInternal(methodCodeNodeNeedingCode, methodIL); - codeGotPublished = true; + if (!FunctionJustThrows(methodIL)) + { + CompileMethodInternal(methodCodeNodeNeedingCode, methodIL); + codeGotPublished = true; + } + else + { + if (logger.IsVerbose) + logger.Writer.WriteLine($"Warning: Method `{MethodBeingCompiled}` was not compiled because it always throws an exception"); + } } } } @@ -850,7 +905,14 @@ private bool canTailCall(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUC private MethodWithToken ComputeMethodWithToken(MethodDesc method, ref CORINFO_RESOLVED_TOKEN pResolvedToken, TypeDesc constrainedType, bool unboxing) { ModuleToken token = HandleToModuleToken(ref pResolvedToken, method, out object context, ref constrainedType); - return new MethodWithToken(method, token, constrainedType: constrainedType, unboxing: unboxing, context: context); + + TypeDesc devirtualizedMethodOwner = null; + if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod) + { + devirtualizedMethodOwner = HandleToObject(pResolvedToken.hClass); + } + + return new MethodWithToken(method, token, constrainedType: constrainedType, unboxing: unboxing, context: context, devirtualizedMethodOwner: devirtualizedMethodOwner); } private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken, MethodDesc methodDesc, out object context, ref TypeDesc constrainedType) @@ -1340,7 +1402,7 @@ private void ceeInfoGetCallInfo( // Its basic meaning is that shared generic methods always need instantiating // stubs as the shared generic code needs the method dictionary parameter that cannot // be provided by other means. - useInstantiatingStub = originalMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstMethodDescArg(); + useInstantiatingStub = originalMethod.OwningType.IsArray || originalMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstMethodDescArg(); callerMethod = HandleToObject(callerHandle); @@ -1540,7 +1602,8 @@ private void ceeInfoGetCallInfo( } methodToCall = targetMethod; - MethodDesc canonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); + bool isArrayConstructor = targetMethod.OwningType.IsArray && targetMethod.IsConstructor; + MethodDesc canonMethod = (isArrayConstructor ? null : targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)); if (directCall) { @@ -1554,7 +1617,7 @@ private void ceeInfoGetCallInfo( bool allowInstParam = (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_ALLOWINSTPARAM) != 0; - if (!allowInstParam && canonMethod.RequiresInstArg()) + if (!allowInstParam && canonMethod != null && canonMethod.RequiresInstArg()) { useInstantiatingStub = true; } @@ -1578,7 +1641,17 @@ private void ceeInfoGetCallInfo( const CORINFO_CALLINFO_FLAGS LdVirtFtnMask = CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN | CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_CALLVIRT; bool unresolvedLdVirtFtn = ((flags & LdVirtFtnMask) == LdVirtFtnMask) && !resolvedCallVirt; - if ((pResult->exactContextNeedsRuntimeLookup && useInstantiatingStub && (!allowInstParam || resolvedConstraint)) || forceUseRuntimeLookup) + if (isArrayConstructor) + { + // Constructors on arrays are special and don't actually have entrypoints. + // That would be fine by itself and wouldn't need special casing. But + // constructors on SzArray have a weird property that causes them not to have canonical forms. + // int[][] has a .ctor(int32,int32) to construct the jagged array in one go, but its canonical + // form of __Canon[] doesn't have the two-parameter constructor. The canonical form would need + // to have an unlimited number of constructors to cover stuff like "int[][][][][][]..." + pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL; + } + else if ((pResult->exactContextNeedsRuntimeLookup && useInstantiatingStub && (!allowInstParam || resolvedConstraint)) || forceUseRuntimeLookup) { if (unresolvedLdVirtFtn) { @@ -1603,7 +1676,7 @@ private void ceeInfoGetCallInfo( if (allowInstParam) { useInstantiatingStub = false; - methodToCall = canonMethod; + methodToCall = canonMethod ?? methodToCall; } pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL; @@ -1830,12 +1903,19 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO nonUnboxingMethod = rawPinvoke.Target; } - // READYTORUN: FUTURE: Direct calls if possible - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( - _compilation.NodeFactory.MethodEntrypoint( - ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), - isInstantiatingStub: useInstantiatingStub, - isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0)); + if (methodToCall.OwningType.IsArray && methodToCall.IsConstructor) + { + pResult->codePointerOrStubLookup.constLookup = default; + } + else + { + // READYTORUN: FUTURE: Direct calls if possible + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + _compilation.NodeFactory.MethodEntrypoint( + ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), + isInstantiatingStub: useInstantiatingStub, + isPrecodeImportRequired: (flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0)); + } // If the abi of the method isn't stable, this will cause a usage of the RequiresRuntimeJitSymbol, which will trigger a RequiresRuntimeJitException UpdateConstLookupWithRequiresRuntimeJitSymbolIfNeeded(ref pResult->codePointerOrStubLookup.constLookup, targetMethod); @@ -2256,7 +2336,7 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, } // ENCODE_FIELD_BASE_OFFSET - int fieldBaseOffset = pMT.FieldBaseOffset().AsInt; + int fieldBaseOffset = ((MetadataType)pMT).FieldBaseOffset().AsInt; Debug.Assert(pResult->offset >= (uint)fieldBaseOffset); pResult->offset -= (uint)fieldBaseOffset; pResult->fieldAccessor = CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INSTANCE_WITH_BASE; @@ -2372,96 +2452,6 @@ private unsafe HRESULT allocPgoInstrumentationBySchema(CORINFO_METHOD_STRUCT_* f return 0; } - private PgoSchemaElem[] getPgoInstrumentationResults(MethodDesc method) - { - return _compilation.ProfileData[method]?.SchemaData; - } - - private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref PgoInstrumentationSchema* pSchema, ref uint countSchemaItems, byte** pInstrumentationData) - { - MethodDesc methodDesc = HandleToObject(ftnHnd); - - if (!_pgoResults.TryGetValue(methodDesc, out PgoInstrumentationResults pgoResults)) - { - var pgoResultsSchemas = getPgoInstrumentationResults(methodDesc); - if (pgoResultsSchemas == null) - { - pgoResults.hr = HRESULT.E_NOTIMPL; - } - else - { - PgoInstrumentationSchema[] nativeSchemas = new PgoInstrumentationSchema[pgoResultsSchemas.Length]; - MemoryStream msInstrumentationData = new MemoryStream(); - BinaryWriter bwInstrumentationData = new BinaryWriter(msInstrumentationData); - for (int i = 0; i < nativeSchemas.Length; i++) - { - if ((bwInstrumentationData.BaseStream.Position % 8) == 4) - { - bwInstrumentationData.Write(0); - } - - Debug.Assert((bwInstrumentationData.BaseStream.Position % 8) == 0); - nativeSchemas[i].Offset = new IntPtr(checked((int)bwInstrumentationData.BaseStream.Position)); - nativeSchemas[i].ILOffset = pgoResultsSchemas[i].ILOffset; - nativeSchemas[i].Count = pgoResultsSchemas[i].Count; - nativeSchemas[i].Other = pgoResultsSchemas[i].Other; - nativeSchemas[i].InstrumentationKind = (PgoInstrumentationKind)pgoResultsSchemas[i].InstrumentationKind; - - if (pgoResultsSchemas[i].DataObject == null) - { - bwInstrumentationData.Write(pgoResultsSchemas[i].DataLong); - } - else - { - object dataObject = pgoResultsSchemas[i].DataObject; - if (dataObject is int[] intArray) - { - foreach (int intVal in intArray) - bwInstrumentationData.Write(intVal); - } - else if (dataObject is long[] longArray) - { - foreach (long longVal in longArray) - bwInstrumentationData.Write(longVal); - } - else if (dataObject is TypeSystemEntityOrUnknown[] typeArray) - { - foreach (TypeSystemEntityOrUnknown typeVal in typeArray) - { - IntPtr ptrVal = IntPtr.Zero; - if (typeVal.AsType != null) - ptrVal = (IntPtr)ObjectToHandle(typeVal.AsType); - else - { - // The "Unknown types are the values from 1-33 - ptrVal = new IntPtr((typeVal.AsUnknown % 32) + 1); - } - - if (IntPtr.Size == 4) - bwInstrumentationData.Write((int)ptrVal); - else - bwInstrumentationData.Write((long)ptrVal); - } - } - } - } - - bwInstrumentationData.Flush(); - pgoResults.pInstrumentationData = (byte*)GetPin(msInstrumentationData.ToArray()); - pgoResults.countSchemaItems = (uint)nativeSchemas.Length; - pgoResults.pSchema = (PgoInstrumentationSchema*)GetPin(nativeSchemas); - pgoResults.hr = HRESULT.S_OK; - } - - _pgoResults.Add(methodDesc, pgoResults); - } - - pSchema = pgoResults.pSchema; - countSchemaItems = pgoResults.countSchemaItems; - *pInstrumentationData = pgoResults.pInstrumentationData; - return pgoResults.hr; - } - private CORINFO_CLASS_STRUCT_* getLikelyClass(CORINFO_METHOD_STRUCT_* ftnHnd, CORINFO_CLASS_STRUCT_* baseHnd, uint IlOffset, ref uint pLikelihood, ref uint pNumberOfClasses) { return null; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs index 0b132c34b12d9..d07b5496a970a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs @@ -61,12 +61,20 @@ void EnsurePgoData() { if (_pgoData == null) { - var compressedIntParser = new PgoProcessor.PgoEncodedCompressedIntParser(Image, Offset); + if (Image == null) + { + _pgoData = Array.Empty(); + _size = 0; + } + else + { + var compressedIntParser = new PgoProcessor.PgoEncodedCompressedIntParser(Image, Offset); - SignatureFormattingOptions formattingOptions = new SignatureFormattingOptions(); + SignatureFormattingOptions formattingOptions = new SignatureFormattingOptions(); - _pgoData = PgoProcessor.ParsePgoData(new PgoDataLoader(_r2rReader, formattingOptions), compressedIntParser, true).ToArray(); - _size = compressedIntParser.Offset - Offset; + _pgoData = PgoProcessor.ParsePgoData(new PgoDataLoader(_r2rReader, formattingOptions), compressedIntParser, true).ToArray(); + _size = compressedIntParser.Offset - Offset; + } } } @@ -100,9 +108,12 @@ public override string ToString() } else { - foreach (object o in elem.DataObject) + if (elem.DataObject != null) { - sb.AppendLine(o.ToString()); + foreach (object o in elem.DataObject) + { + sb.AppendLine(o.ToString()); + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 12bbd06e51bfb..f727ed872e03f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -925,7 +925,11 @@ public IEnumerable AllPgoInfos get { EnsureMethods(); - return _pgoInfos.Values; + + if (_pgoInfos != null) + return _pgoInfos.Values; + else + return Array.Empty(); } } diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CastingTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CastingTests.cs index 06e87dc17be26..f6cd2e187d46c 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CastingTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun.Tests/CastingTests.cs @@ -79,7 +79,7 @@ public void TestSameSizeArrayTypeCasting() Assert.True(byteType.MakeArrayType().CanCastTo(sbyteType.MakeArrayType())); Assert.False(byteType.CanCastTo(sbyteType)); - Assert.True(intPtrType.MakeArrayType().CanCastTo(ulongType.MakeArrayType())); + Assert.False(intPtrType.MakeArrayType().CanCastTo(ulongType.MakeArrayType())); Assert.False(intPtrType.CanCastTo(ulongType)); // These are same size, but not allowed to cast diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj index c8e83d94bf848..580438dcc29d2 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj @@ -146,6 +146,12 @@ TypeSystem\Common\ModuleDesc.cs + + TypeSystem\Common\NotFoundBehavior.cs + + + TypeSystem\Common\ResolutionFailure.cs + TypeSystem\Common\TypeSystemEntity.cs diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 64f75116d6510..1a13309de32cc 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -23,6 +23,7 @@ internal class Program private CommandLineOptions _commandLineOptions; public TargetOS _targetOS; public TargetArchitecture _targetArchitecture; + private bool _armelAbi = false; public OptimizationMode _optimizationMode; private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); private Dictionary _unrootedInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -167,7 +168,10 @@ private void ConfigureTarget() else if (_commandLineOptions.TargetArch.Equals("arm", StringComparison.OrdinalIgnoreCase)) _targetArchitecture = TargetArchitecture.ARM; else if (_commandLineOptions.TargetArch.Equals("armel", StringComparison.OrdinalIgnoreCase)) + { _targetArchitecture = TargetArchitecture.ARM; + _armelAbi = true; + } else if (_commandLineOptions.TargetArch.Equals("arm64", StringComparison.OrdinalIgnoreCase)) _targetArchitecture = TargetArchitecture.ARM64; else @@ -316,7 +320,7 @@ private int Run(string[] args) SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector()); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.CoreRTArmel : TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector()); bool versionBubbleIncludesCoreLib = false; if (_commandLineOptions.InputBubble) @@ -628,6 +632,8 @@ private int Run(string[] args) if (_commandLineOptions.DgmlLogFileName != null) compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName); + + compilation.Dispose(); } return 0; diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.csproj b/src/coreclr/tools/aot/crossgen2/crossgen2.csproj index 87e123adb1d34..7a225efcd0c6e 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.csproj +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.csproj @@ -61,10 +61,14 @@ unix win - $(TargetOSComponent)_$(TargetArchitecture)_$(TargetArchitecture) - $(TargetOSComponent)_$(TargetArchitecture)_$(CrossHostArch) - $(LibPrefix)jitinterface_$(TargetArchitecture)$(LibSuffix) + $(TargetArchitecture) + arm + + $(TargetOSComponent)_$(TargetArchitectureForSharedLibraries)_$(TargetArchitectureForSharedLibraries) + $(TargetOSComponent)_$(TargetArchitectureForSharedLibraries)_$(CrossHostArch) + + $(LibPrefix)jitinterface_$(TargetArchitectureForSharedLibraries)$(LibSuffix) @@ -72,13 +76,13 @@ CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Link="%(FileName)%(Extension)" - /> + /> - + /> + $(CrossHostArch) + amd64 + Microsoft.DiaSymReader.Native.$(DiaSymReaderCrossArch).dll + $(PkgMicrosoft_DiaSymReader_Native)\runtimes\win\native\$(DiaSymReaderCrossArchFileName) + + + $(TargetArchitecture) + amd64 + Microsoft.DiaSymReader.Native.$(DiaSymReaderTargetArch).dll + $(PkgMicrosoft_DiaSymReader_Native)\runtimes\win\native\$(DiaSymReaderTargetArchFileName) + + + + + + $(RuntimeBinDir)$(CrossHostArch)\crossgen2 - + + + + + + diff --git a/src/coreclr/tools/aot/jitinterface/CMakeLists.txt b/src/coreclr/tools/aot/jitinterface/CMakeLists.txt index 9b7640e1a49b8..0b1cdc33fd591 100644 --- a/src/coreclr/tools/aot/jitinterface/CMakeLists.txt +++ b/src/coreclr/tools/aot/jitinterface/CMakeLists.txt @@ -17,4 +17,5 @@ add_library_clr(jitinterface_${ARCH_HOST_NAME} ${JITINTERFACE_RESOURCES} ) -install_clr(TARGETS jitinterface_${ARCH_HOST_NAME}) +install_clr(TARGETS jitinterface_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT jit) +install_clr(TARGETS jitinterface_${ARCH_HOST_NAME} DESTINATIONS . COMPONENT alljits) diff --git a/src/coreclr/tools/crossgen/CMakeLists.txt b/src/coreclr/tools/crossgen/CMakeLists.txt index eff97b01a882c..6b998ebb9e1ad 100644 --- a/src/coreclr/tools/crossgen/CMakeLists.txt +++ b/src/coreclr/tools/crossgen/CMakeLists.txt @@ -80,4 +80,4 @@ add_subdirectory(../../zap ../../zap) add_subdirectory(../../vm/crossgen ../../vm/crossgen) # add the install targets -install_clr(TARGETS crossgen ADDITIONAL_DESTINATIONS sharedFramework) +install_clr(TARGETS crossgen DESTINATIONS . sharedFramework COMPONENT runtime) diff --git a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs index ecf68576a8403..70a8b2d081d90 100644 --- a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs +++ b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs @@ -38,6 +38,8 @@ internal class CommandLineOptions public bool DetailedProgressMessages; public List InputFilesToMerge; public List IncludedAssemblies = new List(); + public bool DumpMibc = false; + public FileInfo InputFileToDump; public string[] HelpArgs = Array.Empty(); @@ -259,6 +261,26 @@ void HelpOption() #endif } + var dumpCommand = syntax.DefineCommand(name: "dump", value: ref command, help: "Dump the contents of a Mibc file."); + if (dumpCommand.IsActive) + { + DumpMibc = true; + HelpArgs = new string[] { "dump", "--help", "input", "output" }; + + VerbosityOption(); + HelpOption(); + + string inputFileToDump = null; + syntax.DefineParameter(name: "input", ref inputFileToDump, "Name of the input mibc file to dump."); + if (inputFileToDump != null) + InputFileToDump = new FileInfo(inputFileToDump); + + string outputFile = null; + syntax.DefineParameter(name: "output", ref outputFile, "Name of the output dump file."); + if (outputFile != null) + OutputFileName = new FileInfo(outputFile); + } + if (syntax.ActiveCommand == null) { @@ -341,7 +363,7 @@ private CommandLineOptions() private void ParseCommmandLineHelper(string[] args) { ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, DefineArgumentSyntax); - if (Help || (!FileType.HasValue && (InputFilesToMerge == null))) + if (Help || (!FileType.HasValue && (InputFilesToMerge == null) && !DumpMibc)) { Help = true; } diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index e097a9a1c312e..86448cfb57cbe 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -28,6 +28,9 @@ using System.Reflection.PortableExecutable; using ILCompiler.IBC; using ILCompiler; +using System.Runtime.Serialization.Json; +using System.Text.Json; +using System.Text.Encodings.Web; namespace Microsoft.Diagnostics.Tools.Pgo { @@ -241,6 +244,10 @@ static int InnerMain(CommandLineOptions commandLineOptions) if (!commandLineOptions.DetailedProgressMessages) s_logger.HideDetailedMessages(); + if (commandLineOptions.DumpMibc) + { + return InnerDumpMain(commandLineOptions); + } if (commandLineOptions.InputFilesToMerge != null) { return InnerMergeMain(commandLineOptions); @@ -251,6 +258,103 @@ static int InnerMain(CommandLineOptions commandLineOptions) } } + static int InnerDumpMain(CommandLineOptions commandLineOptions) + { + if ((commandLineOptions.InputFileToDump == null) || (!commandLineOptions.InputFileToDump.Exists)) + { + PrintUsage(commandLineOptions, "Valid input file must be specified"); + return -8; + } + + if (commandLineOptions.OutputFileName == null) + { + PrintUsage(commandLineOptions, "Output filename must be specified"); + return -8; + } + + PrintDetailedMessage($"Opening {commandLineOptions.InputFileToDump}"); + var mibcPeReader = MIbcProfileParser.OpenMibcAsPEReader(commandLineOptions.InputFileToDump.FullName); + var tsc = new TypeRefTypeSystem.TypeRefTypeSystemContext(new PEReader[] { mibcPeReader }); + + PrintDetailedMessage($"Parsing {commandLineOptions.InputFileToDump}"); + var profileData = MIbcProfileParser.ParseMIbcFile(tsc, mibcPeReader, null, onlyDefinedInAssembly: null); + + using (FileStream outputFile = new FileStream(commandLineOptions.OutputFileName.FullName, FileMode.Create, FileAccess.Write)) + { + JsonWriterOptions options = new JsonWriterOptions(); + options.Indented = true; + options.SkipValidation = false; + options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; + + using Utf8JsonWriter jsonWriter = new Utf8JsonWriter(outputFile, options); + jsonWriter.WriteStartObject(); + jsonWriter.WriteStartArray("Methods"); + foreach (MethodProfileData data in profileData.GetAllMethodProfileData()) + { + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("Method", data.Method.ToString()); + if (data.CallWeights != null) + { + jsonWriter.WriteStartArray("CallWeights"); + foreach (var callWeight in data.CallWeights) + { + jsonWriter.WriteString("Method", callWeight.Key.ToString()); + jsonWriter.WriteNumber("Weight", callWeight.Value); + } + jsonWriter.WriteEndArray(); + } + if (data.ExclusiveWeight != 0) + { + jsonWriter.WriteNumber("ExclusiveWeight", data.ExclusiveWeight); + } + if (data.SchemaData != null) + { + jsonWriter.WriteStartArray("InstrumentationData"); + foreach (var schemaElem in data.SchemaData) + { + jsonWriter.WriteStartObject(); + jsonWriter.WriteNumber("ILOffset", schemaElem.ILOffset); + jsonWriter.WriteString("InstrumentationKind", schemaElem.InstrumentationKind.ToString()); + jsonWriter.WriteNumber("Other", schemaElem.Other); + if (schemaElem.DataHeldInDataLong) + { + jsonWriter.WriteNumber("Data", schemaElem.DataLong); + } + else + { + if (schemaElem.DataObject == null) + { + // No data associated with this item + } + else if (schemaElem.DataObject.Length == 1) + { + jsonWriter.WriteString("Data", schemaElem.DataObject.GetValue(0).ToString()); + } + else + { + jsonWriter.WriteStartArray("Data"); + foreach (var dataElem in schemaElem.DataObject) + { + jsonWriter.WriteStringValue(dataElem.ToString()); + } + jsonWriter.WriteEndArray(); + } + } + jsonWriter.WriteEndObject(); + } + jsonWriter.WriteEndArray(); + } + + jsonWriter.WriteEndObject(); + } + jsonWriter.WriteEndArray(); + jsonWriter.WriteEndObject(); + } + PrintMessage($"Generated {commandLineOptions.OutputFileName}"); + + return 0; + } + static int InnerMergeMain(CommandLineOptions commandLineOptions) { diff --git a/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs b/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs index 0bc52929256b9..a640425f209b4 100644 --- a/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs +++ b/src/coreclr/tools/dotnet-pgo/R2RSignatureTypeProvider.cs @@ -97,7 +97,11 @@ MethodDesc IR2RSignatureTypeProvider.GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, TypeDesc owningTypeOverride) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - var method = (MethodDesc)ecmaModule.GetObject(handle); + var method = (MethodDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); + if (method == null) + { + return null; + } if (owningTypeOverride != null) { return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride); @@ -108,7 +112,11 @@ MethodDesc IR2RSignatureTypeProvider.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - var method = (MethodDesc)ecmaModule.GetObject(handle); + var method = (MethodDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); + if (method == null) + { + return null; + } if (owningTypeOverride != null) { return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride); @@ -214,19 +222,19 @@ TypeDesc ISZArrayTypeProvider.GetSZArrayType(TypeDesc elementType) TypeDesc ISimpleTypeProvider.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - return (TypeDesc)ecmaModule.GetObject(handle); + return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } TypeDesc ISimpleTypeProvider.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - return (TypeDesc)ecmaModule.GetObject(handle); + return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } TypeDesc ISignatureTypeProvider.GetTypeFromSpecification(MetadataReader reader, R2RSigProviderContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind) { var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name)); - return (TypeDesc)ecmaModule.GetObject(handle); + return (TypeDesc)ecmaModule.GetObject(handle, NotFoundBehavior.ReturnNull); } } } diff --git a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs index 1cb1138a49293..bb32521c0b750 100644 --- a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs +++ b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemContext.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using System.Text; @@ -120,7 +121,7 @@ public TypeRefTypeSystemContext(IEnumerable refReaders) { TypeRefSignatureParserProvider parserHelper = new TypeRefSignatureParserProvider(this, peInfo.handleLookup); - Func resolverFunc = ResolveTypeRefForPeInfo; + Func resolverFunc = ResolveTypeRefForPeInfo; int memberRefRowCount = peInfo.reader.GetTableRowCount(TableIndex.MemberRef); for (int row = 1; row <= memberRefRowCount; row++) { @@ -142,7 +143,7 @@ public TypeRefTypeSystemContext(IEnumerable refReaders) continue; } - EcmaSignatureParser ecmaSigParse = new EcmaSignatureParser(this, ResolveTypeRefForPeInfo, peInfo.reader.GetBlobReader(memberRef.Signature)); + EcmaSignatureParser ecmaSigParse = new EcmaSignatureParser(this, ResolveTypeRefForPeInfo, peInfo.reader.GetBlobReader(memberRef.Signature), NotFoundBehavior.ReturnNull); string name = peInfo.reader.GetString(memberRef.Name); if (memberRef.GetKind() == MemberReferenceKind.Method) @@ -157,8 +158,9 @@ public TypeRefTypeSystemContext(IEnumerable refReaders) } } - TypeDesc ResolveTypeRefForPeInfo(EntityHandle handle) + TypeDesc ResolveTypeRefForPeInfo(EntityHandle handle, NotFoundBehavior notFoundBehavior) { + Debug.Assert(notFoundBehavior == NotFoundBehavior.ReturnNull); TypeRefTypeSystemType type = null; if (handle.Kind == HandleKind.TypeReference) { diff --git a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs index 45529383eeab7..d057112766cca 100644 --- a/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs +++ b/src/coreclr/tools/dotnet-pgo/TypeRefTypeSystem/TypeRefTypeSystemModule.cs @@ -71,11 +71,16 @@ private TypeRefTypeSystemType GetTypeInternal(string nameSpace, string name) return type; } - public override MetadataType GetType(string nameSpace, string name, bool throwIfNotFound = true) + public override MetadataType GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) { MetadataType type = GetTypeInternal(nameSpace, name); - if ((type == null) && throwIfNotFound) - ThrowHelper.ThrowTypeLoadException(nameSpace, name, this); + if ((type == null) && notFoundBehavior != NotFoundBehavior.ReturnNull) + { + ResolutionFailure failure = ResolutionFailure.GetTypeLoadResolutionFailure(nameSpace, name, this); + ModuleDesc.GetTypeResolutionFailure = failure; + if (notFoundBehavior == NotFoundBehavior.Throw) + failure.Throw(); + } return type; } } diff --git a/src/coreclr/tools/r2rdump/CoreDisTools.cs b/src/coreclr/tools/r2rdump/CoreDisTools.cs index a5c5a7578fcb2..75029e04a163c 100644 --- a/src/coreclr/tools/r2rdump/CoreDisTools.cs +++ b/src/coreclr/tools/r2rdump/CoreDisTools.cs @@ -506,7 +506,7 @@ private void ProbeCommonIntelQuirks(RuntimeFunction rtf, int imageOffset, int rt if (pointsOutsideRuntimeFunction && IsIntel2ByteIndirectJumpPCRelativeInstruction(targetImageOffset, out int instructionRelativeOffset)) { int thunkTargetRVA = targetRVA + instructionRelativeOffset; - bool haveImportCell = TryGetImportCellName(thunkTargetRVA, out string importCellName); ; + bool haveImportCell = TryGetImportCellName(thunkTargetRVA, out string importCellName); if (_options.Naked && haveImportCell) { diff --git a/src/coreclr/tools/r2rdump/TextDumper.cs b/src/coreclr/tools/r2rdump/TextDumper.cs index f2fec941c6c25..613c0bb96eadb 100644 --- a/src/coreclr/tools/r2rdump/TextDumper.cs +++ b/src/coreclr/tools/r2rdump/TextDumper.cs @@ -18,6 +18,8 @@ namespace R2RDump { class TextDumper : Dumper { + private const int GuidByteSize = 16; + public TextDumper(ReadyToRunReader r2r, TextWriter writer, Disassembler disassembler, DumpOptions options) : base(r2r, writer, disassembler, options) { @@ -88,7 +90,14 @@ internal override void DumpHeader(bool dumpSections) int assemblyIndex = 0; foreach (string assemblyName in _r2r.ManifestReferenceAssemblies.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key)) { - WriteDivider($@"Component Assembly [{assemblyIndex}]: {assemblyName}"); + string dividerName = $@"Component Assembly [{assemblyIndex}]: {assemblyName}"; + if (_r2r.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.ManifestAssemblyMvids, out ReadyToRunSection mvidSection)) + { + int mvidOffset = _r2r.GetOffset(mvidSection.RelativeVirtualAddress) + GuidByteSize * assemblyIndex; + Guid mvid = new Guid(new ReadOnlySpan(_r2r.Image, mvidOffset, GuidByteSize)); + dividerName += $@" - MVID {mvid:b}"; + } + WriteDivider(dividerName); ReadyToRunCoreHeader assemblyHeader = _r2r.ReadyToRunAssemblyHeaders[assemblyIndex]; foreach (ReadyToRunSection section in NormalizedSections(assemblyHeader)) { @@ -141,9 +150,12 @@ internal override void DumpAllMethods() if (_options.Pgo) { - foreach (PgoInfo info in _r2r.AllPgoInfos) + if (_r2r != null) { - pgoEntriesNotDumped.Add(info.Key); + foreach (PgoInfo info in _r2r.AllPgoInfos) + { + pgoEntriesNotDumped.Add(info.Key); + } } } foreach (ReadyToRunMethod method in NormalizedMethods()) @@ -500,6 +512,18 @@ internal override void DumpSectionContents(ReadyToRunSection section) string ownerCompositeExecutable = Encoding.UTF8.GetString(_r2r.Image, oceOffset, section.Size - 1); // exclude the zero terminator _writer.WriteLine("Composite executable: {0}", ownerCompositeExecutable.ToEscapedString()); break; + case ReadyToRunSectionType.ManifestAssemblyMvids: + int mvidOffset = _r2r.GetOffset(section.RelativeVirtualAddress); + int mvidCount = section.Size / GuidByteSize; + for (int mvidIndex = 0; mvidIndex < mvidCount; mvidIndex++) + { + Guid mvid = new Guid(new Span(_r2r.Image, mvidOffset + GuidByteSize * mvidIndex, GuidByteSize)); + _writer.WriteLine("MVID[{0}] = {1:b}", mvidIndex, mvid); + } + break; + default: + _writer.WriteLine("Unsupported section type {0}", section.Type); + break; } } diff --git a/src/coreclr/tools/r2rtest/CommandLineOptions.cs b/src/coreclr/tools/r2rtest/CommandLineOptions.cs index adae37ecf5dee..ebadfd8fcdb45 100644 --- a/src/coreclr/tools/r2rtest/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rtest/CommandLineOptions.cs @@ -269,7 +269,7 @@ Option ExecutionTimeoutMinutes() => new Option(new[] { "--execution-timeout-minutes", "-et" }, "Execution timeout (minutes)"); Option R2RDumpPath() => - new Option(new[] { "--r2r-dump-path", "-r2r" }, "Path to R2RDump.exe/dll").ExistingOnly();; + new Option(new[] { "--r2r-dump-path", "-r2r" }, "Path to R2RDump.exe/dll").ExistingOnly(); Option MeasurePerf() => new Option(new[] { "--measure-perf" }, "Print out compilation time"); @@ -290,7 +290,7 @@ Option TargetArch() => // compile-nuget specific options // Option PackageList() => - new Option(new[] { "--package-list", "-pl" }, "Text file containing a package name on each line").ExistingOnly();; + new Option(new[] { "--package-list", "-pl" }, "Text file containing a package name on each line").ExistingOnly(); // // compile-serp specific options diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt index c7c5861f129b4..af1187a59b2aa 100644 --- a/src/coreclr/utilcode/CMakeLists.txt +++ b/src/coreclr/utilcode/CMakeLists.txt @@ -20,7 +20,6 @@ set(UTILCODE_COMMON_SOURCES stgpooli.cpp stgpoolreadonly.cpp utsem.cpp - peinformation.cpp check.cpp log.cpp arraylist.cpp @@ -36,7 +35,6 @@ set(UTILCODE_COMMON_SOURCES corimage.cpp format1.cpp prettyprintsig.cpp - regutil.cpp sha1.cpp sigbuilder.cpp sigparser.cpp diff --git a/src/coreclr/utilcode/ccomprc.cpp b/src/coreclr/utilcode/ccomprc.cpp index 78f7f56e0bd27..134a52836deba 100644 --- a/src/coreclr/utilcode/ccomprc.cpp +++ b/src/coreclr/utilcode/ccomprc.cpp @@ -489,6 +489,9 @@ HRESULT CCompRC::LoadString(ResourceCategory eCategory, UINT iResourceID, __out_ HRESULT CCompRC::LoadString(ResourceCategory eCategory, LocaleID langId, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed) { +#ifdef DBI_COMPONENT_MONO + return E_NOTIMPL; +#else CONTRACTL { GC_NOTRIGGER; @@ -535,6 +538,7 @@ HRESULT CCompRC::LoadString(ResourceCategory eCategory, LocaleID langId, UINT iR return LoadNativeStringResource(NATIVE_STRING_RESOURCE_TABLE(NATIVE_STRING_RESOURCE_NAME), iResourceID, szBuffer, iMax, pcwchUsed); #endif // HOST_WINDOWS +#endif } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/utilcode/clrconfig.cpp b/src/coreclr/utilcode/clrconfig.cpp index 5142e395ac0ec..f21f49ed8d105 100644 --- a/src/coreclr/utilcode/clrconfig.cpp +++ b/src/coreclr/utilcode/clrconfig.cpp @@ -4,18 +4,356 @@ // CLRConfig.cpp // -// -// Unified method of accessing configuration values from environment variables, -// registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values. -// -//***************************************************************************** - #include "stdafx.h" #include "clrconfig.h" +#include "sstring.h" +#include "ex.h" + +// Config prefixes +#define COMPLUS_PREFIX W("COMPlus_") +#define LEN_OF_COMPLUS_PREFIX StrLen(COMPLUS_PREFIX) + +#define DOTNET_PREFIX W("DOTNET_") +#define LEN_OF_DOTNET_PREFIX StrLen(DOTNET_PREFIX) + +using ConfigDWORDInfo = CLRConfig::ConfigDWORDInfo; +using ConfigStringInfo = CLRConfig::ConfigStringInfo; +using LookupOptions = CLRConfig::LookupOptions; + +namespace +{ + // + // ProbabilisticNameSet: + // + // (Used by ConfigCache, below. If used elsewhere, might justify + // promotion to a standalone header file.) + // + // Represent a set of names in a small, fixed amount of storage. + // We turn a name into a small integer, then add the integer to a bitvector. + // An old trick we used in VC++4 minimal rebuild. + // + // For best results, the number of elements should be a fraction of + // the total number of bits in 'bits'. + // + // Note, only the const methods are thread-safe. + // Callers are responsible for providing their own synchronization when + // constructing and Add'ing names to the set. + // + class ProbabilisticNameSet { + public: + ProbabilisticNameSet() + { + WRAPPER_NO_CONTRACT; + + memset(bits, 0, sizeof(bits)); + } + + // Add a name to the set. + // + void Add(LPCWSTR name) + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, 0, &i, &mask); + bits[i] |= mask; + } + + void Add(LPCWSTR name, DWORD count) + { + WRAPPER_NO_CONTRACT; + + unsigned i, mask; + GetBitIndex(name, count, &i, &mask); + bits[i] |= mask; + } + + // Return TRUE if a name *may have* been added to the set; + // return FALSE if the name *definitely* was NOT ever added to the set. + // + bool MayContain(LPCWSTR name) const + { + WRAPPER_NO_CONTRACT; -#ifndef ERANGE -#define ERANGE 34 -#endif + unsigned i, mask; + GetBitIndex(name, 0, &i, &mask); + return !!(bits[i] & mask); + } + + private: + static const unsigned cbitSet = 256U; + static const unsigned cbitWord = 8U*sizeof(unsigned); + unsigned bits[cbitSet/cbitWord]; + + // Return the word index and bit mask corresponding to the bitvector member + // addressed by the *case-insensitive* hash of the given name. + // + void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const + { + LIMITED_METHOD_CONTRACT; + unsigned hash; + if (count > 0) + hash = HashiStringNKnownLower80(name, count) % cbitSet; + else + hash = HashiStringKnownLower80(name) % cbitSet; + *pi = hash / cbitWord; + *pmask = (1U << (hash % cbitWord)); + } + }; + + bool s_fUseEnvCache = false; + ProbabilisticNameSet s_EnvNames; // set of environment value names seen + + bool EnvCacheValueNameSeenPerhaps(LPCWSTR name) + { + WRAPPER_NO_CONTRACT; + + return !s_fUseEnvCache + || s_EnvNames.MayContain(name); + } + + bool CheckLookupOption(const ConfigDWORDInfo & info, LookupOptions option) + { + LIMITED_METHOD_CONTRACT; + return ((info.options & option) == option); + } + + bool CheckLookupOption(const ConfigStringInfo & info, LookupOptions option) + { + LIMITED_METHOD_CONTRACT; + return ((info.options & option) == option); + } + + bool CheckLookupOption(LookupOptions infoOptions, LookupOptions optionToCheck) + { + LIMITED_METHOD_CONTRACT; + return ((infoOptions & optionToCheck) == optionToCheck); + } + + //***************************************************************************** + // Reads from the environment setting + //***************************************************************************** + LPWSTR EnvGetString( + LPCWSTR name, + LookupOptions options) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + WCHAR buff[64]; + const WCHAR* fallbackPrefix = NULL; + const size_t namelen = wcslen(name); + + bool noPrefix = CheckLookupOption(options, LookupOptions::DontPrependPrefix); + if (noPrefix) + { + if (namelen >= _countof(buff)) + { + _ASSERTE(!"Environment variable name too long."); + return NULL; + } + + *buff = W('\0'); + } + else + { + bool dotnetValid = namelen < (size_t)(_countof(buff) - 1 - LEN_OF_DOTNET_PREFIX); + bool complusValid = namelen < (size_t)(_countof(buff) - 1 - LEN_OF_COMPLUS_PREFIX); + if(!dotnetValid || !complusValid) + { + _ASSERTE(!"Environment variable name too long."); + return NULL; + } + + // Check if the name has been cached. + if (!EnvCacheValueNameSeenPerhaps(name)) + return NULL; + + // Priority order is DOTNET_ and then COMPlus_. + wcscpy_s(buff, _countof(buff), DOTNET_PREFIX); + fallbackPrefix = COMPLUS_PREFIX; + } + + wcscat_s(buff, _countof(buff), name); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + NewArrayHolder ret = NULL; + HRESULT hr = S_OK; + EX_TRY + { + PathString temp; + + DWORD len = WszGetEnvironmentVariable(buff, temp); + if (len == 0 && fallbackPrefix != NULL) + { + wcscpy_s(buff, _countof(buff), fallbackPrefix); + wcscat_s(buff, _countof(buff), name); + len = WszGetEnvironmentVariable(buff, temp); + } + + if (len != 0) + ret = temp.GetCopyOfUnicodeString(); + } + EX_CATCH_HRESULT(hr); + + if (hr != S_OK) + { + SetLastError(hr); + } + + if(ret != NULL) + return ret.Extract(); + + return NULL; + } + + HRESULT GetConfigDWORD( + LPCWSTR name, + DWORD defValue, + __out DWORD *result, + LookupOptions options) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + CANNOT_TAKE_LOCK; + } + CONTRACTL_END; + + SUPPORTS_DAC_HOST_ONLY; + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + NewArrayHolder val = EnvGetString(name, options); + if (val != NULL) + { + errno = 0; + LPWSTR endPtr; + DWORD configMaybe = wcstoul(val, &endPtr, 16); // treat it has hex + BOOL fSuccess = ((errno != ERANGE) && (endPtr != val)); + if (fSuccess) + { + *result = configMaybe; + return (S_OK); + } + } + + *result = defValue; + return (E_FAIL); + } + + LPWSTR GetConfigString( + LPCWSTR name, + LookupOptions options) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END; + + NewArrayHolder ret(NULL); + + FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. + + ret = EnvGetString(name, options); + if (ret != NULL) + { + if (*ret != W('\0')) + { + ret.SuppressRelease(); + return(ret); + } + ret.Clear(); + } + + return NULL; + } + + //--------------------------------------------------------------------------------------- + // + // Given an input string, returns a newly-allocated string equal to the input but with + // leading and trailing whitespace trimmed off. If input is already trimmed, or if + // trimming would result in an empty string, this function sets the output string to NULL + // + // Caller must free *pwszTrimmed if non-NULL + // + // Arguments: + // * wszOrig - String to trim + // * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or + // NULL) + // + // Return Value: + // HRESULT indicating success or failure. + // + HRESULT TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + _ASSERTE(wszOrig != NULL); + _ASSERTE(pwszTrimmed != NULL); + + // In case we return early, set [out] to NULL by default + *pwszTrimmed = NULL; + + // Get pointers into internal string that show where to do the trimming. + size_t cchOrig = wcslen(wszOrig); + if (!FitsIn(cchOrig)) + return COR_E_OVERFLOW; + DWORD cchAfterTrim = (DWORD) cchOrig; + LPCWSTR wszAfterTrim = wszOrig; + ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim); + + // Is input string already trimmed? If so, save an allocation and just return. + if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim)) + { + // Yup, just return success + return S_OK; + } + + if (cchAfterTrim == 0) + { + // After trimming, there's nothing left, so just return NULL + return S_OK; + } + + // Create a new buffer to hold a copy of the trimmed string. Caller will be + // responsible for this buffer if we return it. + NewArrayHolder wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]); + if (wszTrimmedCopy == NULL) + { + return E_OUTOFMEMORY; + } + + errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim); + if (err != 0) + { + return E_FAIL; + } + + // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for + // deleting it. + wszTrimmedCopy.SuppressRelease(); + *pwszTrimmed = wszTrimmedCopy; + return S_OK; + } +} // // Creating structs using the macro table in CLRConfigValues.h @@ -23,46 +361,32 @@ // These macros intialize ConfigDWORDInfo structs. #define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \ - const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::LookupOptions::Default}; #define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions}; // These macros intialize ConfigStringInfo structs. #define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \ - const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::LookupOptions::Default}; #define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; - -// TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to -// CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in -// file:../utilcode/CLRConfig.h) should be removed. -#define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; -#define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; // // Debug versions of the macros // #ifdef _DEBUG #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \ - const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::LookupOptions::Default}; #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions}; #define CONFIG_STRING_INFO(symbol, name, description) \ - const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default}; + const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::LookupOptions::Default}; #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions}; - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \ - const LPCWSTR CLRConfig::symbol = name; #else #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) #define CONFIG_STRING_INFO(symbol, name, description) #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) - #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) - #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) #endif // _DEBUG // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code. @@ -72,15 +396,10 @@ #undef RETAIL_CONFIG_STRING_INFO #undef RETAIL_CONFIG_DWORD_INFO_EX #undef RETAIL_CONFIG_STRING_INFO_EX -#undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS -#undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS #undef CONFIG_DWORD_INFO #undef CONFIG_STRING_INFO #undef CONFIG_DWORD_INFO_EX #undef CONFIG_STRING_INFO_EX -#undef CONFIG_DWORD_INFO_DIRECT_ACCESS -#undef CONFIG_STRING_INFO_DIRECT_ACCESS - // // Look up a DWORD config value. @@ -88,19 +407,13 @@ // Arguments: // * info - see file:../inc/CLRConfig.h for details. // -// * useDefaultIfNotSet - if true, fall back to the default value if the value is not set. -// -// * acceptExplicitDefaultFromRegutil - if false, only accept a value returned by REGUTIL if it is -// different from the default value. This parameter is useful as a way to preserve existing -// behavior. -// // * result - the result. // // Return value: // * true for success, false otherwise. // // static -DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault) +DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, /* [Out] */ bool *isDefault) { CONTRACTL { @@ -112,41 +425,39 @@ DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplici _ASSERTE (isDefault != nullptr); - - // - // Set up REGUTIL options. - // - REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); - BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_); - DWORD resultMaybe; - HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus); + HRESULT hr = GetConfigDWORD(info.name, info.defaultValue, &resultMaybe, info.options); - if (!acceptExplicitDefaultFromRegutil) - { - // Ignore the default value even if it's set explicitly. - if (resultMaybe != info.defaultValue) - { - *isDefault = false; - return resultMaybe; - } - } - else + // Ignore the default value even if it's set explicitly. + if (resultMaybe != info.defaultValue) { - // If we are willing to accept the default value when it's set explicitly, - // checking the HRESULT here is sufficient. E_FAIL is returned when the - // default is used. - if (SUCCEEDED(hr)) - { - *isDefault = false; - return resultMaybe; - } + *isDefault = false; + return resultMaybe; } *isDefault = true; return info.defaultValue; } +// +// Look up a DWORD config value. +// +// Arguments: +// * info - see file:../inc/CLRConfig.h for details +// +// static +DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, DWORD defaultValue) +{ + bool isDefault = false; + DWORD valueMaybe = GetConfigValue(info, &isDefault); + + // If the default value was returned, defer to the user supplied version. + if (isDefault) + return defaultValue; + + return valueMaybe; +} + // // Look up a DWORD config value. // @@ -156,10 +467,8 @@ DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplici // static DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info) { - // We pass false for 'acceptExplicitDefaultFromRegutil' to maintain the existing behavior of this function. - // Callers who don't need that behavior should switch to the other version of this function and pass true. bool unused; - return GetConfigValue(info, false /* acceptExplicitDefaultFromRegutil */, &unused); + return GetConfigValue(info, &unused); } // @@ -217,16 +526,8 @@ HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z L LPWSTR result = NULL; - - // - // Set up REGUTIL options. - // - REGUTIL::CORConfigLevel level = GetConfigLevel(info.options); - BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_); - - result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level); - - if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue)) + result = GetConfigString(info.name, info.options); + if ((result != NULL) && CheckLookupOption(info, LookupOptions::TrimWhiteSpaceFromStringValue)) { // If this fails, result remains untouched, so we'll just return the untrimmed // value. @@ -261,46 +562,44 @@ BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name) } CONTRACTL_END; - // Check REGUTIL, both with and without the COMPlus_ prefix { LPWSTR result = NULL; - result = REGUTIL::GetConfigString_DontUse_(name, TRUE); + result = GetConfigString(name, LookupOptions::Default); if (result != NULL) { FreeConfigString(result); return TRUE; } - result = REGUTIL::GetConfigString_DontUse_(name, FALSE); + result = GetConfigString(name, LookupOptions::DontPrependPrefix); if (result != NULL) { FreeConfigString(result); return TRUE; } - } return FALSE; } -//--------------------------------------------------------------------------------------- -// -// Given an input string, returns a newly-allocated string equal to the input but with -// leading and trailing whitespace trimmed off. If input is already trimmed, or if -// trimming would result in an empty string, this function sets the output string to NULL // -// Caller must free *pwszTrimmed if non-NULL +// Deallocation function for code:CLRConfig::FreeConfigString // -// Arguments: -// * wszOrig - String to trim -// * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or -// NULL) +// static +void CLRConfig::FreeConfigString(__in_z LPWSTR str) +{ + LIMITED_METHOD_CONTRACT; + + // See EnvGetString(). + delete [] str; +} + // -// Return Value: -// HRESULT indicating success or failure. +// Initialize the internal cache for faster lookup. // -HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed) +// static +void CLRConfig::Initialize() { CONTRACTL { @@ -309,77 +608,58 @@ HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTr } CONTRACTL_END; - _ASSERTE(wszOrig != NULL); - _ASSERTE(pwszTrimmed != NULL); - - // In case we return early, set [out] to NULL by default - *pwszTrimmed = NULL; - - // Get pointers into internal string that show where to do the trimming. - size_t cchOrig = wcslen(wszOrig); - if (!FitsIn(cchOrig)) - return COR_E_OVERFLOW; - DWORD cchAfterTrim = (DWORD) cchOrig; - LPCWSTR wszAfterTrim = wszOrig; - ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim); - - // Is input string already trimmed? If so, save an allocation and just return. - if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim)) - { - // Yup, just return success - return S_OK; - } + // Check if caching is disabled. + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0) + return; - if (cchAfterTrim == 0) - { - // After trimming, there's nothing left, so just return NULL - return S_OK; - } + const WCHAR prefixC = towlower(COMPLUS_PREFIX[0]); + const WCHAR prefixD = towlower(DOTNET_PREFIX[0]); - // Create a new buffer to hold a copy of the trimmed string. Caller will be - // responsible for this buffer if we return it. - NewArrayHolder wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]); - if (wszTrimmedCopy == NULL) + // Create a cache of environment variables + WCHAR* wszStrings = GetEnvironmentStringsW(); + if (wszStrings != NULL) { - return E_OUTOFMEMORY; - } + // GetEnvironmentStrings returns pointer to a null terminated block containing + // null terminated strings + for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++) + { + WCHAR wch = towlower(*wszCurr); + + // Lets only cache env variables with targeted prefixes + bool matchC = wch == prefixC; + bool matchD = wch == prefixD; + if (matchC || matchD) + { + WCHAR *wszName = wszCurr; + + // Look for the separator between name and value + while (*wszCurr && *wszCurr != W('=')) + wszCurr++; + + if (*wszCurr == W('=')) + { + // Check the prefix + if(matchC + && SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX) == 0) + { + wszName += LEN_OF_COMPLUS_PREFIX; + s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName)); + } + else if (matchD + && SString::_wcsnicmp(wszName, DOTNET_PREFIX, LEN_OF_DOTNET_PREFIX) == 0) + { + wszName += LEN_OF_DOTNET_PREFIX; + s_EnvNames.Add(wszName, (DWORD) (wszCurr - wszName)); + } + } + } + + // Look for current string termination + while (*wszCurr) + wszCurr++; + } - errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim); - if (err != 0) - { - return E_FAIL; + FreeEnvironmentStringsW(wszStrings); + s_fUseEnvCache = true; } - - // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for - // deleting it. - wszTrimmedCopy.SuppressRelease(); - *pwszTrimmed = wszTrimmedCopy; - return S_OK; -} - - -// -// Deallocation function for code:CLRConfig::FreeConfigString -// -void CLRConfig::FreeConfigString(__in_z LPWSTR str) -{ - LIMITED_METHOD_CONTRACT; - - delete [] str; -} - -// -// Helper method to translate LookupOptions to REGUTIL::CORConfigLevel. -// -//static -REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options) -{ - LIMITED_METHOD_CONTRACT; - - REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0; - - if(CheckLookupOption(options, IgnoreEnv) == FALSE) - level = static_cast(level | REGUTIL::COR_CONFIG_ENV); - - return static_cast(level | REGUTIL::COR_CONFIG_USER | REGUTIL::COR_CONFIG_MACHINE); } diff --git a/src/coreclr/utilcode/configuration.cpp b/src/coreclr/utilcode/configuration.cpp index e67507d5bae05..50a5e335a742b 100644 --- a/src/coreclr/utilcode/configuration.cpp +++ b/src/coreclr/utilcode/configuration.cpp @@ -52,7 +52,7 @@ static LPCWSTR GetConfigurationValue(LPCWSTR name) DWORD Configuration::GetKnobDWORDValue(LPCWSTR name, const CLRConfig::ConfigDWORDInfo& dwordInfo) { bool returnedDefaultValue; - DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, true /* acceptExplicitDefaultFromRegutil */, &returnedDefaultValue); + DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, &returnedDefaultValue); if (!returnedDefaultValue) { return legacyValue; @@ -108,7 +108,7 @@ LPCWSTR Configuration::GetKnobStringValue(LPCWSTR name) bool Configuration::GetKnobBooleanValue(LPCWSTR name, const CLRConfig::ConfigDWORDInfo& dwordInfo) { bool returnedDefaultValue; - DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, true /* acceptExplicitDefaultFromRegutil */, &returnedDefaultValue); + DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, &returnedDefaultValue); if (!returnedDefaultValue) { return (legacyValue != 0); diff --git a/src/coreclr/utilcode/log.cpp b/src/coreclr/utilcode/log.cpp index 37edc05b5cdb2..b4a73f2cb6baf 100644 --- a/src/coreclr/utilcode/log.cpp +++ b/src/coreclr/utilcode/log.cpp @@ -47,16 +47,16 @@ VOID InitLogging() // FIX bit of a workaround for now, check for the log file in the // registry and if there, turn on file logging VPM - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogEnable, LOG_ENABLE); - LogFacilityMask = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS; - LogVMLevel = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LogVMLevel); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFileAppend, LOG_ENABLE_APPEND_FILE); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFlushFile, LOG_ENABLE_FLUSH_FILE); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToDebugger, LOG_ENABLE_DEBUGGER_LOGGING); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToFile, LOG_ENABLE_FILE_LOGGING); - LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToConsole, LOG_ENABLE_CONSOLE_LOGGING); + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogEnable) != 0) ? LOG_ENABLE : 0; + LogFacilityMask = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS; + LogVMLevel = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LogLevel, LogVMLevel); + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFileAppend) != 0) ? LOG_ENABLE_APPEND_FILE : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFlushFile) != 0) ? LOG_ENABLE_FLUSH_FILE : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogToDebugger) != 0) ? LOG_ENABLE_DEBUGGER_LOGGING : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogToFile) != 0) ? LOG_ENABLE_FILE_LOGGING : 0; + LogFlags |= (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogToConsole) != 0) ? LOG_ENABLE_CONSOLE_LOGGING : 0; - LogFacilityMask2 = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS; + LogFacilityMask2 = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS; if (SUCCEEDED(szLogFileName.ReSizeNoThrow(MAX_LONGPATH))) { @@ -73,7 +73,7 @@ VOID InitLogging() delete fileName; } - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogWithPid, FALSE)) + if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogWithPid)) { WCHAR szPid[20]; swprintf_s(szPid, COUNTOF(szPid), W(".%d"), GetCurrentProcessId()); diff --git a/src/coreclr/utilcode/peinformation.cpp b/src/coreclr/utilcode/peinformation.cpp deleted file mode 100644 index 78e41b11196d6..0000000000000 --- a/src/coreclr/utilcode/peinformation.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -------------------------------------------------------------------------------- -// PEInformation.cpp -// - -// -------------------------------------------------------------------------------- - -#include "stdafx.h" -#include "utilcode.h" -#include "peinformation.h" - - -HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, PEKIND * pPeKind) -{ - return TranslatePEToArchitectureType(CLRPeKind, dwImageType, 0, pPeKind); -} - -HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, DWORD dwAssemblyFlags, PEKIND * pPeKind) -{ - HRESULT hr = S_OK; - - _ASSERTE(pPeKind != NULL); - - if (CLRPeKind == peNot) - { // Not a PE. Shouldn't ever get here. - *pPeKind = peInvalid; - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - goto Exit; - } - else if (IsAfPA_NoPlatform(dwAssemblyFlags)) - { - *pPeKind = peNone; - goto Exit; - } - else - { - if ((CLRPeKind & peILonly) && - !(CLRPeKind & pe32Plus) && - !(CLRPeKind & pe32BitRequired) && - (dwImageType == IMAGE_FILE_MACHINE_I386)) - { - // Processor-agnostic (MSIL) - *pPeKind = peMSIL; - } - else if (CLRPeKind & pe32Plus) - { - // 64-bit - - if (CLRPeKind & pe32BitRequired) - { - *pPeKind = peInvalid; - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - goto Exit; - } - - // Regardless of whether ILONLY is set or not, the architecture - // is the machine type. - - if (dwImageType == IMAGE_FILE_MACHINE_IA64) - { - *pPeKind = peIA64; - } - else if (dwImageType == IMAGE_FILE_MACHINE_AMD64) - { - *pPeKind = peAMD64; - } - else - { // We don't support other architectures - *pPeKind = peInvalid; - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - goto Exit; - } - } - else - { - // 32-bit, non-agnostic - - if (dwImageType == IMAGE_FILE_MACHINE_I386) - { - *pPeKind = peI386; - } - else if (dwImageType == IMAGE_FILE_MACHINE_ARMNT) - { - *pPeKind = peARM; - } - else - { // Not supported - *pPeKind = peInvalid; - hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); - goto Exit; - } - } - } - -Exit: - return hr; -} diff --git a/src/coreclr/utilcode/regutil.cpp b/src/coreclr/utilcode/regutil.cpp deleted file mode 100644 index fcfbe6eb7c7cd..0000000000000 --- a/src/coreclr/utilcode/regutil.cpp +++ /dev/null @@ -1,759 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -//***************************************************************************** -// regutil.cpp -// - -// -// This module contains a set of functions that can be used to access the -// registry. -// -//***************************************************************************** - - -#include "stdafx.h" -#include "utilcode.h" -#include "mscoree.h" -#include "sstring.h" -#include "ex.h" - -#define COMPLUS_PREFIX W("COMPlus_") -#define LEN_OF_COMPLUS_PREFIX 8 - -#if (!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(DEBUG)) && !defined(TARGET_UNIX) -#define ALLOW_REGISTRY -#endif - -#undef WszRegCreateKeyEx -#undef WszRegOpenKeyEx -#undef WszRegOpenKey -#define WszRegCreateKeyEx RegCreateKeyExW -#define WszRegOpenKeyEx RegOpenKeyExW -#define WszRegOpenKey(hKey, wszSubKey, phkRes) RegOpenKeyExW(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes) - -//***************************************************************************** -// Reads from the environment setting -//***************************************************************************** -LPWSTR REGUTIL::EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - WCHAR buff[64]; - - if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0))) - { - return NULL; - } - - if (fPrependCOMPLUS) - { -#ifdef ALLOW_REGISTRY - if (!EnvCacheValueNameSeenPerhaps(name)) - return NULL; -#endif // ALLOW_REGISTRY - wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX); - } - else - { - *buff = 0; - } - - wcscat_s(buff, _countof(buff), name); - - FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. - - - NewArrayHolder ret = NULL; - HRESULT hr = S_OK; - DWORD Len; - EX_TRY - { - PathString temp; - - Len = WszGetEnvironmentVariable(buff, temp); - if (Len != 0) - { - ret = temp.GetCopyOfUnicodeString(); - } - - } - EX_CATCH_HRESULT(hr); - - if (hr != S_OK) - { - SetLastError(hr); - } - - if(ret != NULL) - { - return ret.Extract(); - } - - return NULL; - - -} - -//***************************************************************************** -// Reads a DWORD from the COR configuration according to the level specified -// Returns back defValue if the key cannot be found -//***************************************************************************** -DWORD REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - SUPPORTS_DAC_HOST_ONLY; - - ULONGLONG result; - GetConfigInteger(name, defValue, &result, TRUE, level, fPrependCOMPLUS); - - return (DWORD)result; -} - -#define uniwcst(val, endptr, base) (fGetDWORD ? wcstoul(val, endptr, base) : _wcstoui64(val, endptr, base)) - -// -// Look up a dword config value, and write the result to the DWORD passed in by reference. -// -// Return value: -// * E_FAIL if the value is not found. (result is assigned the default value) -// * S_OK if the value is found. (result is assigned the value that was found) -// -// Arguments: -// * info - see file:../inc/CLRConfig.h for details -// * result - Pointer to the output DWORD. -// -// static -HRESULT REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, __out DWORD * result, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - ULONGLONG ullResult; - HRESULT hr = GetConfigInteger(name, defValue, &ullResult, TRUE, level, fPrependCOMPLUS); - *result = (DWORD)ullResult; - return hr; -} - -ULONGLONG REGUTIL::GetConfigULONGLONG_DontUse_(LPCWSTR name, ULONGLONG defValue, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - SUPPORTS_DAC_HOST_ONLY; - - ULONGLONG result; - GetConfigInteger(name, defValue, &result, FALSE, level, fPrependCOMPLUS); - - return result; -} - -// This function should really be refactored to return the string from the environment and let the caller decide -// what to convert it to; and return the buffer read from the reg call. -// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as -// a 32-bit number. When we have the _wcstoui64 API on MAC we will use uniwcst instead of wcstoul. -HRESULT REGUTIL::GetConfigInteger(LPCWSTR name, ULONGLONG defValue, __out ULONGLONG * result, BOOL fGetDWORD, CORConfigLevel level, BOOL fPrependCOMPLUS) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - CANNOT_TAKE_LOCK; - } - CONTRACTL_END; - - SUPPORTS_DAC_HOST_ONLY; - - ULONGLONG rtn; - ULONGLONG ret = 0; - DWORD type = 0; - DWORD size = 4; - - FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. - - if (level & COR_CONFIG_ENV) - { - WCHAR* val = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first - if (val != 0) { - errno = 0; - LPWSTR endPtr; - rtn = uniwcst(val, &endPtr, 16); // treat it has hex - BOOL fSuccess = ((errno != ERANGE) && (endPtr != val)); - delete[] val; - - if (fSuccess) // success - { - *result = rtn; - return (S_OK); - } - } - } - - // Early out if no registry access, simplifies following code. - // - if (!(level & COR_CONFIG_REGISTRY)) - { - *result = defValue; - return (E_FAIL); - } - -#ifdef ALLOW_REGISTRY - // Probe the config cache to see if there is any point - // probing the registry; if not, don't bother. - // - if (!RegCacheValueNameSeenPerhaps(name)) - { - *result = defValue; - return (E_FAIL); - } -#endif // ALLOW_REGISTRY - - if (level & COR_CONFIG_USER) - { -#ifdef ALLOW_REGISTRY - { - LONG retVal = ERROR_SUCCESS; - BOOL bCloseHandle = FALSE; - HKEY userKey = s_hUserFrameworkKey; - - if (userKey == INVALID_HANDLE_VALUE) - { - retVal = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey); - bCloseHandle = TRUE; - } - - if (retVal == ERROR_SUCCESS) - { - rtn = WszRegQueryValueEx(userKey, name, 0, &type, (LPBYTE)&ret, &size); - - if (bCloseHandle) - VERIFY(!RegCloseKey(userKey)); - - if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD))) - { - *result = ret; - return (S_OK); - } - } - } -#endif // ALLOW_REGISTRY - } - - if (level & COR_CONFIG_MACHINE) - { -#ifdef ALLOW_REGISTRY - { - LONG retVal = ERROR_SUCCESS; - BOOL bCloseHandle = FALSE; - HKEY machineKey = s_hMachineFrameworkKey; - - if (machineKey == INVALID_HANDLE_VALUE) - { - retVal = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey); - bCloseHandle = TRUE; - } - - if (retVal == ERROR_SUCCESS) - { - rtn = WszRegQueryValueEx(machineKey, name, 0, &type, (LPBYTE)&ret, &size); - - if (bCloseHandle) - VERIFY(!RegCloseKey(machineKey)); - - if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD))) - { - *result = ret; - return (S_OK); - } - } - } -#endif // ALLOW_REGISTRY - } - - *result = defValue; - return (E_FAIL); -} - -//***************************************************************************** -// Reads a string from the COR configuration according to the level specified -// The caller is responsible for deallocating the returned string by -// calling code:REGUTIL::FreeConfigString or using a code:ConfigStringHolder -//***************************************************************************** - -LPWSTR REGUTIL::GetConfigString_DontUse_(LPCWSTR name, BOOL fPrependCOMPLUS, CORConfigLevel level, BOOL fUsePerfCache) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - FORBID_FAULT; - } - CONTRACTL_END; - -#ifdef ALLOW_REGISTRY - HRESULT lResult; - RegKeyHolder userKey = NULL; - RegKeyHolder machineKey = NULL; - RegKeyHolder fusionKey = NULL; - DWORD type; - DWORD size; -#endif // ALLOW_REGISTRY - NewArrayHolder ret(NULL); - - FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value. - - if (level & COR_CONFIG_ENV) - { - ret = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first - if (ret != 0) { - if (*ret != 0) - { - ret.SuppressRelease(); - return(ret); - } - ret.Clear(); - } - } - - // Early out if no registry access, simplifies following code. - // - if (!(level & COR_CONFIG_REGISTRY)) - { - return(ret); - } - -#ifdef ALLOW_REGISTRY - // Probe the config cache to see if there is any point - // probing the registry; if not, don't bother. - // - if (fUsePerfCache && !RegCacheValueNameSeenPerhaps(name)) - return ret; -#endif // ALLOW_REGISTRY - - if (level & COR_CONFIG_USER) - { -#ifdef ALLOW_REGISTRY - BOOL bUsingCachedKey = FALSE; - - if (s_hUserFrameworkKey != INVALID_HANDLE_VALUE) - { - bUsingCachedKey = TRUE; - userKey = s_hUserFrameworkKey; - } - - if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey) == ERROR_SUCCESS) - { - BOOL bReturn = FALSE; - if (WszRegQueryValueEx(userKey, name, 0, &type, 0, &size) == ERROR_SUCCESS && - type == REG_SZ) - { - ret = (LPWSTR) new (nothrow) BYTE [size]; - if (ret) - { - ret[0] = W('\0'); - lResult = WszRegQueryValueEx(userKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); - _ASSERTE(lResult == ERROR_SUCCESS); - { - ret.SuppressRelease(); - } - } - bReturn = TRUE; - } - - if (bUsingCachedKey) - userKey.SuppressRelease(); - - if (bReturn) - return ret; - } - -#endif // ALLOW_REGISTRY - } - - if (level & COR_CONFIG_MACHINE) - { -#ifdef ALLOW_REGISTRY - BOOL bUsingCachedKey = FALSE; - - if (s_hMachineFrameworkKey != INVALID_HANDLE_VALUE) - { - bUsingCachedKey = TRUE; - machineKey = s_hMachineFrameworkKey; - } - - if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey) == ERROR_SUCCESS) - { - BOOL bReturn = FALSE; - if (WszRegQueryValueEx(machineKey, name, 0, &type, 0, &size) == ERROR_SUCCESS && - type == REG_SZ) - { - ret = (LPWSTR) new (nothrow) BYTE [size]; - if (ret) - { - ret[0] = W('\0'); - lResult = WszRegQueryValueEx(machineKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size); - _ASSERTE(lResult == ERROR_SUCCESS); - { - ret.SuppressRelease(); - } - } - bReturn = TRUE; - } - - if (bUsingCachedKey) - machineKey.SuppressRelease(); - - if (bReturn) - return ret; - } - -#endif // ALLOW_REGISTRY - } - - return NULL; -} - -//***************************************************************************** -// Deallocation function for code:REGUTIL::GetConfigString_DontUse_ -// -// Notes: -// Use a code:ConfigStringHolder to automatically call this. -//***************************************************************************** -void REGUTIL::FreeConfigString(__in_z LPWSTR str) -{ - LIMITED_METHOD_CONTRACT; - - delete [] str; -} - -//***************************************************************************** -// Reads a BIT flag from the COR configuration according to the level specified -// Returns back defValue if the key cannot be found -//***************************************************************************** -DWORD REGUTIL::GetConfigFlag_DontUse_(LPCWSTR name, DWORD bitToSet, BOOL defValue) -{ - WRAPPER_NO_CONTRACT; - - return(GetConfigDWORD_DontUse_(name, defValue) != 0 ? bitToSet : 0); -} - - -#ifdef ALLOW_REGISTRY - - - -// -// ProbabilisticNameSet: -// -// (Used by ConfigCache, below. If used elsewhere, might justify -// promotion to a standalone header file.) -// -// Represent a set of names in a small, fixed amount of storage. -// We turn a name into a small integer, then add the integer to a bitvector. -// An old trick we used in VC++4 minimal rebuild. -// -// For best results, the number of elements should be a fraction of -// the total number of bits in 'bits'. -// -// Note, only the const methods are thread-safe. -// Callers are responsible for providing their own synchronization when -// constructing and Add'ing names to the set. -// -class ProbabilisticNameSet { -public: - ProbabilisticNameSet() - { - WRAPPER_NO_CONTRACT; - - memset(bits, 0, sizeof(bits)); - } - - // Add a name to the set. - // - void Add(LPCWSTR name) - { - WRAPPER_NO_CONTRACT; - - unsigned i, mask; - GetBitIndex(name, 0, &i, &mask); - bits[i] |= mask; - } - - void Add(LPCWSTR name, DWORD count) - { - WRAPPER_NO_CONTRACT; - - unsigned i, mask; - GetBitIndex(name, count, &i, &mask); - bits[i] |= mask; - } - - // Return TRUE if a name *may have* been added to the set; - // return FALSE if the name *definitely* was NOT ever added to the set. - // - BOOL MayContain(LPCWSTR name) const - { - WRAPPER_NO_CONTRACT; - - unsigned i, mask; - GetBitIndex(name, 0, &i, &mask); - return !!(bits[i] & mask); - } - -private: - static const unsigned cbitSet = 256U; - static const unsigned cbitWord = 8U*sizeof(unsigned); - unsigned bits[cbitSet/cbitWord]; - - // Return the word index and bit mask corresponding to the bitvector member - // addressed by the *case-insensitive* hash of the given name. - // - void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const - { - LIMITED_METHOD_CONTRACT; - unsigned hash; - if (count > 0) - hash = HashiStringNKnownLower80(name, count) % cbitSet; - else - hash = HashiStringKnownLower80(name) % cbitSet; - *pi = hash / cbitWord; - *pmask = (1U << (hash % cbitWord)); - } - -}; - - -// From the Win32 SDK docs: -// Registry Element Size Limits -// ... -// The maximum size of a value name is as follows: -// Windows Server 2003 and Windows XP: 16,383 characters -// Windows 2000: 260 ANSI characters or 16,383 Unicode characters. -// Windows Me/98/95: 255 characters -// Despite that, we only cache value names of 80 characters or less -- -// longer names don't make sense as configuration settings names. -// -static const unsigned cchRegValueNameMax = 80; - -BOOL REGUTIL::s_fUseRegCache = FALSE; -BOOL REGUTIL::s_fUseEnvCache = FALSE; -HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE; -HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE; -static ProbabilisticNameSet regNames; // set of registry value names seen; should be - // a static field of REGUTIL but I don't - // want to expose ProbabilisticNameSet. -static ProbabilisticNameSet envNames; // set of environment value names seen; - -// "Registry Configuration Cache" -// -// Initialize the (optional) registry config cache. -// -// The purpose of the cache is to avoid hundreds of registry probes -// otherwise incurred by calls to GetConfigDWORD_DontUse_ and GetConfigString_DontUse_. -// -// We accomplish this by enumerating the relevant registry keys and -// remembering the extant value names; and then by avoiding probing -// for a name that was not seen in the enumeration (initialization) phase. -// -// It is optional in the sense that REGUTIL facilities like -// GetConfigDWORD_DontUse_ and GetConfigString_DontUse_ will work fine if the cache -// is never initialized; however, each config access then will hit -// the registry (typically multiple times to search HKCU and HKLM). -// -// -// Initialization: Enumerate these registry keys -// HKCU Software\Microsoft\.NetFramework -// HKLM Software\Microsoft\.NetFramework -// for value names, and "remember" them in the ProbalisticNameSet 'names'. -// -// If we ever find a reg value named DisableConfigCache under any of these -// three keys, the feature is disabled. -// -// This method is not thread-safe. It should only be called once. -// -// Perf Optimization for VSWhidbey:113373. -// -void REGUTIL::InitOptionalConfigCache() -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - static const HKEY roots[] = { HKEY_CURRENT_USER, - HKEY_LOCAL_MACHINE}; - - LONG l = ERROR_SUCCESS; // general Win32 API error return code - HKEY hkey = NULL; - - // No caching if the environment variable COMPlus_DisableConfigCache is set - // - if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0) - goto failure; - - // Enumerate each root - // - for (int i = 0; i < NumItems(roots); i++) { - hkey = NULL; // defensive - l = WszRegOpenKeyEx(roots[i], FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hkey); - if (l == ERROR_FILE_NOT_FOUND) { - // That registry key is not present. - // For example, installation with no HKCU\...\.NETFramework. - // Should be OK to proceed. - continue; - } - else if (l == ERROR_ACCESS_DENIED) { - // If we encounter access denied for the current key, ignore - // the failure and continue to cache the rest. Effectively this means - // we are caching that key as containing no values, which is correct - // because in the unlikely event there are values hiding underneath - // later attempts to access them (open the key) would also hit access - // denied and continue on probing other locations. - continue; - } - else if (l != ERROR_SUCCESS) { - // Something else went wrong. To be safe, don't enable the cache. - goto failure; - } - - // Enumerate every value name under this key. - // - for (int j = 0; ; j++) { - WCHAR wszValue[cchRegValueNameMax + 1]; - DWORD dwValueSize = NumItems(wszValue); - l = WszRegEnumValue(hkey, j, wszValue, &dwValueSize, - NULL, NULL, NULL, NULL); - - if (l == ERROR_SUCCESS) { - // Add value name to the names cache. - regNames.Add(wszValue); - } - else if (l == ERROR_NO_MORE_ITEMS) { - // Expected case: we've considered every value under this key. - break; - } - else if ((l == ERROR_INSUFFICIENT_BUFFER || l == ERROR_MORE_DATA) && - (dwValueSize > cchRegValueNameMax)) { - // Name is too long. That's OK, we don't cache such names. - continue; - } - else if (l == ERROR_ACCESS_DENIED) { - // As above, ignore access denied and continue on trying to cache - continue; - } - else { - // WszRegEnumValue failed OOM, or something else went wrong. - // To be safe, don't enable the cache. - goto failure; - } - } - - // Save the handles to framework regkeys so that future reads dont have to - // open it again - if (roots[i] == HKEY_CURRENT_USER) - s_hUserFrameworkKey = hkey; - else if (roots[i] == HKEY_LOCAL_MACHINE) - s_hMachineFrameworkKey = hkey; - else - RegCloseKey(hkey); - - hkey = NULL; - } - - // Success. We've enumerated all value names under the roots; - // enable the REGUTIL value name config cache. - // - s_fUseRegCache = TRUE; - - // Now create a cache of environment variables - if (WCHAR * wszStrings = WszGetEnvironmentStrings()) - { - // GetEnvironmentStrings returns pointer to a null terminated block containing - // null terminated strings - for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++) - { - WCHAR wch = towlower(*wszCurr); - - // Lets only cache env variables with the COMPlus prefix only - if (wch == W('c')) - { - WCHAR *wszName = wszCurr; - - // Look for the separator between name and value - while (*wszCurr && *wszCurr != W('=')) - wszCurr++; - - if (*wszCurr == W('=')) - { - // Check the prefix - if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX)) - { - wszName += LEN_OF_COMPLUS_PREFIX; - envNames.Add(wszName, (DWORD) (wszCurr - wszName)); - } - } - - } - // Look for current string termination - while (*wszCurr) - wszCurr++; - - } - - WszFreeEnvironmentStrings(wszStrings); - s_fUseEnvCache = TRUE; - - } - return; - -failure: - if (hkey != NULL) - RegCloseKey(hkey); -} - -// Return TRUE if the registry value name was seen (or might have been seen) -// in the registry at cache initialization time; -// return FALSE if it definitely was not seen at startup. -// -// If not using the config cache, return TRUE always. -// -// Perf Optimization for VSWhidbey:113373. -// -BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name) -{ - WRAPPER_NO_CONTRACT; - - return !s_fUseRegCache - || (wcslen(name) > cchRegValueNameMax) - || regNames.MayContain(name); -} - -BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name) -{ - WRAPPER_NO_CONTRACT; - - return !s_fUseEnvCache - || envNames.MayContain(name); -} - -#endif // ALLOW_REGISTRY diff --git a/src/coreclr/utilcode/stresslog.cpp b/src/coreclr/utilcode/stresslog.cpp index 2b81551eb3877..6203d57270176 100644 --- a/src/coreclr/utilcode/stresslog.cpp +++ b/src/coreclr/utilcode/stresslog.cpp @@ -142,7 +142,7 @@ void StressLog::Leave(CRITSEC_COOKIE) { /*********************************************************************************/ void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread, - ULONGLONG maxBytesTotal, void* moduleBase, LPWSTR logFilename) + unsigned maxBytesTotal, void* moduleBase, LPWSTR logFilename) { STATIC_CONTRACT_LEAF; diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 018ea351c5308..1be42772b6e2e 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -1405,22 +1405,6 @@ bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SI return(m_list.IsInList(methodName, className, pSigInfo)); } -/**************************************************************************/ -void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal) -{ - CONTRACTL - { - NOTHROW; - } - CONTRACTL_END; - - // make sure that the memory was zero initialized - _ASSERTE(m_inited == 0 || m_inited == 1); - - m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal); - m_inited = 1; -} - /**************************************************************************/ void ConfigString::init(const CLRConfig::ConfigStringInfo & info) { diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 5bd32629f3d5b..0e1c2513fbca0 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -909,14 +909,12 @@ convert_to_absolute_path(VM_SOURCES_WKS_ARCH_ASM ${VM_SOURCES_WKS_ARCH_ASM}) convert_to_absolute_path(VM_SOURCES_DAC ${VM_SOURCES_DAC}) convert_to_absolute_path(VM_SOURCES_WKS_SPECIAL ${VM_SOURCES_WKS_SPECIAL}) -if (CLR_CMAKE_BUILD_SUBSET_RUNTIME) - add_library_clr(cee_dac ${VM_SOURCES_DAC}) - add_dependencies(cee_dac eventing_headers) - set_target_properties(cee_dac PROPERTIES DAC_COMPONENT TRUE) - target_precompile_headers(cee_dac PRIVATE [["common.h"]]) - - add_subdirectory(wks) -endif(CLR_CMAKE_BUILD_SUBSET_RUNTIME) +add_library_clr(cee_dac ${VM_SOURCES_DAC}) +add_dependencies(cee_dac eventing_headers) +set_target_properties(cee_dac PROPERTIES DAC_COMPONENT TRUE) +target_precompile_headers(cee_dac PRIVATE [["common.h"]]) + +add_subdirectory(wks) if(FEATURE_PERFTRACING) add_subdirectory(eventing) diff --git a/src/coreclr/vm/amd64/excepamd64.cpp b/src/coreclr/vm/amd64/excepamd64.cpp index 5c93b0342d6cf..17b9c4e7b3378 100644 --- a/src/coreclr/vm/amd64/excepamd64.cpp +++ b/src/coreclr/vm/amd64/excepamd64.cpp @@ -573,7 +573,7 @@ AdjustContextForVirtualStub( { LIMITED_METHOD_CONTRACT; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); // We may not have a managed thread object. Example is an AV on the helper thread. // (perhaps during StubManager::IsStub) diff --git a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp index e76b6d4ce4366..38bff78a54cb0 100644 --- a/src/coreclr/vm/amd64/jitinterfaceamd64.cpp +++ b/src/coreclr/vm/amd64/jitinterfaceamd64.cpp @@ -280,7 +280,7 @@ PBYTE WriteBarrierManager::CalculatePatchLocation(LPVOID base, LPVOID label, int int WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier, bool isRuntimeSuspended) { - GCX_MAYBE_COOP_NO_THREAD_BROKEN((!isRuntimeSuspended && GetThread() != NULL)); + GCX_MAYBE_COOP_NO_THREAD_BROKEN((!isRuntimeSuspended && GetThreadNULLOk() != NULL)); int stompWBCompleteActions = SWB_PASS; if (!isRuntimeSuspended && m_currentWriteBarrier != WRITE_BARRIER_UNINITIALIZED) { diff --git a/src/coreclr/vm/amd64/pinvokestubs.S b/src/coreclr/vm/amd64/pinvokestubs.S index c6febd27e2de9..6c57f8713831b 100644 --- a/src/coreclr/vm/amd64/pinvokestubs.S +++ b/src/coreclr/vm/amd64/pinvokestubs.S @@ -161,7 +161,7 @@ NESTED_ENTRY JIT_PInvokeBegin, _TEXT, NoHandler mov r14, rdi // Get current thread and cache it in current frame - call C_FUNC(GetThread) + call C_FUNC(GetThreadHelper) mov qword ptr [r14 + OFFSETOF__InlinedCallFrame__m_pThread], rax // pFrame->m_Next = pThread->m_pFrame; diff --git a/src/coreclr/vm/amd64/virtualcallstubcpu.hpp b/src/coreclr/vm/amd64/virtualcallstubcpu.hpp index d4035966a52e0..860a681e21352 100644 --- a/src/coreclr/vm/amd64/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/amd64/virtualcallstubcpu.hpp @@ -595,9 +595,7 @@ void DispatchHolder::InitializeStatic() static_assert_no_msg(((sizeof(DispatchStub) + sizeof(DispatchStubShort)) % sizeof(void*)) == 0); static_assert_no_msg(((sizeof(DispatchStub) + sizeof(DispatchStubLong)) % sizeof(void*)) == 0); - // TODO: This should be a static_assert_no_msg(), but there were reports of build failure with VS 2019 due to the expression - // not being a compile-time constant, see https://github.com/dotnet/runtime/issues/11858 - _ASSERTE((DispatchStubLong_offsetof_failLabel - DispatchStubLong_offsetof_failDisplBase) < INT8_MAX); + static_assert_no_msg((DispatchStubLong_offsetof_failLabel - DispatchStubLong_offsetof_failDisplBase) < INT8_MAX); // Common dispatch stub initialization dispatchInit._entryPoint [0] = 0x48; diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index ea0806ebfd738..72634148ebaaf 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -67,6 +67,7 @@ #include "../binder/inc/bindertracing.h" #include "../binder/inc/clrprivbindercoreclr.h" +#include "../binder/inc/coreclrbindercommon.h" // this file handles string conversion errors for itself #undef MAKE_TRANSLATIONFAILED @@ -1115,7 +1116,7 @@ void SystemDomain::DetachBegin() // yet). // TODO: we should really not running managed DLLMain during process detach. - if (GetThread() == NULL) + if (GetThreadNULLOk() == NULL) { return; } @@ -1291,7 +1292,7 @@ void SystemDomain::Init() } #ifdef _DEBUG - BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE); + BOOL fPause = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_PauseOnLoad); while (fPause) { @@ -1611,8 +1612,6 @@ void SystemDomain::SetThreadAptState (Thread::ApartmentState state) STANDARD_VM_CONTRACT; Thread* pThread = GetThread(); - _ASSERTE(pThread); - if(state == Thread::AS_InSTA) { Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InSTA); @@ -3043,6 +3042,7 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, // Find the list lock entry FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pFile); + bool registerNewAssembly = false; if (fileLock == NULL) { // Check again in case we were racing @@ -3050,6 +3050,7 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, if (result == NULL) { // We are the first one in - create the DomainAssembly + registerNewAssembly = true; fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly); pDomainAssembly.SuppressRelease(); #ifndef CROSSGEN_COMPILE @@ -3082,6 +3083,11 @@ DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, { result->EnsureLoadLevel(targetLevel); } + + if (registerNewAssembly) + { + pFile->GetAssemblyLoadContext()->AddLoadedAssembly(pDomainAssembly->GetCurrentAssembly()); + } } else result->EnsureLoadLevel(targetLevel); @@ -3541,7 +3547,7 @@ void AppDomain::SetFriendlyName(LPCWSTR pwzFriendlyName, BOOL fDebuggerCares/*=T CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); } @@ -3595,7 +3601,7 @@ LPCWSTR AppDomain::GetFriendlyName(BOOL fDebuggerCares/*=TRUE*/) CONTRACT (LPCWSTR) { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); INJECT_FAULT(COMPlusThrowOM();); @@ -3626,7 +3632,7 @@ LPCWSTR AppDomain::GetFriendlyNameForDebugger() CONTRACT (LPCWSTR) { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; POSTCONDITION(CheckPointer(RETVAL)); } @@ -4530,7 +4536,6 @@ void AppDomain::ExceptionUnwind(Frame *pFrame) LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n", pFrame)); Thread *pThread = GetThread(); - _ASSERTE(pThread); LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n")); } @@ -5280,13 +5285,14 @@ AppDomain::AssemblyIterator::Next_Unlocked( #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) // Returns S_OK if the assembly was successfully loaded -HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, BINDER_SPACE::AssemblyName *pAssemblyName, ICLRPrivAssembly **ppLoadedAssembly) +HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, BINDER_SPACE::AssemblyName *pAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, ICLRPrivAssembly **ppLoadedAssembly) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; + PRECONDITION(pAssemblyName != NULL); PRECONDITION(ppLoadedAssembly != NULL); } CONTRACTL_END; @@ -5308,188 +5314,179 @@ HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToB ICLRPrivAssembly *pResolvedAssembly = NULL; - // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.Resolve method. - // - // First, initialize an assembly spec for the requested assembly - // - AssemblySpec spec; - hr = spec.Init(pIAssemblyName); - if (SUCCEEDED(hr)) - { - bool fResolvedAssembly = false; - BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, 0 /*binderID*/, pManagedAssemblyLoadContextToBindWithin, hr}; + bool fResolvedAssembly = false; + BinderTracing::ResolutionAttemptedOperation tracer{pAssemblyName, 0 /*binderID*/, pManagedAssemblyLoadContextToBindWithin, hr}; - // Allocate an AssemblyName managed object - _gcRefs.oRefAssemblyName = (ASSEMBLYNAMEREF) AllocateObject(CoreLibBinder::GetClass(CLASS__ASSEMBLY_NAME)); + // Allocate an AssemblyName managed object + _gcRefs.oRefAssemblyName = (ASSEMBLYNAMEREF) AllocateObject(CoreLibBinder::GetClass(CLASS__ASSEMBLY_NAME)); - // Initialize the AssemblyName object from the AssemblySpec - spec.AssemblyNameInit(&_gcRefs.oRefAssemblyName, NULL); + // Initialize the AssemblyName object + AssemblySpec::InitializeAssemblyNameRef(pAssemblyName, &_gcRefs.oRefAssemblyName); - bool isSatelliteAssemblyRequest = !spec.IsNeutralCulture(); + bool isSatelliteAssemblyRequest = !pAssemblyName->IsNeutralCulture(); - EX_TRY + EX_TRY + { + if (pTPABinder != NULL) { - if (pTPABinder != NULL) + // Step 2 (of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName) - Invoke Load method + // This is not invoked for TPA Binder since it always returns NULL. + tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::AssemblyLoadContextLoad); + + // Finally, setup arguments for invocation + MethodDescCallSite methLoadAssembly(METHOD__ASSEMBLYLOADCONTEXT__RESOLVE); + + // Setup the arguments for the call + ARG_SLOT args[2] = { - // Step 2 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) - Invoke Load method - // This is not invoked for TPA Binder since it always returns NULL. - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::AssemblyLoadContextLoad); + PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance + ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance + }; - // Finally, setup arguments for invocation - MethodDescCallSite methLoadAssembly(METHOD__ASSEMBLYLOADCONTEXT__RESOLVE); + // Make the call + _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methLoadAssembly.Call_RetOBJECTREF(args); + if (_gcRefs.oRefLoadedAssembly != NULL) + { + fResolvedAssembly = true; + } - // Setup the arguments for the call - ARG_SLOT args[2] = - { - PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance - ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance - }; + hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; + + // Step 3 (of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName) + if (!fResolvedAssembly && !isSatelliteAssemblyRequest) + { + tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::DefaultAssemblyLoadContextFallback); - // Make the call - _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methLoadAssembly.Call_RetOBJECTREF(args); - if (_gcRefs.oRefLoadedAssembly != NULL) + // If we could not resolve the assembly using Load method, then attempt fallback with TPA Binder. + // Since TPA binder cannot fallback to itself, this fallback does not happen for binds within TPA binder. + // + // Switch to pre-emp mode before calling into the binder + GCX_PREEMP(); + ICLRPrivAssembly *pCoreCLRFoundAssembly = NULL; + hr = pTPABinder->BindUsingAssemblyName(pAssemblyName, &pCoreCLRFoundAssembly); + if (SUCCEEDED(hr)) { + _ASSERTE(pCoreCLRFoundAssembly != NULL); + pResolvedAssembly = pCoreCLRFoundAssembly; fResolvedAssembly = true; } + } + } - hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; + if (!fResolvedAssembly && isSatelliteAssemblyRequest) + { + // Step 4 (of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName) + // + // Attempt to resolve it using the ResolveSatelliteAssembly method. + // Finally, setup arguments for invocation + tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::ResolveSatelliteAssembly); - // Step 3 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) - if (!fResolvedAssembly && !isSatelliteAssemblyRequest) - { - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::DefaultAssemblyLoadContextFallback); - - // If we could not resolve the assembly using Load method, then attempt fallback with TPA Binder. - // Since TPA binder cannot fallback to itself, this fallback does not happen for binds within TPA binder. - // - // Switch to pre-emp mode before calling into the binder - GCX_PREEMP(); - ICLRPrivAssembly *pCoreCLRFoundAssembly = NULL; - hr = pTPABinder->BindAssemblyByName(pIAssemblyName, &pCoreCLRFoundAssembly); - if (SUCCEEDED(hr)) - { - _ASSERTE(pCoreCLRFoundAssembly != NULL); - pResolvedAssembly = pCoreCLRFoundAssembly; - fResolvedAssembly = true; - } - } - } + MethodDescCallSite methResolveSatelitteAssembly(METHOD__ASSEMBLYLOADCONTEXT__RESOLVESATELLITEASSEMBLY); - if (!fResolvedAssembly && isSatelliteAssemblyRequest) + // Setup the arguments for the call + ARG_SLOT args[2] = { - // Step 4 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) - // - // Attempt to resolve it using the ResolveSatelliteAssembly method. - // Finally, setup arguments for invocation - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::ResolveSatelliteAssembly); + PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance + ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance + }; - MethodDescCallSite methResolveSatelitteAssembly(METHOD__ASSEMBLYLOADCONTEXT__RESOLVESATELLITEASSEMBLY); + // Make the call + _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methResolveSatelitteAssembly.Call_RetOBJECTREF(args); + if (_gcRefs.oRefLoadedAssembly != NULL) + { + // Set the flag indicating we found the assembly + fResolvedAssembly = true; + } - // Setup the arguments for the call - ARG_SLOT args[2] = - { - PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance - ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance - }; + hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; + } - // Make the call - _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methResolveSatelitteAssembly.Call_RetOBJECTREF(args); - if (_gcRefs.oRefLoadedAssembly != NULL) - { - // Set the flag indicating we found the assembly - fResolvedAssembly = true; - } + if (!fResolvedAssembly) + { + // Step 5 (of CLRPrivBinderAssemblyLoadContext::BindAssemblyByName) + // + // If we couldn't resolve the assembly using TPA LoadContext as well, then + // attempt to resolve it using the Resolving event. + // Finally, setup arguments for invocation + tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::AssemblyLoadContextResolvingEvent); - hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; - } + MethodDescCallSite methResolveUsingEvent(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUSINGEVENT); - if (!fResolvedAssembly) + // Setup the arguments for the call + ARG_SLOT args[2] = { - // Step 5 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) - // - // If we couldn't resolve the assembly using TPA LoadContext as well, then - // attempt to resolve it using the Resolving event. - // Finally, setup arguments for invocation - tracer.GoToStage(BinderTracing::ResolutionAttemptedOperation::Stage::AssemblyLoadContextResolvingEvent); - - MethodDescCallSite methResolveUsingEvent(METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUSINGEVENT); + PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance + ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance + }; - // Setup the arguments for the call - ARG_SLOT args[2] = - { - PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance - ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance - }; + // Make the call + _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methResolveUsingEvent.Call_RetOBJECTREF(args); + if (_gcRefs.oRefLoadedAssembly != NULL) + { + // Set the flag indicating we found the assembly + fResolvedAssembly = true; + } - // Make the call - _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methResolveUsingEvent.Call_RetOBJECTREF(args); - if (_gcRefs.oRefLoadedAssembly != NULL) - { - // Set the flag indicating we found the assembly - fResolvedAssembly = true; - } + hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; + } - hr = fResolvedAssembly ? S_OK : COR_E_FILENOTFOUND; + if (fResolvedAssembly && pResolvedAssembly == NULL) + { + // If we are here, assembly was successfully resolved via Load or Resolving events. + _ASSERTE(_gcRefs.oRefLoadedAssembly != NULL); + + // We were able to get the assembly loaded. Now, get its name since the host could have + // performed the resolution using an assembly with different name. + DomainAssembly *pDomainAssembly = _gcRefs.oRefLoadedAssembly->GetDomainAssembly(); + PEAssembly *pLoadedPEAssembly = NULL; + bool fFailLoad = false; + if (!pDomainAssembly) + { + // Reflection emitted assemblies will not have a domain assembly. + fFailLoad = true; } - - if (fResolvedAssembly && pResolvedAssembly == NULL) + else { - // If we are here, assembly was successfully resolved via Load or Resolving events. - _ASSERTE(_gcRefs.oRefLoadedAssembly != NULL); - - // We were able to get the assembly loaded. Now, get its name since the host could have - // performed the resolution using an assembly with different name. - DomainAssembly *pDomainAssembly = _gcRefs.oRefLoadedAssembly->GetDomainAssembly(); - PEAssembly *pLoadedPEAssembly = NULL; - bool fFailLoad = false; - if (!pDomainAssembly) + pLoadedPEAssembly = pDomainAssembly->GetFile(); + if (!pLoadedPEAssembly->HasHostAssembly()) { // Reflection emitted assemblies will not have a domain assembly. fFailLoad = true; } - else - { - pLoadedPEAssembly = pDomainAssembly->GetFile(); - if (!pLoadedPEAssembly->HasHostAssembly()) - { - // Reflection emitted assemblies will not have a domain assembly. - fFailLoad = true; - } - } - - // The loaded assembly's ICLRPrivAssembly* is saved as HostAssembly in PEAssembly - if (fFailLoad) - { - SString name; - spec.GetFileOrDisplayName(0, name); - COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED, name); - } - - pResolvedAssembly = pLoadedPEAssembly->GetHostAssembly(); } - if (fResolvedAssembly) + // The loaded assembly's ICLRPrivAssembly* is saved as HostAssembly in PEAssembly + if (fFailLoad) { - _ASSERTE(pResolvedAssembly != NULL); + PathString name; + pAssemblyName->GetDisplayName(name, BINDER_SPACE::AssemblyName::INCLUDE_ALL); + COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED, name); + } - // Get the ICLRPrivAssembly reference to return back to. - *ppLoadedAssembly = clr::SafeAddRef(pResolvedAssembly); - hr = S_OK; + pResolvedAssembly = pLoadedPEAssembly->GetHostAssembly(); + } - tracer.SetFoundAssembly(static_cast(pResolvedAssembly)); - } - else - { - hr = COR_E_FILENOTFOUND; - } + if (fResolvedAssembly) + { + _ASSERTE(pResolvedAssembly != NULL); + + // Get the ICLRPrivAssembly reference to return back to. + *ppLoadedAssembly = clr::SafeAddRef(pResolvedAssembly); + hr = S_OK; + + tracer.SetFoundAssembly(static_cast(pResolvedAssembly)); } - EX_HOOK + else { - Exception* ex = GET_EXCEPTION(); - tracer.SetException(ex); + hr = COR_E_FILENOTFOUND; } - EX_END_HOOK } + EX_HOOK + { + Exception* ex = GET_EXCEPTION(); + tracer.SetException(ex); + } + EX_END_HOOK GCPROTECT_END(); diff --git a/src/coreclr/vm/appdomain.hpp b/src/coreclr/vm/appdomain.hpp index de75885b68621..9fff8b424965f 100644 --- a/src/coreclr/vm/appdomain.hpp +++ b/src/coreclr/vm/appdomain.hpp @@ -864,7 +864,7 @@ class LoadLevelLimiter void Activate() { WRAPPER_NO_CONTRACT; - m_previousLimit=GetThread()->GetLoadLevelLimiter(); + m_previousLimit= GetThread()->GetLoadLevelLimiter(); if(m_previousLimit) m_currentLevel=m_previousLimit->GetLoadLevel(); GetThread()->SetLoadLevelLimiter(this); diff --git a/src/coreclr/vm/arm/exceparm.cpp b/src/coreclr/vm/arm/exceparm.cpp index d82646cff70fd..59d61b0e1d60b 100644 --- a/src/coreclr/vm/arm/exceparm.cpp +++ b/src/coreclr/vm/arm/exceparm.cpp @@ -61,7 +61,7 @@ AdjustContextForVirtualStub( { LIMITED_METHOD_CONTRACT; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); // We may not have a managed thread object. Example is an AV on the helper thread. // (perhaps during StubManager::IsStub) diff --git a/src/coreclr/vm/arm/pinvokestubs.S b/src/coreclr/vm/arm/pinvokestubs.S index 483816c0dd2cc..8e7c468c36163 100644 --- a/src/coreclr/vm/arm/pinvokestubs.S +++ b/src/coreclr/vm/arm/pinvokestubs.S @@ -105,7 +105,7 @@ str r9, [r4, #InlinedCallFrame__m_pSPAfterProlog] // r0 = GetThread() - bl C_FUNC(GetThread) + bl C_FUNC(GetThreadHelper) str r0, [r4, #InlinedCallFrame__m_pThread] // pFrame->m_Next = pThread->m_pFrame; diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index d41eb948fd945..1ca6fd09642d0 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -1398,7 +1398,7 @@ void StubLinkerCPU::ThumbEmitGetThread(ThumbReg dest) { #ifdef TARGET_UNIX - ThumbEmitMovConstant(ThumbReg(0), (TADDR)GetThread); + ThumbEmitMovConstant(ThumbReg(0), (TADDR)GetThreadHelper); ThumbEmitCallRegister(ThumbReg(0)); diff --git a/src/coreclr/vm/arm64/crthelpers.S b/src/coreclr/vm/arm64/crthelpers.S index 387bfc8a943c6..e123fc82808d1 100644 --- a/src/coreclr/vm/arm64/crthelpers.S +++ b/src/coreclr/vm/arm64/crthelpers.S @@ -12,7 +12,7 @@ LEAF_ENTRY JIT_MemSet, _TEXT cbz x2, LOCAL_LABEL(JIT_MemSet_ret) - strb w1, [x0] + ldrb wzr, [x0] b C_PLTFUNC(memset) @@ -23,7 +23,7 @@ LEAF_END_MARKED JIT_MemSet, _TEXT LEAF_ENTRY JIT_MemCpy, _TEXT cbz x2, LOCAL_LABEL(JIT_MemCpy_ret) - strb wzr, [x0] + ldrb wzr, [x0] ldrb wzr, [x1] b C_PLTFUNC(memcpy) diff --git a/src/coreclr/vm/arm64/pinvokestubs.S b/src/coreclr/vm/arm64/pinvokestubs.S index ff16e14674ef3..e10b81144eed3 100644 --- a/src/coreclr/vm/arm64/pinvokestubs.S +++ b/src/coreclr/vm/arm64/pinvokestubs.S @@ -118,7 +118,7 @@ LOCAL_LABEL(\__PInvokeStubFuncName\()_0): str x9, [x19, #InlinedCallFrame__m_pCalleeSavedFP] // x0 = GetThread() - bl C_FUNC(GetThread) + bl C_FUNC(GetThreadHelper) str x0, [x19, #InlinedCallFrame__m_pThread] // pFrame->m_Next = pThread->m_pFrame; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index f91778c344688..acacdf8af377e 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -1154,7 +1154,7 @@ AdjustContextForVirtualStub( { LIMITED_METHOD_CONTRACT; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); // We may not have a managed thread object. Example is an AV on the helper thread. // (perhaps during StubManager::IsStub) diff --git a/src/coreclr/vm/array.cpp b/src/coreclr/vm/array.cpp index 7b57038c0805c..8ef78b688a95c 100644 --- a/src/coreclr/vm/array.cpp +++ b/src/coreclr/vm/array.cpp @@ -305,12 +305,6 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy _ASSERTE(cbCGCDescData == CGCDesc::ComputeSizeRepeating(nSeries)); } } -#ifdef FEATURE_COLLECTIBLE_TYPES - else if (this->IsCollectible()) - { - cbCGCDescData = (DWORD)CGCDesc::ComputeSize(1); - } -#endif DWORD dwMultipurposeSlotsMask = 0; dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasPerInstInfo; @@ -714,19 +708,6 @@ MethodTable* Module::CreateArrayMethodTable(TypeHandle elemTypeHnd, CorElementTy pSeries->SetSeriesSize(-(SSIZE_T)(pMT->GetBaseSize())); } -#ifdef FEATURE_COLLECTIBLE_TYPES - if (!pMT->ContainsPointers() && this->IsCollectible()) - { - CGCDescSeries *pSeries; - - // For collectible types, insert empty gc series - CGCDesc::GetCGCDescFromMT(pMT)->InitValueClassSeries(pMT, 1); - pSeries = CGCDesc::GetCGCDescFromMT(pMT)->GetHighestSeries(); - pSeries->SetSeriesOffset(ArrayBase::GetDataPtrOffset(pMT)); - pSeries->val_serie[0].set_val_serie_item (0, static_cast(pMT->GetComponentSize())); - } -#endif - // If we get here we are assuming that there was no truncation. If this is not the case then // an array whose base type is not a value class was created and was larger then 0xffff (a word) _ASSERTE(dwComponentSize == pMT->GetComponentSize()); diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index 3c6833f7a2a6e..542de57517f57 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -119,7 +119,6 @@ Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyContr #endif m_nextAvailableModuleIndex(1), m_pLoaderAllocator(NULL), - m_isDisabledPrivateReflection(0), #ifdef FEATURE_COMINTEROP m_pITypeLib(NULL), #endif // FEATURE_COMINTEROP @@ -219,34 +218,6 @@ void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocat } } -BOOL Assembly::IsDisabledPrivateReflection() -{ - CONTRACTL - { - THROWS; - } - CONTRACTL_END; - - enum { UNINITIALIZED, ENABLED, DISABLED}; - - if (m_isDisabledPrivateReflection == UNINITIALIZED) - { - HRESULT hr = GetManifestModule()->GetCustomAttribute(GetManifestToken(), WellKnownAttribute::DisablePrivateReflectionType, NULL, 0); - IfFailThrow(hr); - - if (hr == S_OK) - { - m_isDisabledPrivateReflection = DISABLED; - } - else - { - m_isDisabledPrivateReflection = ENABLED; - } - } - - return m_isDisabledPrivateReflection == DISABLED; -} - #ifndef CROSSGEN_COMPILE Assembly::~Assembly() { @@ -1280,7 +1251,7 @@ void Assembly::UpdateCachedFriendAssemblyInfo() CONTRACTL_END ReleaseHolder pOldFriendAssemblyDescriptor; - + { CrstHolder friendDescriptorLock(&g_friendAssembliesCrst); if (m_pFriendAssemblyDescriptor != NULL) @@ -1362,11 +1333,6 @@ bool Assembly::IgnoresAccessChecksTo(Assembly *pAccessedAssembly) } CONTRACTL_END; - if (pAccessedAssembly->IsDisabledPrivateReflection()) - { - return false; - } - return GetFriendAssemblyInfo()->IgnoresAccessChecksTo(pAccessedAssembly); } @@ -1593,7 +1559,7 @@ static void RunMainPre() { LIMITED_METHOD_CONTRACT; - _ASSERTE(GetThread() != 0); + _ASSERTE(GetThreadNULLOk() != 0); g_fWeControlLifetime = TRUE; } @@ -1605,7 +1571,7 @@ static void RunMainPost() GC_TRIGGERS; MODE_ANY; INJECT_FAULT(COMPlusThrowOM();); - PRECONDITION(CheckPointer(GetThread())); + PRECONDITION(CheckPointer(GetThreadNULLOk())); } CONTRACTL_END @@ -2020,7 +1986,7 @@ bool Assembly::TrySetTypeLib(_In_ ITypeLib *pNew) // Add an assembly to the assemblyref list. pAssemEmitter specifies where // the AssemblyRef is emitted to. //*********************************************************** -mdAssemblyRef Assembly::AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter, BOOL fUsePublicKeyToken) +mdAssemblyRef Assembly::AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter) { CONTRACT(mdAssemblyRef) { @@ -2048,7 +2014,7 @@ mdAssemblyRef Assembly::AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssembl } mdAssemblyRef ar; - IfFailThrow(spec.EmitToken(pAssemEmitter, &ar, fUsePublicKeyToken)); + IfFailThrow(spec.EmitToken(pAssemEmitter, &ar)); RETURN ar; } // Assembly::AddAssemblyRef diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index 2dad285a7de72..3e5447ffffbee 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -373,8 +373,6 @@ class Assembly return GetManifestFile()->HashIdentity(); } - BOOL IsDisabledPrivateReflection(); - //**************************************************************************************** // // Uses the given token to load a module or another assembly. Returns the module in @@ -428,7 +426,7 @@ class Assembly void AddType(Module* pModule, mdTypeDef cl); void AddExportedType(mdExportedType cl); - mdAssemblyRef AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter = NULL, BOOL fUsePublicKeyToken = TRUE); + mdAssemblyRef AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter); //**************************************************************************************** @@ -583,7 +581,6 @@ class Assembly #endif // FEATURE_COLLECTIBLE_TYPES DWORD m_nextAvailableModuleIndex; PTR_LoaderAllocator m_pLoaderAllocator; - DWORD m_isDisabledPrivateReflection; #ifdef FEATURE_COMINTEROP // If a TypeLib is ever required for this module, cache the pointer here. diff --git a/src/coreclr/vm/assemblyloadcontext.cpp b/src/coreclr/vm/assemblyloadcontext.cpp index eed482ce830b7..1ec1a1eeeb6c8 100644 --- a/src/coreclr/vm/assemblyloadcontext.cpp +++ b/src/coreclr/vm/assemblyloadcontext.cpp @@ -24,6 +24,26 @@ NativeImage *AssemblyLoadContext::LoadNativeImage(Module *componentModule, LPCUT AssemblyLoadContext *loadContext = componentModule->GetFile()->GetAssemblyLoadContext(); PTR_LoaderAllocator moduleLoaderAllocator = componentModule->GetLoaderAllocator(); - return NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator); + NativeImage *nativeImage = NativeImage::Open(componentModule, nativeImageName, loadContext, moduleLoaderAllocator); + m_nativeImages.Append(nativeImage); + + for (COUNT_T assemblyIndex = 0; assemblyIndex < m_loadedAssemblies.GetCount(); assemblyIndex++) + { + nativeImage->CheckAssemblyMvid(m_loadedAssemblies[assemblyIndex]); + } + + return nativeImage; +} +#endif + +#ifndef DACCESS_COMPILE +void AssemblyLoadContext::AddLoadedAssembly(Assembly *loadedAssembly) +{ + BaseDomain::LoadLockHolder lock(AppDomain::GetCurrentDomain()); + m_loadedAssemblies.Append(loadedAssembly); + for (COUNT_T nativeImageIndex = 0; nativeImageIndex < m_nativeImages.GetCount(); nativeImageIndex++) + { + m_nativeImages[nativeImageIndex]->CheckAssemblyMvid(loadedAssembly); + } } #endif diff --git a/src/coreclr/vm/assemblyloadcontext.h b/src/coreclr/vm/assemblyloadcontext.h index 9455567b7a52e..1ecdd0446d29a 100644 --- a/src/coreclr/vm/assemblyloadcontext.h +++ b/src/coreclr/vm/assemblyloadcontext.h @@ -5,10 +5,13 @@ #define _ASSEMBLYLOADCONTEXT_H #include "crst.h" +#include #include + class NativeImage; class Module; +class Assembly; // // Unmanaged counter-part of System.Runtime.Loader.AssemblyLoadContext @@ -23,6 +26,8 @@ class AssemblyLoadContext : public IUnknownCommon m_nativeImages; + SArray m_loadedAssemblies; }; #endif diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 165462e2a5164..e0098726cfe3e 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -1435,7 +1435,7 @@ void QCALLTYPE AssemblyNative::ApplyUpdate( { if (CORDebuggerAttached()) { - COMPlusThrow(kNotSupportedException); + COMPlusThrow(kNotSupportedException, W("NotSupported_DebuggerAttached")); } Module* pModule = assembly->GetDomainAssembly()->GetModule(); if (!pModule->IsEditAndContinueEnabled()) @@ -1447,6 +1447,7 @@ void QCALLTYPE AssemblyNative::ApplyUpdate( { COMPlusThrow(kInvalidOperationException, W("InvalidOperation_EditFailed")); } + g_metadataUpdatesApplied = true; } #else COMPlusThrow(kNotImplementedException); diff --git a/src/coreclr/vm/assemblyspec.cpp b/src/coreclr/vm/assemblyspec.cpp index 946f5856f7ca2..7152b5d239342 100644 --- a/src/coreclr/vm/assemblyspec.cpp +++ b/src/coreclr/vm/assemblyspec.cpp @@ -580,6 +580,35 @@ void AssemblySpec::AssemblyNameInit(ASSEMBLYNAMEREF* pAsmName, PEImage* pImageIn GCPROTECT_END(); } +/* static */ +void AssemblySpec::InitializeAssemblyNameRef(_In_ BINDER_SPACE::AssemblyName* assemblyName, _Out_ ASSEMBLYNAMEREF* assemblyNameRef) +{ + CONTRACTL + { + THROWS; + MODE_COOPERATIVE; + GC_TRIGGERS; + PRECONDITION(assemblyName != NULL); + PRECONDITION(IsProtectedByGCFrame(assemblyNameRef)); + } + CONTRACTL_END; + + AssemblySpec spec; + spec.InitializeWithAssemblyIdentity(assemblyName); + + StackScratchBuffer nameBuffer; + spec.SetName(assemblyName->GetSimpleName().GetUTF8(nameBuffer)); + + StackScratchBuffer cultureBuffer; + if (assemblyName->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE)) + { + LPCSTR culture = assemblyName->IsNeutralCulture() ? "" : assemblyName->GetCulture().GetUTF8(cultureBuffer); + spec.SetCulture(culture); + } + + spec.AssemblyNameInit(assemblyNameRef, NULL); +} + #endif // CROSSGEN_COMPILE // Check if the supplied assembly's public key matches up with the one in the Spec, if any @@ -859,9 +888,7 @@ HRESULT AssemblySpec::CheckFriendAssemblyName() HRESULT AssemblySpec::EmitToken( IMetaDataAssemblyEmit *pEmit, - mdAssemblyRef *pToken, - BOOL fUsePublicKeyToken, /*=TRUE*/ - BOOL fMustBeBindable /*=FALSE*/) + mdAssemblyRef *pToken) { CONTRACTL { @@ -902,7 +929,7 @@ HRESULT AssemblySpec::EmitToken( // If we've been asked to emit a public key token in the reference but we've // been given a public key then we need to generate the token now. - if (m_cbPublicKeyOrToken && fUsePublicKeyToken && IsAfPublicKey(m_dwFlags)) { + if (m_cbPublicKeyOrToken && IsAfPublicKey(m_dwFlags)) { StrongNameBufferHolder pbPublicKeyToken; DWORD cbPublicKeyToken; IfFailThrow(StrongNameTokenFromPublicKey(m_pbPublicKeyOrToken, @@ -939,11 +966,6 @@ HRESULT AssemblySpec::EmitToken( return hr; } -//=========================================================================================== -// Constructs an AssemblySpec for the given IAssemblyName. Recognizes IAssemblyName objects -// that were built from WinRT AssemblySpec objects, extracts the encoded type name, and sets -// the type namespace and class name properties appropriately. - AssemblySpecBindingCache::AssemblySpecBindingCache() { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/assemblyspec.hpp b/src/coreclr/vm/assemblyspec.hpp index 70f7b9ac760d6..e7dcc4a6235e9 100644 --- a/src/coreclr/vm/assemblyspec.hpp +++ b/src/coreclr/vm/assemblyspec.hpp @@ -185,15 +185,10 @@ class AssemblySpec : public BaseAssemblySpec m_dwHashAlg = pSource->m_dwHashAlg; } - HRESULT CheckFriendAssemblyName(); - HRESULT EmitToken(IMetaDataAssemblyEmit *pEmit, - mdAssemblyRef *pToken, - BOOL fUsePublicKeyToken = TRUE, - BOOL fMustBeBindable = FALSE /*(used only by FusionBind's implementation)*/); - + mdAssemblyRef *pToken); VOID Bind( AppDomain* pAppDomain, @@ -207,8 +202,7 @@ class AssemblySpec : public BaseAssemblySpec DomainAssembly *LoadDomainAssembly(FileLoadLevel targetLevel, BOOL fThrowOnFileNotFound = TRUE); - //**************************************************************************************** - // + public: // static // Creates and loads an assembly based on the name and context. static Assembly *LoadAssembly(LPCSTR pSimpleName, AssemblyMetaDataInternal* pContext, @@ -216,10 +210,12 @@ class AssemblySpec : public BaseAssemblySpec DWORD cbPublicKeyOrToken, DWORD dwFlags); - // Load an assembly based on an explicit path static Assembly *LoadAssembly(LPCWSTR pFilePath); + // Initialize an AssemblyName managed object based on the specified assemblyName + static void InitializeAssemblyNameRef(_In_ BINDER_SPACE::AssemblyName* assemblyName, _Out_ ASSEMBLYNAMEREF* assemblyNameRef); + public: void MatchPublicKeys(Assembly *pAssembly); diff --git a/src/coreclr/vm/baseassemblyspec.cpp b/src/coreclr/vm/baseassemblyspec.cpp index 5a57c0ba5d3e7..bab0fe7fa7776 100644 --- a/src/coreclr/vm/baseassemblyspec.cpp +++ b/src/coreclr/vm/baseassemblyspec.cpp @@ -13,8 +13,6 @@ #include "common.h" #include "thekey.h" -#include "../binder/inc/fusionassemblyname.hpp" - #include "strongnameinternal.h" #include "strongnameholders.h" @@ -364,181 +362,4 @@ VOID BaseAssemblySpec::SetName(SString const & ssName) m_ownedFlags |= NAME_OWNED; } -HRESULT BaseAssemblySpec::Init(IAssemblyName *pName) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(pName); - - HRESULT hr; - - // Fill out info from name, if we have it. - - DWORD cbSize = 0; - hr=pName->GetProperty(ASM_NAME_NAME, NULL, &cbSize); - if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { - hr=S_OK; - CQuickBytes qb; - LPWSTR pwName = (LPWSTR) qb.AllocNoThrow(cbSize); - if (!pwName) - return E_OUTOFMEMORY; - - IfFailRet(pName->GetProperty(ASM_NAME_NAME, pwName, &cbSize)); - - m_pAssemblyName = NULL; - - hr = FString::ConvertUnicode_Utf8(pwName, & ((LPSTR &) m_pAssemblyName)); - - if (FAILED(hr)) - { - return hr; - } - - m_ownedFlags |= NAME_OWNED; - } - IfFailRet(hr); - - // Note: cascade checks so we don't set lower priority version #'s if higher ones are missing - cbSize = sizeof(m_context.usMajorVersion); - hr=pName->GetProperty(ASM_NAME_MAJOR_VERSION, &m_context.usMajorVersion, &cbSize); - - if (hr!=S_OK || !cbSize) - m_context.usMajorVersion = (USHORT) -1; - else { - cbSize = sizeof(m_context.usMinorVersion); - hr=pName->GetProperty(ASM_NAME_MINOR_VERSION, &m_context.usMinorVersion, &cbSize); - } - - if (hr!=S_OK || !cbSize) - m_context.usMinorVersion = (USHORT) -1; - else { - cbSize = sizeof(m_context.usBuildNumber); - pName->GetProperty(ASM_NAME_BUILD_NUMBER, &m_context.usBuildNumber, &cbSize); - } - - if (hr!=S_OK || !cbSize) - m_context.usBuildNumber = (USHORT) -1; - else { - cbSize = sizeof(m_context.usRevisionNumber); - pName->GetProperty(ASM_NAME_REVISION_NUMBER, &m_context.usRevisionNumber, &cbSize); - } - - if (hr!=S_OK || !cbSize) - m_context.usRevisionNumber = (USHORT) -1; - - if (hr==E_INVALIDARG) - hr=S_FALSE; - - IfFailRet(hr); - - cbSize = 0; - hr = pName->GetProperty(ASM_NAME_CULTURE, NULL, &cbSize); - - if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { - LPWSTR pwName = (LPWSTR) alloca(cbSize); - IfFailRet(pName->GetProperty(ASM_NAME_CULTURE, pwName, &cbSize)); - - hr = FString::ConvertUnicode_Utf8(pwName, & ((LPSTR &) m_context.szLocale)); - - m_ownedFlags |= LOCALE_OWNED; - } - - IfFailRet(hr); - - m_dwFlags = 0; - - cbSize = 0; - hr=pName->GetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, NULL, &cbSize); - if (hr== HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { - m_pbPublicKeyOrToken = new (nothrow) BYTE[cbSize]; - if (m_pbPublicKeyOrToken == NULL) - return E_OUTOFMEMORY; - m_cbPublicKeyOrToken = cbSize; - m_ownedFlags |= PUBLIC_KEY_OR_TOKEN_OWNED; - IfFailRet(pName->GetProperty(ASM_NAME_PUBLIC_KEY_TOKEN, m_pbPublicKeyOrToken, &cbSize)); - } - else { - if (hr!=E_INVALIDARG) - IfFailRet(hr); - hr=pName->GetProperty(ASM_NAME_PUBLIC_KEY, NULL, &cbSize); - if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { - hr=S_OK; - // @todo: we need to normalize this into a public key token so - // comparisons work correctly. But this involves binding to mscorsn. - m_pbPublicKeyOrToken = new (nothrow) BYTE[cbSize]; - if (m_pbPublicKeyOrToken == NULL) - return E_OUTOFMEMORY; - m_cbPublicKeyOrToken = cbSize; - m_dwFlags |= afPublicKey; - m_ownedFlags |= PUBLIC_KEY_OR_TOKEN_OWNED; - IfFailRet(pName->GetProperty(ASM_NAME_PUBLIC_KEY, m_pbPublicKeyOrToken, &cbSize)); - } - else { - IfFailRet(hr); - hr= pName->GetProperty(ASM_NAME_NULL_PUBLIC_KEY, NULL, &cbSize); - if (hr!=S_OK) - hr=pName->GetProperty(ASM_NAME_NULL_PUBLIC_KEY_TOKEN, NULL, &cbSize); - if ( hr == S_OK ) { - m_pbPublicKeyOrToken = new (nothrow) BYTE[0]; - if (m_pbPublicKeyOrToken == NULL) - return E_OUTOFMEMORY; - m_cbPublicKeyOrToken = 0; - m_ownedFlags |= PUBLIC_KEY_OR_TOKEN_OWNED; - } - if (hr==E_INVALIDARG) - hr=S_FALSE; - IfFailRet(hr); - - } - } - - // Recover the afRetargetable flag - BOOL bRetarget; - cbSize = sizeof(bRetarget); - hr = pName->GetProperty(ASM_NAME_RETARGET, &bRetarget, &cbSize); - if (hr == S_OK && cbSize != 0 && bRetarget) - m_dwFlags |= afRetargetable; - - // Recover the Processor Architecture flags - PEKIND peKind; - cbSize = sizeof(PEKIND); - hr = pName->GetProperty(ASM_NAME_ARCHITECTURE, &peKind, &cbSize); - if ((hr == S_OK) && (cbSize != 0) && (peKind < (afPA_NoPlatform >> afPA_Shift)) && (peKind >= (afPA_MSIL >> afPA_Shift))) - m_dwFlags |= (((DWORD)peKind) << afPA_Shift); - - cbSize = 0; - hr=pName->GetProperty(ASM_NAME_CODEBASE_URL, NULL, &cbSize); - if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { - m_wszCodeBase = new (nothrow) WCHAR [ cbSize/sizeof(WCHAR) ]; - if (m_wszCodeBase == NULL) - return E_OUTOFMEMORY; - m_ownedFlags |= CODE_BASE_OWNED; - IfFailRet(pName->GetProperty(ASM_NAME_CODEBASE_URL, - (void*)m_wszCodeBase, &cbSize)); - } - else - IfFailRet(hr); - - // Recover the Content Type enum - DWORD dwContentType; - cbSize = sizeof(dwContentType); - hr = pName->GetProperty(ASM_NAME_CONTENT_TYPE, &dwContentType, &cbSize); - if ((hr == S_OK) && (cbSize == sizeof(dwContentType))) - { - _ASSERTE((dwContentType == AssemblyContentType_Default) || (dwContentType == AssemblyContentType_WindowsRuntime)); - if (dwContentType == AssemblyContentType_WindowsRuntime) - { - m_dwFlags |= afContentType_WindowsRuntime; - } - } - - return S_OK; -} - #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/baseassemblyspec.h b/src/coreclr/vm/baseassemblyspec.h index 150368c1c447c..06ac705df06e5 100644 --- a/src/coreclr/vm/baseassemblyspec.h +++ b/src/coreclr/vm/baseassemblyspec.h @@ -56,8 +56,6 @@ class BaseAssemblySpec HRESULT Init(mdAssembly tkAssemblyRef, IMetaDataAssemblyImport* pImport); HRESULT Init(LPCSTR pAssemblyDisplayName); - HRESULT Init(IAssemblyName *pName); - // Note that this method does not clone the fields! VOID CopyFrom(const BaseAssemblySpec *pSpec); @@ -94,7 +92,6 @@ class BaseAssemblySpec void SetCodeBase(LPCWSTR szCodeBase); VOID SetCulture(LPCSTR szCulture); - bool IsNeutralCulture(); VOID ConvertPublicKeyToToken(); @@ -129,9 +126,13 @@ class BaseAssemblySpec void GetFileOrDisplayName(DWORD flags, SString &result) const; void GetDisplayName(DWORD flags, SString &result) const; -protected: +protected: // static static BOOL CompareRefToDef(const BaseAssemblySpec *pRef, const BaseAssemblySpec *pDef); +protected: + void InitializeWithAssemblyIdentity(BINDER_SPACE::AssemblyIdentity *identity); + void PopulateAssemblyNameData(AssemblyNameData &data) const; + private: void GetDisplayNameInternal(DWORD flags, SString &result) const; }; diff --git a/src/coreclr/vm/baseassemblyspec.inl b/src/coreclr/vm/baseassemblyspec.inl index 5e1496b5986e1..e90d2f0d9eec1 100644 --- a/src/coreclr/vm/baseassemblyspec.inl +++ b/src/coreclr/vm/baseassemblyspec.inl @@ -514,11 +514,6 @@ inline void BaseAssemblySpec::SetCulture(LPCSTR szCulture) m_context.szLocale=szCulture; } -inline bool BaseAssemblySpec::IsNeutralCulture() -{ - return strcmp(m_context.szLocale,"")==0; -} - inline void BaseAssemblySpec::SetContext(ASSEMBLYMETADATA* assemblyData) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/binder.cpp b/src/coreclr/vm/binder.cpp index 3dbd866059672..25c4f540fbeda 100644 --- a/src/coreclr/vm/binder.cpp +++ b/src/coreclr/vm/binder.cpp @@ -480,13 +480,13 @@ void CoreLibBinder::TriggerGCUnderStress() CONTRACTL_END; #ifndef DACCESS_COMPILE - _ASSERTE (GetThread ()); + _ASSERTE (GetThreadNULLOk()); TRIGGERSGC (); // Force a GC here because GetClass could trigger GC nondeterminsticly if (g_pConfig->GetGCStressLevel() != 0) { DEBUG_ONLY_REGION(); - Thread * pThread = GetThread (); + Thread * pThread = GetThread(); BOOL bInCoopMode = pThread->PreemptiveGCDisabled (); GCX_COOP (); if (bInCoopMode) diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index dfecf88defd16..1a19c43a67fa5 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -822,7 +822,7 @@ void CallCountingManager::CompleteCallCounting() } CONTRACTL_END; - _ASSERTE(GetThread() == TieredCompilationManager::GetBackgroundWorkerThread()); + _ASSERTE(GetThreadNULLOk() == TieredCompilationManager::GetBackgroundWorkerThread()); AppDomain *appDomain = GetAppDomain(); TieredCompilationManager *tieredCompilationManager = appDomain->GetTieredCompilationManager(); @@ -943,7 +943,7 @@ void CallCountingManager::StopAndDeleteAllCallCountingStubs() } CONTRACTL_END; - _ASSERTE(GetThread() == TieredCompilationManager::GetBackgroundWorkerThread()); + _ASSERTE(GetThreadNULLOk() == TieredCompilationManager::GetBackgroundWorkerThread()); // If a number of call counting stubs have completed, we can try to delete them to reclaim some memory. Deleting // involves suspending the runtime and will delete all call counting stubs, and after that some call counting stubs may @@ -1001,7 +1001,7 @@ void CallCountingManager::StopAllCallCounting(TieredCompilationManager *tieredCo } CONTRACTL_END; - _ASSERTE(GetThread() == TieredCompilationManager::GetBackgroundWorkerThread()); + _ASSERTE(GetThreadNULLOk() == TieredCompilationManager::GetBackgroundWorkerThread()); _ASSERTE(MethodDescBackpatchInfoTracker::IsLockOwnedByCurrentThread()); _ASSERTE(CodeVersionManager::IsLockOwnedByCurrentThread()); _ASSERTE(tieredCompilationManager != nullptr); diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index d5493b661bc32..fa32e4e299013 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -106,7 +106,6 @@ void CallDescrWorker(CallDescrData * pCallDescrData) DWORD_PTR ObjRefTable[OBJREF_TABSIZE]; curThread = GetThread(); - _ASSERTE(curThread != NULL); static_assert_no_msg(sizeof(curThread->dangerousObjRefs) == sizeof(ObjRefTable)); memcpy(ObjRefTable, curThread->dangerousObjRefs, sizeof(ObjRefTable)); diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 4a71037c89c65..f84714c9ce673 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -11855,7 +11855,7 @@ HRESULT Module::WriteMethodProfileDataLogFile(bool cleanup) { if (GetAssembly()->IsInstrumented() && (m_pProfilingBlobTable != NULL) && (m_tokenProfileData != NULL)) { - ProfileEmitter * pEmitter = new ProfileEmitter(); + NewHolder pEmitter(new ProfileEmitter()); // Get this ahead of time - metadata access may be logged, which will // take the m_tokenProfileData->crst, which we take a couple lines below @@ -12520,7 +12520,7 @@ void ReflectionModule::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) Module::Initialize(pamTracker); - IfFailThrow(CreateICeeGen(IID_ICeeGen, (void **)&m_pCeeFileGen)); + IfFailThrow(CreateICeeGen(IID_ICeeGenInternal, (void **)&m_pCeeFileGen)); // Collectible modules should try to limit the growth of their associate IL section, as common scenarios for collectible // modules include single type modules diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 073b567e44171..1f4b9b3b4ac6d 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -100,9 +100,7 @@ extern VerboseLevel g_CorCompileVerboseLevel; #elif defined(HOST_ARM) #define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm.dll") #elif defined(HOST_ARM64) -// Use diasymreader until the package has an arm64 version - issue #7360 -//#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm64.dll") -#define NATIVE_SYMBOL_READER_DLL W("diasymreader.dll") +#define NATIVE_SYMBOL_READER_DLL W("Microsoft.DiaSymReader.Native.arm64.dll") #endif typedef DPTR(PersistentInlineTrackingMapNGen) PTR_PersistentInlineTrackingMapNGen; @@ -3210,7 +3208,7 @@ class ReflectionModule : public Module HCEESECTION m_sdataSection; protected: - ICeeGen * m_pCeeFileGen; + ICeeGenInternal * m_pCeeFileGen; private: Assembly *m_pCreatingAssembly; ISymUnmanagedWriter *m_pISymUnmanagedWriter; @@ -3286,7 +3284,7 @@ class ReflectionModule : public Module m_pCreatingAssembly = assembly; } - ICeeGen *GetCeeGen() {LIMITED_METHOD_CONTRACT; return m_pCeeFileGen; } + ICeeGenInternal *GetCeeGen() {LIMITED_METHOD_CONTRACT; return m_pCeeFileGen; } RefClassWriter *GetClassWriter() { diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 3561f0e3689e5..77120d5f81d07 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -205,9 +205,6 @@ #include "interpreter.h" #endif // FEATURE_INTERPRETER -#include "../binder/inc/coreclrbindercommon.h" - - #ifdef FEATURE_PERFMAP #include "perfmap.h" #endif @@ -305,12 +302,8 @@ HRESULT EnsureEEStarted() { BEGIN_ENTRYPOINT_NOTHROW; -#ifndef TARGET_UNIX - // The sooner we do this, the sooner we avoid probing registry entries. - // (Perf Optimization for VSWhidbey:113373.) - REGUTIL::InitOptionalConfigCache(); -#endif - + // Initialize our configuration. + CLRConfig::Initialize(); BOOL bStarted=FALSE; @@ -705,12 +698,12 @@ void EEStartupHelper() #endif // TARGET_UNIX #ifdef STRESS_LOG - if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, g_pConfig->StressLog ()) != 0) { - unsigned facilities = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LF_ALL); - unsigned level = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); - unsigned bytesPerThread = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 4); - ULONGLONG totalBytes = REGUTIL::GetConfigULONGLONG_DontUse_(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); - LPWSTR logFilename = REGUTIL::GetConfigString_DontUse_(CLRConfig::UNSUPPORTED_StressLogFilename); + if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, g_pConfig->StressLog()) != 0) { + unsigned facilities = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFacility, LF_ALL); + unsigned level = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000); + unsigned bytesPerThread = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 4); + unsigned totalBytes = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024); + CLRConfigStringHolder logFilename = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLogFilename); StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, GetClrModuleBase(), logFilename); g_pStressLog = &StressLog::theLog; } @@ -763,9 +756,6 @@ void EEStartupHelper() InitEventStore(); #endif - // Initialize the default Assembly Binder and the binder infrastructure - IfFailGoLog(CCoreCLRBinderHelper::Init()); - if (g_pConfig != NULL) { IfFailGoLog(g_pConfig->sync()); @@ -1206,7 +1196,7 @@ void WaitForEndOfShutdown() // We are shutting down. GC triggers does not have any effect now. CONTRACT_VIOLATION(GCViolation); - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // After a thread is blocked in WaitForEndOfShutdown, the thread should not enter runtime again, // and block at WaitForEndOfShutdown again. if (pThread) @@ -1259,7 +1249,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading) #if defined(FEATURE_COMINTEROP) // Get the current thread. - Thread * pThisThread = GetThread(); + Thread * pThisThread = GetThreadNULLOk(); #endif // If the process is detaching then set the global state. @@ -1718,7 +1708,7 @@ void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading) #endif } - if (GetThread()) + if (GetThreadNULLOk()) { GCX_COOP(); EEShutDownHelper(fIsDllUnloading); @@ -1896,7 +1886,7 @@ struct TlsDestructionMonitor // Don't destroy threads here if we're in shutdown (shutdown will // clean up for us instead). - Thread* thread = GetThread(); + Thread* thread = GetThreadNULLOk(); if (thread) { #ifdef FEATURE_COMINTEROP @@ -2074,7 +2064,7 @@ static HRESULT GetThreadUICultureNames(__inout StringArrayList* pCultureNames) InlineSString sParentCulture; #if 0 // Enable and test if/once the unmanaged runtime is localized - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); // When fatal errors have occured our invariants around GC modes may be broken and attempting to transition to co-op may hang // indefinately. We want to ensure a clean exit so rather than take the risk of hang we take a risk of the error resource not @@ -2203,7 +2193,7 @@ static int GetThreadUICultureId(__out LocaleIDValue* pLocale) int Result = 0; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); #if 0 // Enable and test if/once the unmanaged runtime is localized // When fatal errors have occured our invariants around GC modes may be broken and attempting to transition to co-op may hang diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index f947e85dab7aa..f2ff4333ced3f 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -2439,7 +2439,7 @@ MethodTable::DebugDumpGCDesc( LOG((LF_ALWAYS, LL_ALWAYS, "GC description for '%s':\n\n", pszClassName)); } - if (ContainsPointersOrCollectible()) + if (ContainsPointers()) { CGCDescSeries *pSeries; CGCDescSeries *pHighest; diff --git a/src/coreclr/vm/classcompat.cpp b/src/coreclr/vm/classcompat.cpp index 4f00fbf944814..93d5d44ff6f6f 100644 --- a/src/coreclr/vm/classcompat.cpp +++ b/src/coreclr/vm/classcompat.cpp @@ -607,9 +607,6 @@ VOID MethodTableBuilder::BuildInteropVTable_InterfaceList( *pcBuildingInterfaceList = 0; *ppBuildingInterfaceList = NULL; - // Get the thread for stacking allocator - Thread *pThread = GetThread(); - // Get the metadata for enumerating the interfaces of the class IMDInternalImport *pMDImport = GetModule()->GetMDImport(); @@ -1048,7 +1045,6 @@ VOID MethodTableBuilder::BuildInteropVTable_ResolveInterfaces( HRESULT hr = S_OK; DWORD i; - Thread *pThread = GetThread(); // resolve unresolved interfaces, determine an upper bound on the size of the interface map, // and determine the size of the largest interface (in # slots) @@ -1754,7 +1750,6 @@ VOID MethodTableBuilder::BuildInteropVTable_PlaceInterfaceDeclaration( // laying out the method table. if(bmtInterface->pdwOriginalStart == NULL) { - Thread *pThread = GetThread(); bmtInterface->pdwOriginalStart = new (GetStackingAllocator()) DWORD[bmtInterface->dwMaxExpandedInterfaces]; memset(bmtInterface->pdwOriginalStart, 0, sizeof(DWORD)*bmtInterface->dwMaxExpandedInterfaces); } diff --git a/src/coreclr/vm/clrex.cpp b/src/coreclr/vm/clrex.cpp index 8e4e1e91861e0..aca0fe2678122 100644 --- a/src/coreclr/vm/clrex.cpp +++ b/src/coreclr/vm/clrex.cpp @@ -622,10 +622,8 @@ OBJECTREF CLRException::GetThrowableFromException(Exception *pException) } CONTRACTL_END; - Thread* pThread = GetThread(); - // Can't have a throwable without a Thread. - _ASSERTE(pThread != NULL); + Thread* pThread = GetThread(); if (NULL == pException) { @@ -814,7 +812,7 @@ void CLRException::HandlerState::CleanupTry() if (m_pThread != NULL) { - BEGIN_GETTHREAD_ALLOWED; + // If there is no frame to unwind, UnwindFrameChain call is just an expensive NOP // due to setting up and tear down of EH records. So we avoid it if we can. if (m_pThread->GetFrame() < m_pFrame) @@ -827,7 +825,7 @@ void CLRException::HandlerState::CleanupTry() else m_pThread->EnablePreemptiveGC(); } - END_GETTHREAD_ALLOWED; + } // Make sure to call the base class's CleanupTry so it can do whatever it wants to do. @@ -848,7 +846,7 @@ void CLRException::HandlerState::SetupCatch(INDEBUG_COMMA(__in_z const char * sz if (g_fEEStarted) { - pThread = GetThread(); + pThread = GetThreadNULLOk(); exceptionCode = GetCurrentExceptionCode(); } @@ -1359,7 +1357,7 @@ OBJECTREF EEArgumentException::CreateThrowable() } CONTRACTL_END; - _ASSERTE(GetThread() != NULL); + _ASSERTE(GetThreadNULLOk() != NULL); ProtectArgsStruct prot; memset(&prot, 0, sizeof(ProtectArgsStruct)); @@ -2237,7 +2235,7 @@ void GetLastThrownObjectExceptionFromThread(Exception **ppException) CONTRACTL_END; // If the Thread has been set up, then the LastThrownObject may make sense... - if (GetThread()) + if (GetThreadNULLOk()) { // give back an object that knows about Threads and their exceptions. *ppException = new CLRLastThrownObjectException(); diff --git a/src/coreclr/vm/clrex.h b/src/coreclr/vm/clrex.h index 2ed24e11c527e..2bdf824b6f066 100644 --- a/src/coreclr/vm/clrex.h +++ b/src/coreclr/vm/clrex.h @@ -752,7 +752,7 @@ LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv); #define PAL_SEH_RESTORE_GUARD_PAGE \ if (__exCode == STATUS_STACK_OVERFLOW) \ { \ - Thread *__pThread = GetThread(); \ + Thread *__pThread = GetThreadNULLOk(); \ if (__pThread != NULL) \ { \ __pThread->RestoreGuardPage(); \ @@ -799,7 +799,7 @@ LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv); STRESS_LOG1(LF_EH, LL_INFO100, \ "EX_RETHROW " INDEBUG(__FILE__) " line %d\n", __LINE__); \ __pException.SuppressRelease(); \ - if ((!__state.DidCatchCxx()) && (GetThread() != NULL)) \ + if ((!__state.DidCatchCxx()) && (GetThreadNULLOk() != NULL)) \ { \ if (GetThread()->PreemptiveGCDisabled()) \ { \ @@ -908,7 +908,7 @@ LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv); { \ HRESULT *__phr = (phresult); \ *__phr = S_OK; \ - _ASSERTE(GetThread() == NULL || \ + _ASSERTE(GetThreadNULLOk() == NULL || \ !GetThread()->PreemptiveGCDisabled()); \ MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThreadNULLOk()); \ if (CURRENT_THREAD == NULL) \ diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp index 26ae406dab8f7..edf5d64491eb9 100644 --- a/src/coreclr/vm/clrtocomcall.cpp +++ b/src/coreclr/vm/clrtocomcall.cpp @@ -836,7 +836,7 @@ TADDR ComPlusCall::GetFrameCallIP(FramedMethodFrame *frame) #ifndef DACCESS_COMPILE - Thread* thread = GetThread(); + Thread* thread = GetThreadNULLOk(); if (thread == NULL) { // diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index a7152d3400a31..65692222bb3e1 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -214,7 +214,7 @@ PTR_Module ClassLoader::ComputeLoaderModuleForCompilation( // If this instantiation doesn't have a unique home then use the ngen module // OK, we're certainly NGEN'ing. And if we're NGEN'ing then we're not on the debugger thread. - CONSISTENCY_CHECK(((GetThread() && GetAppDomain()) || IsGCThread()) && + CONSISTENCY_CHECK(((GetThreadNULLOk() && GetAppDomain()) || IsGCThread()) && "unexpected: running a load on debug thread but IsCompilationProcess() returned TRUE"); // Save it into its PreferredZapModule if it's always going to be saved there. @@ -4432,16 +4432,6 @@ BOOL AccessCheckOptions::DemandMemberAccess(AccessCheckContext *pContext, Method return FALSE; } - if (pTargetMT && pTargetMT->GetAssembly()->IsDisabledPrivateReflection()) - { - if (m_fThrowIfTargetIsInaccessible) - { - ThrowAccessException(pContext, pTargetMT, NULL); - } - - return FALSE; - } - BOOL canAccessTarget = FALSE; #ifndef CROSSGEN_COMPILE diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 97a4d5a7cffb8..d0d4d589fe798 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -951,9 +951,9 @@ ExecutionManager::ScanFlag ExecutionManager::GetScanFlags() } CONTRACTL_END; #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) - BEGIN_GETTHREAD_ALLOWED; + - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (!pThread) return ScanNoReaderLock; @@ -966,7 +966,7 @@ ExecutionManager::ScanFlag ExecutionManager::GetScanFlags() if (pThread->PreemptiveGCDisabled() || (pThread == ThreadSuspend::GetSuspensionThread())) return ScanNoReaderLock; - END_GETTHREAD_ALLOWED; + return ScanReaderLock; #else @@ -3448,6 +3448,10 @@ void EEJitManager::CleanupCodeHeaps() HostCodeHeap *pHeap = m_cleanupList; m_cleanupList = NULL; +#if defined(HOST_OSX) && defined(HOST_ARM64) + auto jitWriteEnableHolder = PAL_JITWriteEnable(true); +#endif // defined(HOST_OSX) && defined(HOST_ARM64) + while (pHeap) { HostCodeHeap *pNextHeap = pHeap->m_pNextHeapToRelease; diff --git a/src/coreclr/vm/comcache.cpp b/src/coreclr/vm/comcache.cpp index 689b6411b1c0d..bdfb34497cdfd 100644 --- a/src/coreclr/vm/comcache.cpp +++ b/src/coreclr/vm/comcache.cpp @@ -310,7 +310,6 @@ CtxEntry* CtxEntryCache::CreateCtxEntry(LPVOID pCtxCookie, Thread * pSTAThread) CONTRACTL_END; CtxEntry * pCtxEntry = NULL; - Thread * pThread = GetThread(); // If we don't already have a context entry for the context cookie, // we need to create one. @@ -374,7 +373,7 @@ CtxEntry* CtxEntryCache::FindCtxEntry(LPVOID pCtxCookie, Thread *pThread) pSTAThread = pThread; } - ASSERT (GetThread ()); + ASSERT (GetThreadNULLOk ()); BOOL bFound = FALSE; ACQUIRE_SPINLOCK_NO_HOLDER(&m_Lock); @@ -542,7 +541,7 @@ VOID IUnkEntry::Free() CONTRACTL_END; // Log the de-allocation of the IUnknown entry. - LOG((LF_INTEROP, LL_INFO10000, "IUnkEntry::Free called for context 0x%08X, to release entry with m_pUnknown %p, on thread %p\n", m_pCtxCookie, m_pUnknown, GetThread())); + LOG((LF_INTEROP, LL_INFO10000, "IUnkEntry::Free called for context 0x%08X, to release entry with m_pUnknown %p, on thread %p\n", m_pCtxCookie, m_pUnknown, GetThreadNULLOk())); if (g_fProcessDetach) { @@ -1386,7 +1385,7 @@ HRESULT __stdcall CtxEntry::EnterContextCallback(ComCallData* pComCallData) CtxEntryEnterContextCallbackData *pData = (CtxEntryEnterContextCallbackData*)pComCallData->pUserDefined; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // Make sure the thread has been set before we call the user callback function. if (!pThread) diff --git a/src/coreclr/vm/comcallablewrapper.cpp b/src/coreclr/vm/comcallablewrapper.cpp index 06357458e7698..018cb91076e74 100644 --- a/src/coreclr/vm/comcallablewrapper.cpp +++ b/src/coreclr/vm/comcallablewrapper.cpp @@ -4192,11 +4192,6 @@ BOOL ComCallWrapperTemplate::IsSafeTypeForMarshalling() return TRUE; } - if ((CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_AllowDComReflection) != 0)) - { - return TRUE; - } - BOOL isSafe = TRUE; PTR_MethodTable pMt = this->GetClassType().GetMethodTable(); EX_TRY diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 17235f9a164c8..a500c76603f18 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1022,9 +1022,8 @@ FCIMPL5(FC_BOOL_RET, COMDelegate::BindToMethodInfo, Object* refThisUNSAFE, Objec FCIMPLEND // This method is called (in the late bound case only) once a target method has been decided on. All the consistency checks -// (signature matching etc.) have been done at this point and the only major reason we could fail now is on security grounds -// (someone trying to create a delegate over a method that's not visible to them for instance). This method will initialize the -// delegate (wrapping it in a wrapper delegate if necessary). Upon return the delegate should be ready for invocation. +// (signature matching etc.) have been done at this point, this method will simply initialize the delegate, with any required +// wrapping. The delegate returned will be ready for invocation immediately. void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, OBJECTREF *pRefFirstArg, MethodDesc *pTargetMethod, @@ -1043,19 +1042,16 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, } CONTRACTL_END; - // We might have to wrap the delegate in a wrapper delegate depending on the the target method. The following local - // keeps track of the real (i.e. non-wrapper) delegate whether or not this is required. + // The delegate may be put into a wrapper delegate if our target method requires it. This local + // will always hold the real (un-wrapped) delegate. DELEGATEREF refRealDelegate = NULL; GCPROTECT_BEGIN(refRealDelegate); - // If we didn't wrap the real delegate in a wrapper delegate then the real delegate is the one passed in. - if (refRealDelegate == NULL) - { - if (NeedsWrapperDelegate(pTargetMethod)) - refRealDelegate = CreateWrapperDelegate(*pRefThis, pTargetMethod); - else - refRealDelegate = *pRefThis; - } + // If needed, convert the delegate into a wrapper and get the real delegate within that. + if (NeedsWrapperDelegate(pTargetMethod)) + refRealDelegate = CreateWrapperDelegate(*pRefThis, pTargetMethod); + else + refRealDelegate = *pRefThis; pTargetMethod->EnsureActive(); @@ -2604,11 +2600,6 @@ bool COMDelegate::IsMethodDescCompatible(TypeHandle thFirstArg, if (flags & DBF_InstanceMethodOnly && pTargetMethod->IsStatic()) return false; - // we don't allow you to bind to methods on Nullable because the unboxing stubs don't know how to - // handle this case. - if (!pTargetMethod->IsStatic() && Nullable::IsNullableType(pTargetMethod->GetMethodTable())) - return false; - // Get signatures for the delegate invoke and target methods. MetaSig sigInvoke(pInvokeMethod, thDelegate); MetaSig sigTarget(pTargetMethod, thExactMethodType); diff --git a/src/coreclr/vm/comdynamic.cpp b/src/coreclr/vm/comdynamic.cpp index 892e7498624cc..69aae15b15f2a 100644 --- a/src/coreclr/vm/comdynamic.cpp +++ b/src/coreclr/vm/comdynamic.cpp @@ -365,7 +365,7 @@ void QCALLTYPE COMDynamicWrite::SetMethodIL(QCall::ModuleHandle pModule, if (totalSizeSafe.IsOverflow()) COMPlusThrowOM(); UINT32 totalSize = totalSizeSafe.Value(); - ICeeGen* pGen = pRCW->GetCeeGen(); + ICeeGenInternal* pGen = pRCW->GetCeeGen(); BYTE* buf = NULL; ULONG methodRVA; pGen->AllocateMethodBuffer(totalSize, &buf, &methodRVA); diff --git a/src/coreclr/vm/cominterfacemarshaler.cpp b/src/coreclr/vm/cominterfacemarshaler.cpp index 962c041f129f8..a04ddc991ed9c 100644 --- a/src/coreclr/vm/cominterfacemarshaler.cpp +++ b/src/coreclr/vm/cominterfacemarshaler.cpp @@ -103,7 +103,7 @@ void COMInterfaceMarshaler::CreateObjectRef(BOOL fDuplicate, OBJECTREF *pComObj, PRECONDITION(IsProtectedByGCFrame(pComObj)); PRECONDITION(!m_typeHandle.IsNull()); PRECONDITION(m_typeHandle.IsComObjectType()); - PRECONDITION(m_pThread == GetThread()); + PRECONDITION(m_pThread == GetThreadNULLOk()); PRECONDITION(pIncomingItfMT == NULL || pIncomingItfMT->IsInterface()); } CONTRACTL_END; @@ -306,7 +306,7 @@ OBJECTREF COMInterfaceMarshaler::FindOrCreateObjectRefInternal(IUnknown **ppInco THROWS; GC_TRIGGERS; MODE_COOPERATIVE; - PRECONDITION(m_pThread == GetThread()); + PRECONDITION(m_pThread == GetThreadNULLOk()); PRECONDITION(pIncomingItfMT == NULL || pIncomingItfMT->IsInterface()); } CONTRACTL_END; diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp index e789270777fb8..2ac2b03392e9a 100644 --- a/src/coreclr/vm/commodule.cpp +++ b/src/coreclr/vm/commodule.cpp @@ -44,7 +44,7 @@ static ISymUnmanagedWriter **CreateISymWriterForDynamicModule(ReflectionModule * static ConfigDWORD dbgForcePDBSymbols; - if(dbgForcePDBSymbols.val_DontUse_(W("DbgForcePDBSymbols"), 0) == 1) + if(dbgForcePDBSymbols.val(CLRConfig::INTERNAL_DbgForcePDBSymbols) == 1) { symFormatToUse = eSymbolFormatPDB; } @@ -622,7 +622,7 @@ void QCALLTYPE COMModule::SetFieldRVAContent(QCall::ModuleHandle pModule, INT32 RefClassWriter * pRCW = pModule->GetReflectionModule()->GetClassWriter(); _ASSERTE(pRCW); - ICeeGen * pGen = pRCW->GetCeeGen(); + ICeeGenInternal * pGen = pRCW->GetCeeGen(); ReflectionModule * pReflectionModule = pModule->GetReflectionModule(); diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index 6694e74d8b485..ce97e5a67208a 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -181,7 +181,11 @@ typedef PTR_Object OBJECTREF; typedef DPTR(OBJECTREF) PTR_OBJECTREF; typedef DPTR(PTR_OBJECTREF) PTR_PTR_OBJECTREF; -EXTERN_C Thread* STDCALL GetThread(); +Thread* GetThread(); +Thread* GetThreadNULLOk(); + +EXTERN_C Thread* STDCALL GetThreadHelper(); + void SetThread(Thread*); // This is a mechanism by which macros can make the Thread pointer available to inner scopes diff --git a/src/coreclr/vm/compatibilityswitch.cpp b/src/coreclr/vm/compatibilityswitch.cpp index 49ce4a400c6e8..aedddbc7cbbf4 100644 --- a/src/coreclr/vm/compatibilityswitch.cpp +++ b/src/coreclr/vm/compatibilityswitch.cpp @@ -23,7 +23,7 @@ FCIMPL1(StringObject*, CompatibilitySwitch::GetValue, StringObject* switchNameUN HELPER_METHOD_FRAME_BEGIN_RET_1(name); CLRConfig::ConfigStringInfo info; info.name = name->GetBuffer(); - info.options = CLRConfig::EEConfig_default; + info.options = CLRConfig::LookupOptions::Default; LPWSTR strVal = CLRConfig::GetConfigValue(info); refName = StringObject::NewString(strVal); HELPER_METHOD_FRAME_END(); diff --git a/src/coreclr/vm/compile.cpp b/src/coreclr/vm/compile.cpp index 8c5bef669e623..942c6be50fad6 100644 --- a/src/coreclr/vm/compile.cpp +++ b/src/coreclr/vm/compile.cpp @@ -107,7 +107,7 @@ HRESULT CEECompileInfo::Startup( BOOL fForceDebug, // if (SUCCEEDED(hr)) { #ifdef _DEBUG - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); _ASSERTE(pThread); #endif diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index e2bd606886b0b..bc79ffcd8fa9f 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -466,10 +466,6 @@ FCIMPL1(void, ThreadNative::Sleep, INT32 iTime) HELPER_METHOD_FRAME_BEGIN_0(); - // validate the sleep time - if ((iTime < 0) && (iTime != INFINITE_TIMEOUT)) - COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegOrNegative1")); - GetThread()->UserSleep(iTime); HELPER_METHOD_FRAME_END(); diff --git a/src/coreclr/vm/comthreadpool.cpp b/src/coreclr/vm/comthreadpool.cpp index b596aa34a2e51..578f30e4f3d14 100644 --- a/src/coreclr/vm/comthreadpool.cpp +++ b/src/coreclr/vm/comthreadpool.cpp @@ -150,7 +150,7 @@ FCIMPL4(INT32, ThreadPoolNative::GetNextConfigUInt32Value, [=](const CLRConfig::ConfigDWORDInfo &configInfo, bool isBoolean, const WCHAR *appContextConfigName) -> bool { bool wasNotConfigured = true; - *configValueRef = CLRConfig::GetConfigValue(configInfo, true /* acceptExplicitDefaultFromRegutil */, &wasNotConfigured); + *configValueRef = CLRConfig::GetConfigValue(configInfo, &wasNotConfigured); if (wasNotConfigured) { return false; @@ -366,7 +366,6 @@ FCIMPL0(FC_BOOL_RET, ThreadPoolNative::NotifyRequestComplete) // we need a thread adjustment, before setting up the frame. // Thread *pThread = GetThread(); - _ASSERTE (pThread); INT32 priority = pThread->ResetManagedThreadObjectInCoopMode(ThreadNative::PRIORITY_NORMAL); @@ -469,7 +468,7 @@ RegisterWaitForSingleObjectCallback_Worker(LPVOID ptr) VOID NTAPI RegisterWaitForSingleObjectCallback(PVOID delegateInfo, BOOLEAN TimerOrWaitFired) { - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread == NULL) { ClrFlsSetThreadType(ThreadType_Threadpool_Worker); @@ -529,7 +528,6 @@ FCIMPL5(LPVOID, ThreadPoolNative::CorRegisterWaitForSingleObject, _ASSERTE(hWaitHandle); Thread* pCurThread = GetThread(); - _ASSERTE( pCurThread); DelegateInfoHolder delegateInfo = DelegateInfo::MakeDelegateInfo( &gc.state, @@ -781,7 +779,7 @@ void __stdcall BindIoCompletionCallbackStubEx(DWORD ErrorCode, LPOVERLAPPED lpOverlapped, BOOL setStack) { - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread == NULL) { // TODO: how do we notify user of OOM here? @@ -913,7 +911,7 @@ void AppDomainTimerCallback_Worker(LPVOID ptr) VOID WINAPI AppDomainTimerCallback(PVOID callbackState, BOOLEAN timerOrWaitFired) { - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread == NULL) { // TODO: how do we notify user of OOM here? diff --git a/src/coreclr/vm/comtoclrcall.cpp b/src/coreclr/vm/comtoclrcall.cpp index 0a5be3cb5f8a2..4aa45afe33905 100644 --- a/src/coreclr/vm/comtoclrcall.cpp +++ b/src/coreclr/vm/comtoclrcall.cpp @@ -505,7 +505,7 @@ extern "C" UINT64 __stdcall COMToCLRWorker(Thread *pThread, ComMethodFrame* pFra // HRESULT hr = S_OK; - pThread = GetThread(); + pThread = GetThreadNULLOk(); if (pThread == NULL) { pThread = SetupThreadNoThrow(); diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index d8004a37194be..fec65f96e5928 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -522,8 +522,6 @@ FCIMPL0(EXCEPTION_POINTERS*, ExceptionNative::GetExceptionPointers) EXCEPTION_POINTERS* retVal = NULL; Thread *pThread = GetThread(); - _ASSERTE(pThread); - if (pThread->IsExceptionInProgress()) { retVal = pThread->GetExceptionState()->GetExceptionPointers(); @@ -540,8 +538,6 @@ FCIMPL0(INT32, ExceptionNative::GetExceptionCode) INT32 retVal = 0; Thread *pThread = GetThread(); - _ASSERTE(pThread); - if (pThread->IsExceptionInProgress()) { retVal = pThread->GetExceptionState()->GetExceptionCode(); diff --git a/src/coreclr/vm/contractimpl.cpp b/src/coreclr/vm/contractimpl.cpp index 6d0f9f727c68e..c59d8ad2d4f4a 100644 --- a/src/coreclr/vm/contractimpl.cpp +++ b/src/coreclr/vm/contractimpl.cpp @@ -63,7 +63,6 @@ UINT32 TypeIDMap::LookupTypeID(PTR_MethodTable pMT) { CONTRACTL { NOTHROW; - PRECONDITION(CheckPointer(GetThread())); if (GetThread()->PreemptiveGCDisabled()) { GC_NOTRIGGER; } else { GC_TRIGGERS; } } CONTRACTL_END; @@ -79,7 +78,6 @@ PTR_MethodTable TypeIDMap::LookupType(UINT32 id) { CONTRACTL { NOTHROW; - PRECONDITION(CheckPointer(GetThread())); if (GetThread()->PreemptiveGCDisabled()) { GC_NOTRIGGER; } else { GC_TRIGGERS; } PRECONDITION(id <= TypeIDProvider::MAX_TYPE_ID); } CONTRACTL_END; diff --git a/src/coreclr/vm/coreassemblyspec.cpp b/src/coreclr/vm/coreassemblyspec.cpp index e9857a9b388c2..b8312f5a365c5 100644 --- a/src/coreclr/vm/coreassemblyspec.cpp +++ b/src/coreclr/vm/coreassemblyspec.cpp @@ -17,7 +17,6 @@ #include "peimagelayout.inl" #include "domainfile.h" #include "holder.h" -#include "../binder/inc/assemblybinder.hpp" #include "bundle.h" #include "strongnameinternal.h" #include "strongnameholders.h" @@ -26,12 +25,10 @@ #include "compile.h" #endif - #include "../binder/inc/textualidentityparser.hpp" #include "../binder/inc/assemblyidentity.hpp" #include "../binder/inc/assembly.hpp" #include "../binder/inc/assemblyname.hpp" -#include "../binder/inc/fusionassemblyname.hpp" #include "../binder/inc/coreclrbindercommon.h" #include "../binder/inc/applicationcontext.hpp" @@ -98,15 +95,8 @@ VOID AssemblySpec::Bind(AppDomain *pAppDomain, ReleaseHolder result; HRESULT hr=S_OK; - SString assemblyDisplayName; - pResult->Reset(); - if (m_wszCodeBase == NULL) - { - GetDisplayName(0, assemblyDisplayName); - } - // Have a default binding context setup ICLRPrivBinder *pBinder = GetBindingContextFromParentAssembly(pAppDomain); @@ -139,17 +129,13 @@ VOID AssemblySpec::Bind(AppDomain *pAppDomain, { // For name based binding these arguments shouldn't have been changed from default _ASSERTE(!fNgenExplicitBind && !fExplicitBindToNativeImage); - SafeComHolder pName; - hr = CreateAssemblyNameObject(&pName, assemblyDisplayName); - if (SUCCEEDED(hr)) - { - hr = pBinder->BindAssemblyByName(pName, &pPrivAsm); - } + AssemblyNameData assemblyNameData = { 0 }; + PopulateAssemblyNameData(assemblyNameData); + hr = pBinder->BindAssemblyByName(&assemblyNameData, &pPrivAsm); } else { - hr = pTPABinder->Bind(assemblyDisplayName, - m_wszCodeBase, + hr = pTPABinder->Bind(m_wszCodeBase, GetParentAssembly() ? GetParentAssembly()->GetFile() : NULL, fNgenExplicitBind, fExplicitBindToNativeImage, @@ -187,7 +173,6 @@ STDAPI BinderAcquirePEImage(LPCWSTR wszAssemblyPath, { PEImageHolder pImage = NULL; PEImageHolder pNativeImage = NULL; - AppDomain* pDomain = ::GetAppDomain(); // DEAD ? ? #ifdef FEATURE_PREJIT // fExplicitBindToNativeImage is set on Phone when we bind to a list of native images and have no IL on device for an assembly @@ -342,97 +327,116 @@ HRESULT BaseAssemblySpec::ParseName() IfFailThrow(hr); } + // Name - does not copy the data SetName(pAssemblyIdentity->GetSimpleNameUTF8()); - if (pAssemblyIdentity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION)) - { - m_context.usMajorVersion = (USHORT)pAssemblyIdentity->m_version.GetMajor(); - m_context.usMinorVersion = (USHORT)pAssemblyIdentity->m_version.GetMinor(); - m_context.usBuildNumber = (USHORT)pAssemblyIdentity->m_version.GetBuild(); - m_context.usRevisionNumber = (USHORT)pAssemblyIdentity->m_version.GetRevision(); - } - + // Culture - does not copy the data if (pAssemblyIdentity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE)) { if (!pAssemblyIdentity->m_cultureOrLanguage.IsEmpty()) + { SetCulture(pAssemblyIdentity->GetCultureOrLanguageUTF8()); + } else + { SetCulture(""); + } } - if (pAssemblyIdentity-> - Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN) || - pAssemblyIdentity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) - { - m_pbPublicKeyOrToken = const_cast(pAssemblyIdentity->GetPublicKeyOrTokenArray()); - m_cbPublicKeyOrToken = pAssemblyIdentity->m_publicKeyOrTokenBLOB.GetSize(); + InitializeWithAssemblyIdentity(pAssemblyIdentity); - if (pAssemblyIdentity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) - { - m_dwFlags |= afPublicKey; - } - } - else if (pAssemblyIdentity-> - Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL)) - { - m_pbPublicKeyOrToken = const_cast(pAssemblyIdentity->GetPublicKeyOrTokenArray()); - m_cbPublicKeyOrToken = 0; - } - else - { - m_pbPublicKeyOrToken = NULL; - m_cbPublicKeyOrToken = 0; - } + // Copy and own any fields we do not already own + CloneFields(); + } + EX_CATCH_HRESULT(hr); - if (pAssemblyIdentity-> - Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) - { - switch (pAssemblyIdentity->m_kProcessorArchitecture) - { - case peI386: - m_dwFlags |= afPA_x86; - break; - case peIA64: - m_dwFlags |= afPA_IA64; - break; - case peAMD64: - m_dwFlags |= afPA_AMD64; - break; - case peARM: - m_dwFlags |= afPA_ARM; - break; - case peMSIL: - m_dwFlags |= afPA_MSIL; - break; - default: - IfFailThrow(FUSION_E_INVALID_NAME); - } - } + return hr; +} + +void BaseAssemblySpec::InitializeWithAssemblyIdentity(BINDER_SPACE::AssemblyIdentity *identity) +{ + CONTRACTL + { + INSTANCE_CHECK; + GC_NOTRIGGER; + NOTHROW; + } + CONTRACTL_END; + + // Version + if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION)) + { + m_context.usMajorVersion = (USHORT)identity->m_version.GetMajor(); + m_context.usMinorVersion = (USHORT)identity->m_version.GetMinor(); + m_context.usBuildNumber = (USHORT)identity->m_version.GetBuild(); + m_context.usRevisionNumber = (USHORT)identity->m_version.GetRevision(); + } + // Public key or token - does not copy the data + if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN) + || identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) + { + m_pbPublicKeyOrToken = const_cast(static_cast(identity->m_publicKeyOrTokenBLOB)); + m_cbPublicKeyOrToken = identity->m_publicKeyOrTokenBLOB.GetSize(); - if (pAssemblyIdentity-> - Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) + if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY)) { - m_dwFlags |= afRetargetable; + m_dwFlags |= afPublicKey; } + } + else if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL)) + { + m_pbPublicKeyOrToken = const_cast(static_cast(identity->m_publicKeyOrTokenBLOB)); + m_cbPublicKeyOrToken = 0; + } + else + { + m_pbPublicKeyOrToken = NULL; + m_cbPublicKeyOrToken = 0; + } - if (pAssemblyIdentity-> - Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) + // Architecture + if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) + { + switch (identity->m_kProcessorArchitecture) { - DWORD dwContentType = pAssemblyIdentity->m_kContentType; - - _ASSERTE((dwContentType == AssemblyContentType_Default) || (dwContentType == AssemblyContentType_WindowsRuntime)); - if (dwContentType == AssemblyContentType_WindowsRuntime) - { - m_dwFlags |= afContentType_WindowsRuntime; - } + case peI386: + m_dwFlags |= afPA_x86; + break; + case peIA64: + m_dwFlags |= afPA_IA64; + break; + case peAMD64: + m_dwFlags |= afPA_AMD64; + break; + case peARM: + m_dwFlags |= afPA_ARM; + break; + case peMSIL: + m_dwFlags |= afPA_MSIL; + break; + default: + IfFailThrow(FUSION_E_INVALID_NAME); } + } - CloneFields(); + // Retargetable + if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) + { + m_dwFlags |= afRetargetable; } - EX_CATCH_HRESULT(hr); - return hr; + // Content type + if (identity->Have(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) + { + DWORD dwContentType = identity->m_kContentType; + + _ASSERTE((dwContentType == AssemblyContentType_Default) || (dwContentType == AssemblyContentType_WindowsRuntime)); + if (dwContentType == AssemblyContentType_WindowsRuntime) + { + m_dwFlags |= afContentType_WindowsRuntime; + } + } } #endif // DACCESS_COMPILE @@ -473,6 +477,29 @@ VOID BaseAssemblySpec::GetDisplayName(DWORD flags, SString &result) const GetDisplayNameInternal(flags, result); } +namespace +{ + PEKIND GetProcessorArchitectureFromAssemblyFlags(DWORD flags) + { + if (flags & afPA_MSIL) + return peMSIL; + + if (flags & afPA_x86) + return peI386; + + if (flags & afPA_IA64) + return peIA64; + + if (flags & afPA_AMD64) + return peAMD64; + + if (flags & afPA_ARM64) + return peARM64; + + return peNone; + } +} + VOID BaseAssemblySpec::GetDisplayNameInternal(DWORD flags, SString &result) const { if (flags==0) @@ -550,16 +577,7 @@ VOID BaseAssemblySpec::GetDisplayNameInternal(DWORD flags, SString &result) cons assemblyIdentity. SetHave(BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE); - if (m_dwFlags & afPA_MSIL) - assemblyIdentity.m_kProcessorArchitecture = peMSIL; - else if (m_dwFlags & afPA_x86) - assemblyIdentity.m_kProcessorArchitecture = peI386; - else if (m_dwFlags & afPA_IA64) - assemblyIdentity.m_kProcessorArchitecture = peIA64; - else if (m_dwFlags & afPA_AMD64) - assemblyIdentity.m_kProcessorArchitecture = peAMD64; - else if (m_dwFlags & afPA_ARM) - assemblyIdentity.m_kProcessorArchitecture = peARM; + assemblyIdentity.m_kProcessorArchitecture = GetProcessorArchitectureFromAssemblyFlags(m_dwFlags); } if ((flags & ASM_DISPLAYF_RETARGET) && (m_dwFlags & afRetargetable)) @@ -578,4 +596,53 @@ VOID BaseAssemblySpec::GetDisplayNameInternal(DWORD flags, SString &result) cons result)); } +void BaseAssemblySpec::PopulateAssemblyNameData(AssemblyNameData &data) const +{ + data.Name = m_pAssemblyName; + data.IdentityFlags = BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME; + + if (m_context.usMajorVersion != 0xFFFF) + { + data.MajorVersion = m_context.usMajorVersion; + data.MinorVersion = m_context.usMinorVersion; + data.BuildNumber = m_context.usBuildNumber; + data.RevisionNumber = m_context.usRevisionNumber; + data.IdentityFlags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_VERSION; + } + + if (m_context.szLocale != NULL && m_context.szLocale[0] != 0) + { + data.Culture = m_context.szLocale; + data.IdentityFlags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CULTURE; + } + + data.PublicKeyOrTokenLength = m_cbPublicKeyOrToken; + if (m_cbPublicKeyOrToken > 0) + { + data.PublicKeyOrToken = m_pbPublicKeyOrToken; + data.IdentityFlags |= IsAfPublicKeyToken(m_dwFlags) + ? BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN + : BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY; + } + else + { + data.IdentityFlags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL; + } + + if ((m_dwFlags & afPA_Mask) != 0) + { + data.ProcessorArchitecture = GetProcessorArchitectureFromAssemblyFlags(m_dwFlags); + data.IdentityFlags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; + } + + if ((m_dwFlags & afRetargetable) != 0) + { + data.IdentityFlags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE; + } + if ((m_dwFlags & afContentType_Mask) == afContentType_WindowsRuntime) + { + data.ContentType = AssemblyContentType_WindowsRuntime; + data.IdentityFlags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE; + } +} diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 3b870b718d135..71ecf177e5acc 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -477,6 +477,11 @@ DEFINE_METHOD(MARSHAL, ALLOC_CO_TASK_MEM, AllocCoTa DEFINE_METHOD(MARSHAL, FREE_CO_TASK_MEM, FreeCoTaskMem, SM_IntPtr_RetVoid) DEFINE_FIELD(MARSHAL, SYSTEM_MAX_DBCS_CHAR_SIZE, SystemMaxDBCSCharSize) +DEFINE_METHOD(MARSHAL, STRUCTURE_TO_PTR, StructureToPtr, SM_Obj_IntPtr_Bool_RetVoid) +DEFINE_METHOD(MARSHAL, PTR_TO_STRUCTURE, PtrToStructure, SM_IntPtr_Obj_RetVoid) +DEFINE_METHOD(MARSHAL, DESTROY_STRUCTURE, DestroyStructure, SM_IntPtr_Type_RetVoid) +DEFINE_METHOD(MARSHAL, SIZEOF_TYPE, SizeOf, SM_Type_RetInt) + DEFINE_CLASS(NATIVELIBRARY, Interop, NativeLibrary) DEFINE_METHOD(NATIVELIBRARY, LOADLIBRARYCALLBACKSTUB, LoadLibraryCallbackStub, SM_Str_AssemblyBase_Bool_UInt_RetIntPtr) @@ -772,6 +777,7 @@ DEFINE_CLASS(CALLCONV_STDCALL, CompilerServices, CallConvStd DEFINE_CLASS(CALLCONV_THISCALL, CompilerServices, CallConvThiscall) DEFINE_CLASS(CALLCONV_FASTCALL, CompilerServices, CallConvFastcall) DEFINE_CLASS(CALLCONV_SUPPRESSGCTRANSITION, CompilerServices, CallConvSuppressGCTransition) +DEFINE_CLASS(CALLCONV_MEMBERFUNCTION, CompilerServices, CallConvMemberFunction) DEFINE_CLASS_U(Interop, SafeHandle, SafeHandle) DEFINE_FIELD_U(handle, SafeHandle, m_handle) diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp index 920502d95aad6..689cf04c89a5a 100644 --- a/src/coreclr/vm/corhost.cpp +++ b/src/coreclr/vm/corhost.cpp @@ -129,7 +129,7 @@ HRESULT CorHost2::Stop() { NOTHROW; ENTRY_POINT; // We're bringing the EE down, so no point in probing - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; if (!g_fEEStarted) @@ -210,7 +210,7 @@ HRESULT CorHost2::GetCurrentAppDomainId(DWORD *pdwAppDomainId) } else { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (!pThread) { hr = E_UNEXPECTED; @@ -332,7 +332,7 @@ HRESULT CorHost2::ExecuteAssembly(DWORD dwAppDomainId, AppDomain *pCurDomain = SystemDomain::GetCurrentDomain(); - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL) { pThread = SetupThreadNoThrow(&hr); @@ -425,7 +425,7 @@ HRESULT CorHost2::ExecuteInDefaultAppDomain(LPCWSTR pwzAssemblyPath, BEGIN_ENTRYPOINT_NOTHROW; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL) { pThread = SetupThreadNoThrow(&hr); @@ -528,7 +528,7 @@ HRESULT CorHost2::ExecuteInAppDomain(DWORD dwAppDomainId, CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; @@ -566,7 +566,7 @@ HRESULT CorHost2::CreateAppDomainWithManager( CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; @@ -712,7 +712,7 @@ HRESULT CorHost2::CreateDelegate( CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; @@ -800,7 +800,7 @@ HRESULT CorHost2::Authenticate(ULONGLONG authKey) CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; @@ -814,7 +814,7 @@ HRESULT CorHost2::RegisterMacEHPort() CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; @@ -827,7 +827,7 @@ HRESULT CorHost2::SetStartupFlags(STARTUP_FLAGS flag) CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; @@ -1071,7 +1071,7 @@ HRESULT CorHost2::GetCLRControl(ICLRControl** pCLRControl) // Note: Sampling profilers also use this function to initialize TLS for a unmanaged // sampling thread so that initialization can be done in advance to avoid deadlocks. // See ProfToEEInterfaceImpl::InitializeCurrentThread for more details. -void SetupTLSForThread(Thread* pThread) +void SetupTLSForThread() { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_NOTRIGGER; diff --git a/src/coreclr/vm/crst.cpp b/src/coreclr/vm/crst.cpp index cbfb665285b44..63472e454e210 100644 --- a/src/coreclr/vm/crst.cpp +++ b/src/coreclr/vm/crst.cpp @@ -288,15 +288,13 @@ void CrstBase::Enter(INDEBUG(NoLevelCheckFlag noLevelCheckFlag/* = CRST_LEVEL_CH Thread * pThread; BOOL fToggle; - BEGIN_GETTHREAD_ALLOWED; - pThread = GetThread(); + pThread = GetThreadNULLOk(); fToggle = ((m_dwFlags & (CRST_UNSAFE_ANYMODE | CRST_UNSAFE_COOPGC | CRST_GC_NOTRIGGER_WHEN_TAKEN)) == 0) // condition normally false && pThread && pThread->PreemptiveGCDisabled(); if (fToggle) { pThread->EnablePreemptiveGC(); } - END_GETTHREAD_ALLOWED; #ifdef _DEBUG PreEnter (); @@ -329,9 +327,9 @@ void CrstBase::Enter(INDEBUG(NoLevelCheckFlag noLevelCheckFlag/* = CRST_LEVEL_CH if (fToggle) { - BEGIN_GETTHREAD_ALLOWED; + pThread->DisablePreemptiveGC(); - END_GETTHREAD_ALLOWED; + } } @@ -351,7 +349,7 @@ void CrstBase::Leave() #endif //_DEBUG #if defined(_DEBUG) - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); #endif LeaveCriticalSection(&m_criticalsection); @@ -703,14 +701,14 @@ BOOL CrstBase::IsSafeToTake() // If there is no thread object, we ignore the check since this thread isn't // coordinated with the GC. Thread * pThread; - BEGIN_GETTHREAD_ALLOWED; - pThread = GetThread(); + + pThread = GetThreadNULLOk(); _ASSERTE(pThread == NULL || (pThread->PreemptiveGCDisabled() == ((m_dwFlags & CRST_UNSAFE_COOPGC) != 0)) || ((m_dwFlags & (CRST_UNSAFE_ANYMODE | CRST_GC_NOTRIGGER_WHEN_TAKEN)) != 0) || (GCHeapUtilities::IsGCInProgress() && pThread == ThreadSuspend::GetSuspensionThread())); - END_GETTHREAD_ALLOWED; + if (m_holderthreadid.IsCurrentThread()) { @@ -792,7 +790,6 @@ CrstBase::CrstAndForbidSuspendForDebuggerHolder::CrstAndForbidSuspendForDebugger _ASSERTE((pCrst->m_dwFlags & CRST_REENTRANCY) == 0); Thread *pThread = GetThread(); - _ASSERTE(pThread != nullptr); if (pThread->IsInForbidSuspendForDebuggerRegion()) { AcquireLock(pCrst); diff --git a/src/coreclr/vm/dbginterface.h b/src/coreclr/vm/dbginterface.h index 708b4ff2ac6bb..939fe948a5adc 100644 --- a/src/coreclr/vm/dbginterface.h +++ b/src/coreclr/vm/dbginterface.h @@ -392,7 +392,6 @@ class DebugInterface virtual BOOL ShouldAutoAttach() = 0; virtual BOOL FallbackJITAttachPrompt() = 0; - virtual HRESULT SetFiberMode(bool isFiberMode) = 0; #ifdef FEATURE_INTEROP_DEBUGGING virtual LONG FirstChanceSuspendHijackWorker(PCONTEXT pContext, PEXCEPTION_RECORD pExceptionRecord) = 0; diff --git a/src/coreclr/vm/debugdebugger.cpp b/src/coreclr/vm/debugdebugger.cpp index 5027ebf158a29..a7c3c8e6f51da 100644 --- a/src/coreclr/vm/debugdebugger.cpp +++ b/src/coreclr/vm/debugdebugger.cpp @@ -66,8 +66,6 @@ UINT_PTR FindMostRecentUserCodeOnStack(void) CONTRACTL_END; Thread * pThread = GetThread(); - _ASSERTE(pThread != NULL); - UINT_PTR address = NULL; CONTEXT ctx; diff --git a/src/coreclr/vm/debughelp.cpp b/src/coreclr/vm/debughelp.cpp index 87c30a7b19ca1..67c58eb62b52c 100644 --- a/src/coreclr/vm/debughelp.cpp +++ b/src/coreclr/vm/debughelp.cpp @@ -194,7 +194,7 @@ void *DumpEnvironmentBlock(void) CONTRACTL_END; LPTSTR lpszVariable; - lpszVariable = (LPTSTR)WszGetEnvironmentStrings(); + lpszVariable = (LPTSTR)GetEnvironmentStringsW(); while (*lpszVariable) { @@ -203,7 +203,7 @@ void *DumpEnvironmentBlock(void) fprintf(stderr, "\n"); - return WszGetEnvironmentStrings(); + return GetEnvironmentStringsW(); } #if defined(TARGET_X86) && !defined(TARGET_UNIX) @@ -700,18 +700,6 @@ PCCOR_SIGNATURE RawSigForMethodDesc(MethodDesc* pMD) return(pMD->GetSig()); } -Thread * CurrentThreadInfo () -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - } - CONTRACTL_END; - - return GetThread (); -} - SyncBlock *GetSyncBlockForObject(UINT_PTR obj) { CONTRACTL diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 04b87ac17b6bf..76bccc36ed555 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -4037,9 +4037,9 @@ static void CreateNDirectStubAccessMetadata( } else { - if (unmgdCallConv != CorInfoCallConvExtension::Stdcall && - unmgdCallConv != CorInfoCallConvExtension::C && - unmgdCallConv != CorInfoCallConvExtension::Thiscall) + if (unmgdCallConv == CorInfoCallConvExtension::Managed || + unmgdCallConv == CorInfoCallConvExtension::Fastcall || + unmgdCallConv == CorInfoCallConvExtension::FastcallMemberFunction) { COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV); } diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 4736bd43b87fc..38314fa234384 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -531,10 +531,10 @@ void STDCALL LogUMTransition(UMEntryThunk* thunk) DEBUG_ONLY; GC_NOTRIGGER; ENTRY_POINT; - if (GetThread()) MODE_PREEMPTIVE; else MODE_ANY; + if (GetThreadNULLOk()) MODE_PREEMPTIVE; else MODE_ANY; DEBUG_ONLY; PRECONDITION(CheckPointer(thunk)); - PRECONDITION((GetThread() != NULL) ? (!GetThread()->PreemptiveGCDisabled()) : TRUE); + PRECONDITION((GetThreadNULLOk() != NULL) ? (!GetThread()->PreemptiveGCDisabled()) : TRUE); } CONTRACTL_END; @@ -556,22 +556,6 @@ void STDCALL LogUMTransition(UMEntryThunk* thunk) } #endif -namespace -{ - // Templated function to compute if a char string begins with a constant string. - template - bool BeginsWith(ULONG s1Len, const char* s1, const char (&s2)[S2LEN]) - { - WRAPPER_NO_CONTRACT; - - ULONG s2Len = (ULONG)S2LEN - 1; // Remove null - if (s1Len < s2Len) - return false; - - return (0 == strncmp(s1, s2, s2Len)); - } -} - bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorInfoCallConvExtension* pCallConv) { STANDARD_VM_CONTRACT; @@ -643,34 +627,39 @@ bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorInfoCal else { // Set WinAPI as the default - callConvLocal = MetaSig::GetDefaultUnmanagedCallingConvention(); + callConvLocal = CorInfoCallConvExtension::Managed; + + MetaSig::CallingConventionModifiers modifiers = MetaSig::CALL_CONV_MOD_NONE; + + bool foundBaseCallConv = false; + bool useMemberFunctionVariant = false; CaValue* arrayOfTypes = &namedArgs[0].val; for (ULONG i = 0; i < arrayOfTypes->arr.length; i++) { CaValue& typeNameValue = arrayOfTypes->arr[i]; - // According to ECMA-335, type name strings are UTF-8. Since we are - // looking for type names that are equivalent in ASCII and UTF-8, - // using a const char constant is acceptable. Type name strings are - // in Fully Qualified form, so we include the ',' delimiter. - if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvCdecl,")) - { - callConvLocal = CorInfoCallConvExtension::C; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvStdcall,")) - { - callConvLocal = CorInfoCallConvExtension::Stdcall; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvFastcall,")) + if (!MetaSig::TryApplyModOptToCallingConvention( + typeNameValue.str.pStr, + typeNameValue.str.cbStr, + MetaSig::CallConvModOptNameType::FullyQualifiedName, + &callConvLocal, + &modifiers)) { - callConvLocal = CorInfoCallConvExtension::Fastcall; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvThiscall,")) - { - callConvLocal = CorInfoCallConvExtension::Thiscall; + // We found a second base calling convention. + return false; } } + + if (callConvLocal == CorInfoCallConvExtension::Managed) + { + callConvLocal = MetaSig::GetDefaultUnmanagedCallingConvention(); + } + + if (modifiers & MetaSig::CALL_CONV_MOD_MEMBERFUNCTION) + { + callConvLocal = MetaSig::GetMemberFunctionUnmanagedCallingConventionVariant(callConvLocal); + } } *pCallConv = callConvLocal; return true; diff --git a/src/coreclr/vm/domainfile.cpp b/src/coreclr/vm/domainfile.cpp index 81767151b627b..7eaa37e1f6d2b 100644 --- a/src/coreclr/vm/domainfile.cpp +++ b/src/coreclr/vm/domainfile.cpp @@ -465,10 +465,7 @@ BOOL DomainFile::DoIncrementalLoad(FileLoadLevel level) if (IsError()) return FALSE; - Thread *pThread; - pThread = GetThread(); - _ASSERTE(pThread); - + Thread *pThread = GetThread(); switch (level) { case FILE_LOAD_BEGIN: diff --git a/src/coreclr/vm/dwreport.cpp b/src/coreclr/vm/dwreport.cpp index 0370edd68f10f..e42fbf752d6cf 100644 --- a/src/coreclr/vm/dwreport.cpp +++ b/src/coreclr/vm/dwreport.cpp @@ -855,7 +855,7 @@ HRESULT GetBucketParametersForCurrentException( GenericModeBlock gmb; // Make sure this is (or at least has been) a managed thread. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL) { // Not the greatest error, but we don't expect to be called on a unmanaged thread. return E_UNEXPECTED; diff --git a/src/coreclr/vm/eeconfig.cpp b/src/coreclr/vm/eeconfig.cpp index 6d5ea5a3d267c..5dd61484cd489 100644 --- a/src/coreclr/vm/eeconfig.cpp +++ b/src/coreclr/vm/eeconfig.cpp @@ -123,6 +123,7 @@ HRESULT EEConfig::Init() fNgenBindOptimizeNonGac = false; fStressLog = false; + fForceEnc = false; fProbeForStackOverflow = true; INDEBUG(fStressLog = true;) @@ -150,8 +151,6 @@ HRESULT EEConfig::Init() pPerfTypesToLog = NULL; iFastGCStress = 0; iInjectFatalError = 0; - fSaveThreadInfo = FALSE; - dwSaveThreadInfoMask = (DWORD)-1; #ifdef TEST_DATA_CONSISTENCY // indicates whether to run the self test to determine that we are detecting when a lock is held by the // LS in DAC builds. Initialized via the environment variable TestDataConsistency @@ -180,6 +179,7 @@ HRESULT EEConfig::Init() fSuppressChecks = false; fConditionalContracts = false; fEnableFullDebug = false; + iExposeExceptionsInCOM = 0; #endif #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT @@ -201,7 +201,6 @@ HRESULT EEConfig::Init() #ifdef _DEBUG // interop logging - m_pTraceIUnknown = NULL; m_TraceWrapper = 0; #endif @@ -343,81 +342,6 @@ HRESULT EEConfig::Cleanup() return S_OK; } - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -HRESULT EEConfig::GetConfigString_DontUse_(__in_z LPCWSTR name, __deref_out_z LPWSTR *outVal, BOOL fPrependCOMPLUS) -{ - CONTRACT(HRESULT) { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY); - PRECONDITION(CheckPointer(name)); - POSTCONDITION(CheckPointer(outVal, NULL_OK)); - } CONTRACT_END; - - *outVal = REGUTIL::GetConfigString_DontUse_(name, fPrependCOMPLUS); - - RETURN S_OK; -} - - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -DWORD EEConfig::GetConfigDWORD_DontUse_(__in_z LPCWSTR name, DWORD defValue, DWORD level, BOOL fPrependCOMPLUS) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(name)); - } CONTRACTL_END; - - // @TODO: After everyone has moved off registry, key remove the following line in golden - return REGUTIL::GetConfigDWORD_DontUse_(name, defValue, (REGUTIL::CORConfigLevel)level, fPrependCOMPLUS); -} - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as -// a 32-bit number. When we have the _wcstoui64 API on MAC we will use that instead of wcstoul. -ULONGLONG EEConfig::GetConfigULONGLONG_DontUse_(__in_z LPCWSTR name, ULONGLONG defValue, DWORD level, BOOL fPrependCOMPLUS) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(name)); - } CONTRACTL_END; - - // @TODO: After everyone has moved off registry, key remove the following line in golden - return REGUTIL::GetConfigULONGLONG_DontUse_(name, defValue, (REGUTIL::CORConfigLevel)level, fPrependCOMPLUS); -} - -// -// NOTE: This function is deprecated; use the CLRConfig class instead. -// To use the CLRConfig class, add an entry in file:../inc/CLRConfigValues.h. -// -DWORD EEConfig::GetConfigDWORDInternal_DontUse_(__in_z LPCWSTR name, DWORD defValue, DWORD level, BOOL fPrependCOMPLUS) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - PRECONDITION(CheckPointer(name)); - } CONTRACTL_END; - - // @TODO: After everyone has moved off registry, key remove the following line in golden - return REGUTIL::GetConfigDWORD_DontUse_(name, defValue, (REGUTIL::CORConfigLevel)level, fPrependCOMPLUS); -} - /**************************************************************/ HRESULT EEConfig::sync() @@ -436,10 +360,10 @@ HRESULT EEConfig::sync() // Note the global variable is not updated directly by the GetRegKey function // so we only update it once (to avoid reentrancy windows) -fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TrackDynamicMethodDebugInfo); + fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_TrackDynamicMethodDebugInfo); #ifdef _DEBUG - iFastGCStress = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_FastGCStress, iFastGCStress); + iFastGCStress = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_FastGCStress); IfFailRet(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GcCoverage, (LPWSTR*)&pszGcCoverageOnMethod)); pszGcCoverageOnMethod = NarrowWideChar((LPWSTR)pszGcCoverageOnMethod); @@ -527,7 +451,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #ifdef VERIFY_HEAP if (bGCStressAndHeapVerifyAllowed) { - iGCHeapVerify = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_HeapVerify, iGCHeapVerify); + iGCHeapVerify = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_HeapVerify); } #endif @@ -552,21 +476,10 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ iGCAllowVeryLargeObjects = (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_gcAllowVeryLargeObjects) != 0); #endif - fGCBreakOnOOM = (GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCBreakOnOOM, fGCBreakOnOOM) != 0); - -#ifdef TRACE_GC - iGCtraceStart = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCtraceStart, iGCtraceStart); - iGCtraceEnd = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCtraceEnd, iGCtraceEnd); - iGCtraceFac = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_GCtraceFacility, iGCtraceFac); - iGCprnLvl = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_GCprnLvl, iGCprnLvl); -#endif + fGCBreakOnOOM = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCBreakOnOOM) != 0); #ifdef _DEBUG - iInjectFatalError = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_InjectFatalError, iInjectFatalError); - - fSaveThreadInfo = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SaveThreadInfo, fSaveThreadInfo) != 0); - - dwSaveThreadInfoMask = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SaveThreadInfoMask, dwSaveThreadInfoMask); + iInjectFatalError = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFatalError); { LPWSTR wszSkipGCCoverageList = NULL; @@ -581,8 +494,8 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ IfFailRet(hr); } #endif - fStressLog = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0; - fForceEnc = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_ForceEnc, fForceEnc) != 0; + fStressLog = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_StressLog, fStressLog) != 0; + fForceEnc = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ForceEnc) != 0; { NewArrayHolder wszModifiableAssemblies; @@ -591,7 +504,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ fDebugAssembliesModifiable = _wcsicmp(wszModifiableAssemblies, W("debug")) == 0; } - iRequireZaps = RequireZapsType(GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_ZapRequire, iRequireZaps)); + iRequireZaps = RequireZapsType(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapRequire, iRequireZaps)); if (IsCompilationProcess() || iRequireZaps >= REQUIRE_ZAPS_COUNT) iRequireZaps = REQUIRE_ZAPS_NONE; @@ -644,7 +557,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif #ifdef FEATURE_DOUBLE_ALIGNMENT_HINT - DoubleArrayToLargeObjectHeapThreshold = GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_DoubleArrayToLargeObjectHeap, DoubleArrayToLargeObjectHeapThreshold); + DoubleArrayToLargeObjectHeapThreshold = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DoubleArrayToLargeObjectHeap, DoubleArrayToLargeObjectHeapThreshold); #endif IfFailRet(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ZapBBInstr, (LPWSTR*)&szZapBBInstr)); @@ -669,7 +582,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ g_IBCLogger.DisableAllInstr(); - dwDisableStackwalkCache = GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_DisableStackwalkCache, dwDisableStackwalkCache); + dwDisableStackwalkCache = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableStackwalkCache, dwDisableStackwalkCache); #ifdef _DEBUG @@ -695,9 +608,9 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ dwJitHostMaxSlabCache = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_JitHostMaxSlabCache); - fJitFramed = (GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_JitFramed, fJitFramed) != 0); - fJitMinOpts = (GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_JITMinOpts, fJitMinOpts) == 1); - iJitOptimizeType = GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_JitOptimizeType, iJitOptimizeType); + fJitFramed = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_JitFramed) != 0); + fJitMinOpts = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_JITMinOpts) == 1); + iJitOptimizeType = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_JitOptimizeType); if (iJitOptimizeType > OPT_RANDOM) iJitOptimizeType = OPT_DEFAULT; #ifdef TARGET_X86 @@ -706,7 +619,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #ifdef _DEBUG - fDebuggable = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_JitDebuggable, fDebuggable) != 0); + fDebuggable = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitDebuggable) != 0); LPWSTR wszPreStubStuff = NULL; @@ -741,25 +654,25 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ IfFailRet(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnStructMarshalSetup, (LPWSTR*)&pszBreakOnStructMarshalSetup)); pszBreakOnStructMarshalSetup = NarrowWideChar((LPWSTR)pszBreakOnStructMarshalSetup); - m_fAssertOnBadImageFormat = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnBadImageFormat, m_fAssertOnBadImageFormat) != 0); - m_fAssertOnFailFast = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AssertOnFailFast, m_fAssertOnFailFast) != 0); + m_fAssertOnBadImageFormat = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnBadImageFormat) != 0); + m_fAssertOnFailFast = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnFailFast) != 0); - fSuppressChecks = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_SuppressChecks, fSuppressChecks) != 0); + fSuppressChecks = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_SuppressChecks) != 0); CHECK::SetAssertEnforcement(!fSuppressChecks); - fConditionalContracts = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ConditionalContracts, fConditionalContracts) != 0); + fConditionalContracts = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ConditionalContracts) != 0); #ifdef ENABLE_CONTRACTS_IMPL Contract::SetUnconditionalContractEnforcement(!fConditionalContracts); #endif - fEnableFullDebug = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_EnableFullDebug, fEnableFullDebug) != 0); + fEnableFullDebug = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EnableFullDebug) != 0); - fVerifierOff = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_VerifierOff, fVerifierOff) != 0); + fVerifierOff = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_VerifierOff) != 0); - fJitVerificationDisable = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_JitVerificationDisable, fJitVerificationDisable) != 0); + fJitVerificationDisable = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitVerificationDisable) != 0); - iExposeExceptionsInCOM = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ExposeExceptionsInCOM, iExposeExceptionsInCOM); + iExposeExceptionsInCOM = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ExposeExceptionsInCOM, iExposeExceptionsInCOM); #endif #ifdef FEATURE_COMINTEROP @@ -772,7 +685,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif // FEATURE_COMINTEROP #ifdef _DEBUG - fExpandAllOnLoad = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_ExpandAllOnLoad, fExpandAllOnLoad) != 0); + fExpandAllOnLoad = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ExpandAllOnLoad) != 0); #endif //_DEBUG #ifdef ENABLE_STARTUP_DELAY @@ -803,19 +716,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ fInited = true; #ifdef _DEBUG - m_pTraceIUnknown = (IUnknown*)(DWORD_PTR)(GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_TraceIUnknown, (DWORD)(DWORD_PTR)(m_pTraceIUnknown))); // WIN64 - conversion from DWORD to IUnknown* of greater size - m_TraceWrapper = GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_TraceWrap, m_TraceWrapper); - - // can't have both - if (m_pTraceIUnknown != 0) - { - m_TraceWrapper = 0; - } - else - if (m_TraceWrapper != 0) - { - m_pTraceIUnknown = (IUnknown*)-1; - } + m_TraceWrapper = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_TraceWrap); #endif #ifdef _DEBUG @@ -849,7 +750,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #endif #if defined(_DEBUG) && defined(STUBLINKER_GENERATES_UNWIND_INFO) - fStubLinkerUnwindInfoVerificationOn = (GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_StubLinkerUnwindInfoVerificationOn, fStubLinkerUnwindInfoVerificationOn) != 0); + fStubLinkerUnwindInfoVerificationOn = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_StubLinkerUnwindInfoVerificationOn) != 0); #endif if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_UseMethodDataCache) != 0) { @@ -862,7 +763,7 @@ fTrackDynamicMethodDebugInfo = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_ #if defined(_DEBUG) && defined(TARGET_AMD64) - m_cGenerateLongJumpDispatchStubRatio = GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_GenerateLongJumpDispatchStubRatio, + m_cGenerateLongJumpDispatchStubRatio = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GenerateLongJumpDispatchStubRatio, static_cast(m_cGenerateLongJumpDispatchStubRatio)); #endif diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 11f1d286d9f92..d995d25007d79 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -307,16 +307,6 @@ class EEConfig return fProbeForStackOverflow; } -#ifdef _DEBUG - inline bool AppDomainLeaks() const - { - // Workaround for CoreCLR bug #12075, until this configuration option is removed - // (CoreCLR Bug #12094) - LIMITED_METHOD_DAC_CONTRACT; - return false; - } -#endif - #ifdef TEST_DATA_CONSISTENCY // get the value of fTestDataConsistency, which controls whether we test that we can correctly detect // held locks in DAC builds. This is determined by an environment variable. @@ -417,22 +407,11 @@ class EEConfig LIMITED_METHOD_CONTRACT; return iInjectFatalError; } - - inline BOOL SaveThreadInfo() const - { - return fSaveThreadInfo; - } - - inline DWORD SaveThreadInfoMask() const - { - return dwSaveThreadInfoMask; - } #endif #ifdef _DEBUG // Interop config - IUnknown* GetTraceIUnknown() const {LIMITED_METHOD_CONTRACT; return m_pTraceIUnknown; } int GetTraceWrapper() const {LIMITED_METHOD_CONTRACT; return m_TraceWrapper; } #endif @@ -469,36 +448,6 @@ class EEConfig HRESULT sync(); // check the registry again and update local state - // Helpers to read configuration - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static HRESULT GetConfigString_DontUse_(__in_z LPCWSTR name, __deref_out_z LPWSTR*out, BOOL fPrependCOMPLUS = TRUE); // Note that you own the returned string! - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigDWORD_DontUse_(__in_z LPCWSTR name, DWORD defValue, - DWORD level=(DWORD) REGUTIL::COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static ULONGLONG GetConfigULONGLONG_DontUse_(__in_z LPCWSTR name, ULONGLONG defValue, - DWORD level=(DWORD) REGUTIL::COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - - // - // NOTE: The following function is deprecated; use the CLRConfig class instead. - // To access a configuration value through CLRConfig, add an entry in file:../inc/CLRConfigValues.h. - // - static DWORD GetConfigFlag_DontUse_(__in_z LPCWSTR name, DWORD bitToSet, bool defValue = FALSE); - #ifdef _DEBUG // GC alloc logging bool ShouldLogAlloc(const char *pClass) const { LIMITED_METHOD_CONTRACT; return pPerfTypesToLog && pPerfTypesToLog->IsInList(pClass);} @@ -660,9 +609,6 @@ class EEConfig DWORD iInjectFatalError; - BOOL fSaveThreadInfo; - DWORD dwSaveThreadInfoMask; - AssemblyNamesList *pSkipGCCoverageList; #endif @@ -703,7 +649,6 @@ class EEConfig #ifdef _DEBUG // interop logging - IUnknown* m_pTraceIUnknown; int m_TraceWrapper; #endif @@ -764,10 +709,6 @@ class EEConfig #endif public: - DWORD GetConfigDWORDInternal_DontUse_ (__in_z LPCWSTR name, DWORD defValue, //for getting data in the constructor of EEConfig - DWORD level=(DWORD) REGUTIL::COR_CONFIG_ALL, - BOOL fPrependCOMPLUS = TRUE); - enum BitForMask { CallSite_1 = 0x0001, CallSite_2 = 0x0002, diff --git a/src/coreclr/vm/eecontract.cpp b/src/coreclr/vm/eecontract.cpp index 50695468b6b99..c0b6830b2ba21 100644 --- a/src/coreclr/vm/eecontract.cpp +++ b/src/coreclr/vm/eecontract.cpp @@ -32,8 +32,7 @@ void EEContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z c // Many of the checks below result in calls to GetThread() // that work just fine if GetThread() returns NULL, so temporarily // allow such calls. - BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; - m_pThread = GetThread(); + m_pThread = GetThreadNULLOk(); m_pClrDebugState = GetClrDebugState(); // Call our base DoChecks. @@ -199,56 +198,5 @@ void EEContract::DoChecks(UINT testmask, __in_z const char *szFunction, __in_z c default: UNREACHABLE(); } - END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; - - // EE Thread-required check - // NOTE: The following must NOT be inside BEGIN/END_GETTHREAD_ALLOWED, - // as the change to m_pClrDebugState->m_allowGetThread below would be - // overwritten by END_GETTHREAD_ALLOWED. - switch (testmask & EE_THREAD_Mask) - { - case EE_THREAD_Required: - if (!((EEThreadViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) - { - if (m_pThread == NULL) - { - CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered with no current EE Thread object in TLS.", - Contract::EE_THREAD_Required, - Contract::EE_THREAD_Mask, - m_contractStackRecord.m_szFunction, - m_contractStackRecord.m_szFile, - m_contractStackRecord.m_lineNum - ); - } - else if (!m_pClrDebugState->IsGetThreadAllowed()) - { - // In general, it's unsafe for an EE_THREAD_NOT_REQUIRED function to - // call an EE_THREAD_REQUIRED function. In cases where it is safe, - // you may wrap the call to the EE_THREAD_REQUIRED function inside a - // BEGIN/END_GETTHREAD_ALLOWED block, but you may only do so if the - // case where GetThread() == NULL is clearly handled in a way that - // prevents entry into the BEGIN/END_GETTHREAD_ALLOWED block. - CONTRACT_ASSERT("EE_THREAD_REQUIRED encountered in an EE_THREAD_NOT_REQUIRED scope, without an intervening BEGIN/END_GETTHREAD_ALLOWED block.", - Contract::EE_THREAD_Required, - Contract::EE_THREAD_Mask, - m_contractStackRecord.m_szFunction, - m_contractStackRecord.m_szFile, - m_contractStackRecord.m_lineNum - ); - } - } - m_pClrDebugState->SetGetThreadAllowed(); - break; - - case EE_THREAD_Not_Required: - m_pClrDebugState->ResetGetThreadAllowed(); - break; - - case EE_THREAD_Disabled: - break; - - default: - UNREACHABLE(); - } } #endif // ENABLE_CONTRACTS diff --git a/src/coreclr/vm/eecontract.h b/src/coreclr/vm/eecontract.h index 1c2776aaadd1c..61adea1dc80ba 100644 --- a/src/coreclr/vm/eecontract.h +++ b/src/coreclr/vm/eecontract.h @@ -52,16 +52,6 @@ class EEContract : public BaseContract #define GC_TRIGGERS do { STATIC_CONTRACT_GC_TRIGGERS; REQUEST_TEST(Contract::GC_Triggers, Contract::GC_Disabled); } while(0) #define GC_NOTRIGGER do { STATIC_CONTRACT_GC_NOTRIGGER; REQUEST_TEST(Contract::GC_NoTrigger, Contract::GC_Disabled); } while(0) -// Notice there's no static contract component to this. It's -// perfectly reasonable to find EE_THREAD_REQUIRED inside the scope of -// EE_THREAD_NOT_REQUIRED (e.g., an EE_THREAD_NOT_REQUIRED scope can have two -// possible code paths--one with an EE Thread and one without). So we can't do -// any meaningful testing statically. It's all gotta be done at runtime. -#define EE_THREAD_NOT_REQUIRED \ - do { REQUEST_TEST(Contract::EE_THREAD_Not_Required, Contract::EE_THREAD_Disabled); } while(0) - -#define EE_THREAD_REQUIRED do { REQUEST_TEST(Contract::EE_THREAD_Required, Contract::EE_THREAD_Disabled); } while(0) - #define HOST_NOCALLS do { STATIC_CONTRACT_HOST_NOCALLS; REQUEST_TEST(Contract::HOST_NoCalls, Contract::HOST_Disabled); } while(0) #define HOST_CALLS do { STATIC_CONTRACT_HOST_CALLS; REQUEST_TEST(Contract::HOST_Calls, Contract::HOST_Disabled); } while(0) @@ -74,12 +64,11 @@ class EEContract : public BaseContract #define GC_NOTRIGGER #define HOST_NOCALLS #define HOST_CALLS -#define EE_THREAD_NOT_REQUIRED -#define EE_THREAD_REQUIRED - #endif // ENABLE_CONTRACTS_IMPL +#define EE_THREAD_NOT_REQUIRED + // Replace the CONTRACT macro with the EE version #undef CONTRACT #define CONTRACT(_returntype) CUSTOM_CONTRACT(EEContract, _returntype) diff --git a/src/coreclr/vm/eedbginterfaceimpl.cpp b/src/coreclr/vm/eedbginterfaceimpl.cpp index 002250d72825e..ba48f89faf13b 100644 --- a/src/coreclr/vm/eedbginterfaceimpl.cpp +++ b/src/coreclr/vm/eedbginterfaceimpl.cpp @@ -54,7 +54,7 @@ Thread* EEDbgInterfaceImpl::GetThread(void) CONTRACT_END; #endif - return ::GetThread(); + return ::GetThreadNULLOk(); } #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/eehash.inl b/src/coreclr/vm/eehash.inl index cfc23bfffc301..108bf8711eaaf 100644 --- a/src/coreclr/vm/eehash.inl +++ b/src/coreclr/vm/eehash.inl @@ -677,10 +677,8 @@ BOOL EEHashTableBase::GrowHashTable() CONTRACTL_END #if defined(_DEBUG) && !defined(CROSSGEN_COMPILE) - BEGIN_GETTHREAD_ALLOWED; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); _ASSERTE(!g_fEEStarted || (pThread == NULL) || (pThread->PreemptiveGCDisabled())); - END_GETTHREAD_ALLOWED; #endif // Make the new bucket table 4 times bigger diff --git a/src/coreclr/vm/eepolicy.cpp b/src/coreclr/vm/eepolicy.cpp index 8607432ac3f00..8abaf6f4ee7c8 100644 --- a/src/coreclr/vm/eepolicy.cpp +++ b/src/coreclr/vm/eepolicy.cpp @@ -133,8 +133,7 @@ void EEPolicy::HandleStackOverflow() STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleStackOverflow\n"); - Thread *pThread = GetThread(); - + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL) { // For security reason, it is not safe to continue execution if stack overflow happens @@ -353,7 +352,7 @@ void LogInfoForFatalError(UINT exitCode, LPCWSTR pszMessage, LPCWSTR errorSource static Thread *volatile s_pCrashingThread = FatalErrorNotSeenYet; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); Thread *pPreviousThread = InterlockedCompareExchangeT(&s_pCrashingThread, pThread, FatalErrorNotSeenYet); if (pPreviousThread == pThread) @@ -541,7 +540,7 @@ void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage { #ifdef DEBUGGING_SUPPORTED //Give a managed debugger a chance if this fatal error is on a managed thread. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread && !g_fFatalErrorOccuredOnGCThread) { @@ -639,7 +638,7 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE { DisplayStackOverflowException(); - HandleHolder stackDumpThreadHandle = Thread::CreateUtilityThread(Thread::StackSize_Small, LogStackOverflowStackTraceThread, GetThread(), W(".NET Stack overflow trace logger")); + HandleHolder stackDumpThreadHandle = Thread::CreateUtilityThread(Thread::StackSize_Small, LogStackOverflowStackTraceThread, GetThreadNULLOk(), W(".NET Stack overflow trace logger")); if (stackDumpThreadHandle != INVALID_HANDLE_VALUE) { // Wait for the stack trace logging completion @@ -670,7 +669,7 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE if (!fSkipDebugger) { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); BOOL fTreatAsNativeUnhandledException = FALSE; if (pThread) { @@ -789,7 +788,7 @@ int NOINLINE EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR // because debugger is going to take CrstDebuggerMutex, whose lock level is higher than that of // CrstThreadStore. It should be safe to release the lock since execution will not be resumed // after fatal errors. - if (ThreadStore::HoldingThreadStore(GetThread())) + if (ThreadStore::HoldingThreadStore(GetThreadNULLOk())) { ThreadSuspend::UnlockThreadStore(); } diff --git a/src/coreclr/vm/eetoprofinterfaceimpl.cpp b/src/coreclr/vm/eetoprofinterfaceimpl.cpp index 22708c8d0d90b..34cd1475a4236 100644 --- a/src/coreclr/vm/eetoprofinterfaceimpl.cpp +++ b/src/coreclr/vm/eetoprofinterfaceimpl.cpp @@ -25,14 +25,14 @@ // CLR_TO_PROFILER_ENTRYPOINT (typical choice) // This is used for calling ICorProfilerCallback* methods that either have no // ThreadID parameters, or if they do have a ThreadID parameter, the parameter's -// value is always the *current* ThreadID (i.e., param == GetThread()). This will +// value is always the *current* ThreadID (i.e., param == GetThreadNULLOk()). This will // also force a mode switch to preemptive before calling the profiler. // CLR_TO_PROFILER_ENTRYPOINT_FOR_THREAD // Similar to above, except these are used for ICorProfilerCallback* methods that // specify a ThreadID parameter whose value may not always be the *current* // ThreadID. You must specify the ThreadID as the first parameter to these // macros. The macro will then use your ThreadID rather than that of the current -// GetThread(), to assert that the callback is currently allowed for that +// GetThreadNULLOk(), to assert that the callback is currently allowed for that // ThreadID (i.e., that we have not yet issued a ThreadDestroyed() for that // ThreadID). // @@ -4468,7 +4468,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionThrown( LL_INFO1000, "**PROF: ExceptionThrown. ObjectID: 0x%p. ThreadID: 0x%p\n", thrownObjectId, - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -4504,7 +4504,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionSearchFunctionEnter( CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: ExceptionSearchFunctionEnter. ThreadID: 0x%p, functionId: 0x%p\n", - GetThread(), + GetThreadNULLOk(), functionId)); { @@ -4540,7 +4540,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionSearchFunctionLeave() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: ExceptionSearchFunctionLeave. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -4575,7 +4575,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionSearchFilterEnter(FunctionID functionId) CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: ExceptionSearchFilterEnter. ThreadID: 0x%p, functionId: 0x%p\n", - GetThread(), + GetThreadNULLOk(), functionId)); { @@ -4611,7 +4611,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionSearchFilterLeave() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: ExceptionFilterLeave. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -4646,7 +4646,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionSearchCatcherFound(FunctionID functionId CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: ExceptionSearchCatcherFound. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -4696,7 +4696,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionUnwindFunctionEnter(FunctionID functionI (LF_CORPROF, LL_INFO1000, "**PROF: ExceptionUnwindFunctionEnter. ThreadID: 0x%p, functionId: 0x%p\n", - GetThread(), + GetThreadNULLOk(), functionId)); { @@ -4735,7 +4735,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionUnwindFunctionLeave() (LF_CORPROF, LL_INFO1000, "**PROF: ExceptionUnwindFunctionLeave. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -4773,7 +4773,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionUnwindFinallyEnter(FunctionID functionId (LF_CORPROF, LL_INFO1000, "**PROF: ExceptionUnwindFinallyEnter. ThreadID: 0x%p, functionId: 0x%p\n", - GetThread(), + GetThreadNULLOk(), functionId)); { @@ -4812,7 +4812,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionUnwindFinallyLeave() (LF_CORPROF, LL_INFO1000, "**PROF: ExceptionUnwindFinallyLeave. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -4849,7 +4849,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionCatcherEnter(FunctionID functionId, Obje kEE2PNoTrigger, (LF_CORPROF, LL_INFO1000, "**PROF: ExceptionCatcherEnter. ThreadID: 0x%p, functionId: 0x%p\n", - GetThread(), + GetThreadNULLOk(), functionId)); { @@ -4886,7 +4886,7 @@ HRESULT EEToProfInterfaceImpl::ExceptionCatcherLeave() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: ExceptionCatcherLeave. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { @@ -5023,7 +5023,7 @@ HRESULT EEToProfInterfaceImpl::RuntimeSuspendStarted( (LF_CORPROF, LL_INFO100, "**PROF: RuntimeSuspendStarted. ThreadID 0x%p.\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5061,7 +5061,7 @@ HRESULT EEToProfInterfaceImpl::RuntimeSuspendFinished() (LF_CORPROF, LL_INFO100, "**PROF: RuntimeSuspendFinished. ThreadID 0x%p.\n", - GetThread())); + GetThreadNULLOk())); { @@ -5105,7 +5105,7 @@ HRESULT EEToProfInterfaceImpl::RuntimeSuspendAborted() (LF_CORPROF, LL_INFO100, "**PROF: RuntimeSuspendAborted. ThreadID 0x%p.\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5141,7 +5141,7 @@ HRESULT EEToProfInterfaceImpl::RuntimeResumeStarted() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO100, "**PROF: RuntimeResumeStarted. ThreadID 0x%p.\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5175,7 +5175,7 @@ HRESULT EEToProfInterfaceImpl::RuntimeResumeFinished() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO100, "**PROF: RuntimeResumeFinished. ThreadID 0x%p.\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5266,7 +5266,7 @@ HRESULT EEToProfInterfaceImpl::RuntimeThreadSuspended(ThreadID suspendedThreadId // (4) from occurring until 3) is completely done. It's sufficient to determine we're in 3) by noting // whether the callback is reporting that a thread is "suspending itself" (i.e., suspendedThreadId == threadId) - ForbidSuspendThreadHolder forbidSuspendThread((Thread *) suspendedThreadId == GetThread()); + ForbidSuspendThreadHolder forbidSuspendThread((Thread *) suspendedThreadId == GetThreadNULLOk()); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5367,7 +5367,7 @@ HRESULT EEToProfInterfaceImpl::RemotingClientInvocationStarted() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingClientInvocationStarted. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5402,7 +5402,7 @@ HRESULT EEToProfInterfaceImpl::RemotingClientSendingMessage(GUID *pCookie, BOOL CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingClientSendingMessage. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5437,7 +5437,7 @@ HRESULT EEToProfInterfaceImpl::RemotingClientReceivingReply(GUID * pCookie, BOOL CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingClientReceivingReply. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5472,7 +5472,7 @@ HRESULT EEToProfInterfaceImpl::RemotingClientInvocationFinished() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingClientInvocationFinished. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5507,7 +5507,7 @@ HRESULT EEToProfInterfaceImpl::RemotingServerReceivingMessage(GUID *pCookie, BOO CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingServerReceivingMessage. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5542,7 +5542,7 @@ HRESULT EEToProfInterfaceImpl::RemotingServerInvocationStarted() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingServerInvocationStarted. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5577,7 +5577,7 @@ HRESULT EEToProfInterfaceImpl::RemotingServerInvocationReturned() CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingServerInvocationReturned. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, @@ -5612,7 +5612,7 @@ HRESULT EEToProfInterfaceImpl::RemotingServerSendingReply(GUID *pCookie, BOOL fI CLR_TO_PROFILER_ENTRYPOINT((LF_CORPROF, LL_INFO1000, "**PROF: RemotingServerSendingReply. ThreadID: 0x%p\n", - GetThread())); + GetThreadNULLOk())); { // All callbacks are really NOTHROW, but that's enforced partially by the profiler, diff --git a/src/coreclr/vm/encee.cpp b/src/coreclr/vm/encee.cpp index 24252f397f031..4ecff954111da 100644 --- a/src/coreclr/vm/encee.cpp +++ b/src/coreclr/vm/encee.cpp @@ -330,7 +330,13 @@ HRESULT EditAndContinueModule::UpdateMethod(MethodDesc *pMethod) // to the Method's code must be to the call/jmp blob immediately in front of the // MethodDesc itself. See MethodDesc::IsEnCMethod() // - pMethod->Reset(); + pMethod->ResetCodeEntryPoint(); + + if (pMethod->HasNativeCodeSlot()) + { + RelativePointer *pRelPtr = (RelativePointer *)pMethod->GetAddrOfNativeCodeSlot(); + pRelPtr->SetValueMaybeNull(NULL); + } return S_OK; } @@ -532,7 +538,6 @@ PCODE EditAndContinueModule::JitUpdatedFunction( MethodDesc *pMD, // so that gc can crawl the stack and do the right thing. _ASSERTE(pOrigContext); Thread *pCurThread = GetThread(); - _ASSERTE(pCurThread); FrameWithCookie resFrame(pOrigContext); resFrame.Push(pCurThread); @@ -801,8 +806,6 @@ NOINLINE void EditAndContinueModule::FixContextAndResume( LOG((LF_ENC, LL_INFO100, "EnCModule::ResumeInUpdatedFunction: Resume at EIP=0x%x\n", pNewCodeInfo->GetCodeAddress())); Thread *pCurThread = GetThread(); - _ASSERTE(pCurThread); - pCurThread->SetFilterContext(pContext); SetIP(pContext, pNewCodeInfo->GetCodeAddress()); diff --git a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h index 0f8d100c8a7a1..1b43cd52bd12c 100644 --- a/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h @@ -10,43 +10,49 @@ #include "ds-dump-protocol.h" #undef DS_LOG_ALWAYS_0 -#define DS_LOG_ALWAYS_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg) +#define DS_LOG_ALWAYS_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg "\n") #undef DS_LOG_ALWAYS_1 -#define DS_LOG_ALWAYS_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg, data1) +#define DS_LOG_ALWAYS_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg "\n", data1) #undef DS_LOG_ALWAYS_2 -#define DS_LOG_ALWAYS_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg, data1, data2) +#define DS_LOG_ALWAYS_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_ALWAYS, msg "\n", data1, data2) #undef DS_LOG_INFO_0 -#define DS_LOG_INFO_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, msg) +#define DS_LOG_INFO_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO10, msg "\n") #undef DS_LOG_INFO_1 -#define DS_LOG_INFO_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO10, msg, data1) +#define DS_LOG_INFO_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO10, msg "\n", data1) #undef DS_LOG_INFO_2 -#define DS_LOG_INFO_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, msg, data1, data2) +#define DS_LOG_INFO_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO10, msg "\n", data1, data2) #undef DS_LOG_ERROR_0 -#define DS_LOG_ERROR_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, msg) +#define DS_LOG_ERROR_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, msg "\n") #undef DS_LOG_ERROR_1 -#define DS_LOG_ERROR_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_ERROR, msg, data1) +#define DS_LOG_ERROR_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_ERROR, msg "\n", data1) #undef DS_LOG_ERROR_2 -#define DS_LOG_ERROR_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_ERROR, msg, data1, data2) +#define DS_LOG_ERROR_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_ERROR, msg "\n", data1, data2) #undef DS_LOG_WARNING_0 -#define DS_LOG_WARNING_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_WARNING, msg) +#define DS_LOG_WARNING_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_WARNING, msg "\n") #undef DS_LOG_WARNING_1 -#define DS_LOG_WARNING_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, msg, data1) +#define DS_LOG_WARNING_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_WARNING, msg "\n", data1) #undef DS_LOG_WARNING_2 -#define DS_LOG_WARNING_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_WARNING, msg, data1, data2) +#define DS_LOG_WARNING_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_WARNING, msg "\n", data1, data2) #undef DS_LOG_DEBUG_0 -#define DS_LOG_DEBUG_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO1000, msg) +#define DS_LOG_DEBUG_0(msg) STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_INFO1000, msg "\n") + +#undef DS_LOG_DEBUG_1 +#define DS_LOG_DEBUG_1(msg, data1) STRESS_LOG1(LF_DIAGNOSTICS_PORT, LL_INFO1000, msg "\n", data1) + +#undef DS_LOG_DEBUG_2 +#define DS_LOG_DEBUG_2(msg, data1, data2) STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_INFO1000, msg "\n", data1, data2) #undef DS_ENTER_BLOCKING_PAL_SECTION #define DS_ENTER_BLOCKING_PAL_SECTION diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h index 84712e9130104..c7edf06b5acd6 100644 --- a/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h @@ -2767,7 +2767,7 @@ ep_rt_thread_handle_t ep_rt_thread_get_handle (void) { STATIC_CONTRACT_NOTHROW; - return GetThread (); + return GetThreadNULLOk (); } static @@ -2865,7 +2865,7 @@ ep_rt_thread_set_activity_id ( * ThreadSequenceNumberMap. */ -EP_RT_DEFINE_HASH_MAP(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, EventPipeThreadSessionState *, uint32_t) +EP_RT_DEFINE_HASH_MAP_REMOVE(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, EventPipeThreadSessionState *, uint32_t) EP_RT_DEFINE_HASH_MAP_ITERATOR(thread_sequence_number_map, ep_rt_thread_sequence_number_hash_map_t, ep_rt_thread_sequence_number_hash_map_iterator_t, EventPipeThreadSessionState *, uint32_t) /* diff --git a/src/coreclr/vm/eventing/eventpipe/ep-rt-types-coreclr.h b/src/coreclr/vm/eventing/eventpipe/ep-rt-types-coreclr.h index 84cfe3be7d179..e81be5c77911c 100644 --- a/src/coreclr/vm/eventing/eventpipe/ep-rt-types-coreclr.h +++ b/src/coreclr/vm/eventing/eventpipe/ep-rt-types-coreclr.h @@ -325,10 +325,10 @@ typedef struct _rt_coreclr_thread_params_t { */ #undef ep_rt_thread_sequence_number_hash_map_t -typedef struct _rt_coreclr_table_default_internal_t ep_rt_thread_sequence_number_hash_map_t; +typedef struct _rt_coreclr_table_remove_internal_t ep_rt_thread_sequence_number_hash_map_t; #undef ep_rt_thread_sequence_number_hash_map_iterator_t -typedef class _rt_coreclr_table_default_internal_t::table_type_t::Iterator ep_rt_thread_sequence_number_hash_map_iterator_t; +typedef class _rt_coreclr_table_remove_internal_t::table_type_t::Iterator ep_rt_thread_sequence_number_hash_map_iterator_t; #endif /* ENABLE_PERFTRACING */ #endif /* __EVENTPIPE_RT_TYPES_CORECLR_H__ */ diff --git a/src/coreclr/vm/eventpipeinternal.cpp b/src/coreclr/vm/eventpipeinternal.cpp index 8bcc00e0daad7..7d0e429be2e08 100644 --- a/src/coreclr/vm/eventpipeinternal.cpp +++ b/src/coreclr/vm/eventpipeinternal.cpp @@ -162,7 +162,7 @@ int QCALLTYPE EventPipeInternal::EventActivityIdControl(uint32_t controlCode, GU BEGIN_QCALL; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL || pActivityId == NULL) { retVal = 1; diff --git a/src/coreclr/vm/eventreporter.cpp b/src/coreclr/vm/eventreporter.cpp index f8e3ef7c0b265..318cc21320f62 100644 --- a/src/coreclr/vm/eventreporter.cpp +++ b/src/coreclr/vm/eventreporter.cpp @@ -547,8 +547,6 @@ StackWalkAction LogCallstackForEventReporterCallback( void LogCallstackForEventReporterWorker(EventReporter& reporter) { Thread* pThread = GetThread(); - _ASSERTE (pThread); - SmallStackSString WordAt; if (!WordAt.LoadResource(CCompRC::Optional, IDS_ER_WORDAT)) @@ -670,7 +668,7 @@ void DoReportForUnhandledNativeException(PEXCEPTION_POINTERS pExceptionInfo) if (ShouldLogInEventLog()) { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); EventReporter reporter(EventReporter::ERT_UnhandledException); EX_TRY { diff --git a/src/coreclr/vm/eventtrace.cpp b/src/coreclr/vm/eventtrace.cpp index 0f014ab8694c9..99620ac07e2a8 100644 --- a/src/coreclr/vm/eventtrace.cpp +++ b/src/coreclr/vm/eventtrace.cpp @@ -321,7 +321,7 @@ ETW::SamplingLog::EtwStackWalkStatus ETW::SamplingLog::SaveCurrentStack(int skip return ETW::SamplingLog::UnInitialized; } #endif // TARGET_AMD64 - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL) { return ETW::SamplingLog::UnInitialized; @@ -3340,7 +3340,7 @@ BOOL ETW::TypeSystemLog::ShouldLogType(TypeHandle th) // When we have a thread context, default to calling the API that requires one which // reduces the cost of locking. - if (GetThread() != NULL) + if (GetThreadNULLOk() != NULL) { LookupOrCreateTypeLoggingInfo(th, &fCreatedNew); } @@ -4701,7 +4701,7 @@ VOID ETW::ExceptionLog::ExceptionThrown(CrawlFrame *pCf, BOOL bIsReThrownExcept CONTRACTL { NOTHROW; GC_TRIGGERS; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(GetThread()->GetThrowable() != NULL); } CONTRACTL_END; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 34afa5b2458c9..1b192e683695a 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -551,10 +551,10 @@ void CreateTypeInitializationExceptionObject(LPCWSTR pTypeThatFailed, PRECONDITION(IsProtectedByGCFrame(pInnerException)); PRECONDITION(IsProtectedByGCFrame(pInitException)); PRECONDITION(IsProtectedByGCFrame(pThrowable)); - PRECONDITION(CheckPointer(GetThread())); + PRECONDITION(CheckPointer(GetThreadNULLOk())); } CONTRACTL_END; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); *pThrowable = NULL; // This will make sure to put the thread back to its original state if something @@ -2974,7 +2974,7 @@ void FreeExceptionData(ExceptionData *pedata) // @NICE: At one point, we had the comment: // (DM) Remove this when shutdown works better. // This test may no longer be necessary. Remove at own peril. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (!pThread) return; @@ -3384,7 +3384,6 @@ BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT #ifndef TARGET_UNIX // Watson is supported on Windows only Thread *pThread = GetThread(); - _ASSERTE(pThread); if (pThread && (currentIP != 0)) { @@ -3499,7 +3498,7 @@ BOOL IsAsyncThreadException(OBJECTREF *pThrowable) { STATIC_CONTRACT_MODE_COOPERATIVE; STATIC_CONTRACT_FORBID_FAULT; - if ( (GetThread() && GetThread()->IsRudeAbort() && GetThread()->IsRudeAbortInitiated()) + if ( (GetThreadNULLOk() && GetThread()->IsRudeAbort() && GetThread()->IsRudeAbortInitiated()) ||IsExceptionOfType(kThreadAbortException, pThrowable) ||IsExceptionOfType(kThreadInterruptedException, pThrowable)) { return TRUE; @@ -3519,7 +3518,7 @@ BOOL IsUncatchable(OBJECTREF *pThrowable) _ASSERTE(pThrowable != NULL); - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread) { @@ -3982,7 +3981,7 @@ LONG WatsonLastChance( // EXCEPTION_CONTINUE_SEARCH, _CONTINUE_ } else { - g_pDebugInterface->LaunchDebuggerForUser(GetThread(), pExceptionInfo, FALSE, FALSE); + g_pDebugInterface->LaunchDebuggerForUser(GetThreadNULLOk(), pExceptionInfo, FALSE, FALSE); } return EXCEPTION_CONTINUE_SEARCH; @@ -4203,8 +4202,7 @@ bool CheckThreadExceptionStateForInterception() { LIMITED_METHOD_CONTRACT; - Thread* pThread = GetThread(); - + Thread* pThread = GetThreadNULLOk(); if (pThread == NULL) { return false; @@ -4249,7 +4247,7 @@ BOOL ExceptionIsAlwaysSwallowed(EXCEPTION_POINTERS *pExceptionInfo) if (IsComPlusException(pExceptionInfo->ExceptionRecord)) { // Our exception code. Get the current exception from the thread. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread) { OBJECTREF throwable; @@ -4614,7 +4612,7 @@ LONG InternalUnhandledExceptionFilter_Worker( } // We don't do anything when this is called from an unmanaged thread. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); #ifdef _DEBUG static bool bBreakOnUncaught = false; @@ -4881,7 +4879,7 @@ lDone: ; #ifdef _DEBUG char buffer[200]; sprintf_s(buffer, 200, "\nInternal error: Uncaught exception was thrown from IP = %p in UnhandledExceptionFilter_Worker on thread 0x%08x\n", - param.ExceptionEIP, ((GetThread() == NULL) ? NULL : GetThread()->GetThreadId())); + param.ExceptionEIP, ((GetThreadNULLOk() == NULL) ? NULL : GetThread()->GetThreadId())); PrintToStdErrA(buffer); _ASSERTE(!"Unexpected exception in UnhandledExceptionFilter_Worker"); #endif @@ -5046,8 +5044,8 @@ LONG EntryPointFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID _pData) return EXCEPTION_CONTINUE_SEARCH; } - Thread* pThread = GetThread(); - if (pThread && !GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException)) + Thread* pThread = GetThreadNULLOk(); + if (pThread && !pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException)) { // Invoke the UEF worker to perform unhandled exception processing ret = InternalUnhandledExceptionFilter_Worker (pExceptionInfo); @@ -5105,7 +5103,7 @@ LONG __stdcall COMUnhandledExceptionFilter( // EXCEPTION_CONTINUE_SEARCH or // various runtimes again. // // Thus, check if this UEF has already been invoked in context of this thread and runtime and if so, dont invoke it again. - if (GetThread() && (GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))) + if (GetThreadNULLOk() && (GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))) { LOG((LF_EH, LL_INFO10, "Exiting COMUnhandledExceptionFilter since we have already done UE processing for this thread!\n")); return retVal; @@ -5115,7 +5113,7 @@ LONG __stdcall COMUnhandledExceptionFilter( // EXCEPTION_CONTINUE_SEARCH or retVal = InternalUnhandledExceptionFilter(pExceptionInfo); // If thread object exists, mark that this thread has done unhandled exception processing - if (GetThread()) + if (GetThreadNULLOk()) { LOG((LF_EH, LL_INFO100, "COMUnhandledExceptionFilter: setting TSNC_ProcessedUnhandledException\n")); GetThread()->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException); @@ -5257,7 +5255,7 @@ DefaultCatchHandler(PEXCEPTION_POINTERS pExceptionPointers, int suppressSelectiveBreak = false; // to filter for the case where breakOnUncaught == "2" #endif - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // The following reduces a window for a race during shutdown. if (!pThread) @@ -5444,7 +5442,7 @@ BOOL NotifyAppDomainsOfUnhandledException( LOG((LF_EH, LL_INFO10, "In NotifyAppDomainsOfUnhandledException\n")); - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // The following reduces a window for a race during shutdown. if (!pThread) @@ -5596,7 +5594,6 @@ static LONG ThreadBaseExceptionFilter_Worker(PEXCEPTION_POINTERS pExceptionInfo, _ASSERTE(!g_fNoExceptions); Thread* pThread = GetThread(); - _ASSERTE(pThread); #ifdef _DEBUG if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException) && @@ -6313,7 +6310,7 @@ CreateCOMPlusExceptionObject(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord } CONTRACTL_END; - _ASSERTE(GetThread() == pThread); + _ASSERTE(GetThreadNULLOk() == pThread); DWORD exceptionCode = pExceptionRecord->ExceptionCode; @@ -6477,7 +6474,7 @@ bool IsGcMarker(CONTEXT* pContext, EXCEPTION_RECORD *pExceptionRecord) // // Note these "fake" AVs will be reported by the kernel as reads from // address 0xF...F so we also use that as a screen. - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (exceptionCode == STATUS_ACCESS_VIOLATION && GCStress::IsEnabled() && pExceptionRecord->ExceptionInformation[0] == 0 && @@ -6571,7 +6568,7 @@ IsDebuggerFault(EXCEPTION_RECORD *pExceptionRecord, // Even if a debugger is not attached, we must let the debugger handle the exception in case it's coming from a // patch-skipper. if ((!IsComPlusException(pExceptionRecord)) && - (GetThread() != NULL) && + (GetThreadNULLOk() != NULL) && (g_pDebugInterface != NULL) && g_pDebugInterface->FirstChanceNativeException(pExceptionRecord, pContext, @@ -6612,7 +6609,7 @@ BOOL IsIPinVirtualStub(PCODE f_IP) { LIMITED_METHOD_CONTRACT; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); // We may not have a managed thread object. Example is an AV on the helper thread. // (perhaps during StubManager::IsStub) @@ -7138,7 +7135,7 @@ LONG WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_NO_MEMORY)); - pThread = GetThread(); + pThread = GetThreadNULLOk(); // // Since we are in an OOM situation, we test the thread object before logging since if the @@ -7279,7 +7276,7 @@ LONG WINAPI CLRVectoredExceptionHandlerPhase2(PEXCEPTION_POINTERS pExceptionInfo // // @TODO: I'd love a way to call into the debugger with GCX_NOTRIGGER still in scope, and force them to make // the choice to break the no-trigger region after taking all necessary precautions. - if (IsDebuggerFault(pExceptionRecord, pExceptionInfo->ContextRecord, pExceptionRecord->ExceptionCode, GetThread())) + if (IsDebuggerFault(pExceptionRecord, pExceptionInfo->ContextRecord, pExceptionRecord->ExceptionCode, GetThreadNULLOk())) { return EXCEPTION_CONTINUE_EXECUTION; } @@ -7460,7 +7457,7 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExcepti // time, then skip the check for whether or not the AV is in our impl. // AVs are ok on the Helper thread (for which there is no pThread object, // and so the AVInRuntime holder doesn't work. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); bool fAVisOk = (IsDbgHelperSpecialThread() || IsETWRundownSpecialThread() || @@ -7893,7 +7890,7 @@ LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo) // exceptions on this thread. Indeed, even checking to see if the faulting // address is in JITted code is problematic if we have no Thread object, since // this thread will bypass all our locks. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread) { @@ -8256,7 +8253,7 @@ LONG NotifyOfCHFFilterWrapper( // 1) The thread object has been set up. // 2) The thread has an exception on it. // 3) The exception is the same as the one this filter is called on. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if ( (pThread == NULL) || (pThread->GetExceptionState()->GetContextRecord() == NULL) || (GetSP(pThread->GetExceptionState()->GetContextRecord()) != GetSP(pExceptionInfo->ContextRecord) ) ) @@ -8665,8 +8662,6 @@ void SetReversePInvokeEscapingUnhandledExceptionStatus(BOOL fIsUnwinding, LIMITED_METHOD_CONTRACT; Thread *pCurThread = GetThread(); - _ASSERTE(pCurThread); - if (pCurThread->GetExceptionState()->IsExceptionInProgress()) { if (!fIsUnwinding) @@ -8732,7 +8727,7 @@ BOOL SetupWatsonBucketsForNonPreallocatedExceptions(OBJECTREF oThrowable /* = NU GC_TRIGGERS; MODE_COOPERATIVE; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); } CONTRACTL_END; @@ -8849,7 +8844,7 @@ BOOL SetupWatsonBucketsForEscapingPreallocatedExceptions() GC_TRIGGERS; MODE_ANY; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); } CONTRACTL_END; @@ -8978,7 +8973,7 @@ void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject) GC_TRIGGERS; MODE_ANY; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); } CONTRACTL_END; @@ -9157,7 +9152,7 @@ BOOL IsThrowableThreadAbortException(OBJECTREF oThrowable) GC_NOTRIGGER; MODE_COOPERATIVE; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(oThrowable != NULL); } CONTRACTL_END; @@ -9206,7 +9201,7 @@ PTR_ExInfo GetEHTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable, GC_NOTRIGGER; MODE_COOPERATIVE; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(oPreAllocThrowable != NULL); PRECONDITION(CLRException::IsPreallocatedExceptionObject(oPreAllocThrowable)); PRECONDITION(IsWatsonEnabled()); @@ -9265,7 +9260,7 @@ PTR_EHWatsonBucketTracker GetWatsonBucketTrackerForPreallocatedException(OBJECTR GC_TRIGGERS; MODE_COOPERATIVE; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(oPreAllocThrowable != NULL); PRECONDITION(CLRException::IsPreallocatedExceptionObject(oPreAllocThrowable)); PRECONDITION(IsWatsonEnabled()); @@ -9368,7 +9363,7 @@ PTR_EHWatsonBucketTracker GetWatsonBucketTrackerForPreallocatedException(OBJECTR { if (fCaptureBucketsIfNotPresent) { - pWBTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, GetThread(), &gc.oPreAllocThrowable); + pWBTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, GetThreadNULLOk(), &gc.oPreAllocThrowable); // Check if we have the buckets now if (pWBTracker->RetrieveWatsonBuckets() != NULL) @@ -9429,7 +9424,7 @@ BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException) GC_TRIGGERS; MODE_ANY; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(refException != NULL); PRECONDITION(IsWatsonEnabled()); } @@ -9689,7 +9684,7 @@ void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp) GC_TRIGGERS; MODE_ANY; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(!(GetThread()->GetExceptionState()->GetFlags()->GotWatsonBucketDetails())); PRECONDITION(adjustedIp != NULL); PRECONDITION(IsWatsonEnabled()); @@ -10215,7 +10210,7 @@ BOOL CopyWatsonBucketsToThrowable(PTR_VOID pUnmanagedBuckets, OBJECTREF oTargetT GC_TRIGGERS; MODE_COOPERATIVE; THROWS; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(pUnmanagedBuckets != NULL); PRECONDITION(!CLRException::IsPreallocatedExceptionObject((oTargetThrowable == NULL)?GetThread()->GetThrowable():oTargetThrowable)); PRECONDITION(IsWatsonEnabled()); @@ -10304,7 +10299,7 @@ void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOrigin GC_TRIGGERS; MODE_ANY; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(IsWatsonEnabled()); } CONTRACTL_END; @@ -10869,7 +10864,7 @@ PTR_ExInfo GetEHTrackerForException(OBJECTREF oThrowable, PTR_ExInfo pStartingEH GC_NOTRIGGER; MODE_COOPERATIVE; NOTHROW; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(oThrowable != NULL); } CONTRACTL_END; @@ -11054,7 +11049,7 @@ BOOL ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(ExceptionN THROWS; GC_TRIGGERS; MODE_COOPERATIVE; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(notificationType != UnhandledExceptionHandler); } CONTRACTL_END; @@ -11124,7 +11119,7 @@ void ExceptionNotifications::DeliverNotificationInternal(ExceptionNotificationHa } CONTRACTL_END; - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); _ASSERTE(pCurThread != NULL); // Get the current AppDomain diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 478eb2c7a42f6..4f67a680c1975 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -2045,7 +2045,7 @@ void ExceptionTracker::DebugLogTrackerRanges(__in_z const char *pszTag) } CONTRACTL_END; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); ExceptionTracker* pTracker = pThread ? pThread->GetExceptionState()->m_pCurrentTracker : NULL; int i = 0; @@ -2430,8 +2430,6 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame( pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName)); Thread *pThread = GetThread(); - _ASSERTE (pThread); - INDEBUG( DumpClauses(pcfThisFrame->GetJitManager(), pcfThisFrame->GetMethodToken(), uMethodStartPC, uControlPC) ); bool fIsILStub = pMD->IsILStub(); @@ -3403,7 +3401,7 @@ void ExceptionTracker::PopTrackers( // Only call into PopTrackers if we have a managed thread and we have an exception progress. // Otherwise, the call below (to PopTrackers) is a noop. If this ever changes, then this short-circuit needs to be fixed. - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); if ((pCurThread != NULL) && (pCurThread->GetExceptionState()->IsExceptionInProgress())) { // Refer to the comment around ExceptionTracker::HasFrameBeenUnwoundByAnyActiveException @@ -3483,7 +3481,7 @@ void ExceptionTracker::PopTrackers( } CONTRACTL_END; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); ExceptionTracker* pTracker = (pThread ? pThread->GetExceptionState()->m_pCurrentTracker : NULL); // NOTE: @@ -4027,7 +4025,7 @@ BOOL ExceptionTracker::NotifyDebuggerOfStub(Thread* pThread, StackFrame sf, Fram if (g_EnableSIS) { - _ASSERTE(GetThread() == pThread); + _ASSERTE(GetThreadNULLOk() == pThread); GCX_COOP(); @@ -4189,7 +4187,7 @@ inline bool ExceptionTracker::IsValid() EX_TRY { - Thread* pThisThread = GetThread(); + Thread* pThisThread = GetThreadNULLOk(); if (m_pThread == pThisThread) { fRetVal = true; @@ -4224,7 +4222,7 @@ BOOL ExceptionTracker::ThrowableIsValid() UINT_PTR ExceptionTracker::DebugComputeNestingLevel() { UINT_PTR uNestingLevel = 0; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread) { @@ -4735,8 +4733,6 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar { // Get the thread and the thread exception state - they must exist at this point Thread *pCurThread = GetThread(); - _ASSERTE(pCurThread != NULL); - ThreadExceptionState * pCurTES = pCurThread->GetExceptionState(); _ASSERTE(pCurTES != NULL); } @@ -5074,7 +5070,7 @@ bool IsDivByZeroAnIntegerOverflow(PCONTEXT pContext) BOOL IsSafeToCallExecutionManager() { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // It is safe to call the ExecutionManager::IsManagedCode only if the current thread is in // the cooperative mode. Otherwise ExecutionManager::IsManagedCode could deadlock if @@ -5208,7 +5204,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) else { // This is a breakpoint or single step stop, we report it to the debugger. - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread != NULL && g_pDebugInterface != NULL) { #if (defined(TARGET_ARM) || defined(TARGET_ARM64)) @@ -5810,7 +5806,7 @@ UMThunkUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionRecord IN OUT PDISPATCHER_CONTEXT pDispatcherContext ) { - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread == NULL) { return ExceptionContinueSearch; } @@ -5882,7 +5878,7 @@ NOT_BIT64_ARG(IN ULONG MemoryStackFp), // // We check for thread object since this function is the personality routine of the UMThunk // and we can landup here even when thread creation (within the thunk) fails. - if (GetThread() != NULL) + if (GetThreadNULLOk() != NULL) { SetReversePInvokeEscapingUnhandledExceptionStatus(IS_UNWINDING(pExceptionRecord->ExceptionFlags), MemoryStackFp @@ -5913,8 +5909,6 @@ CallDescrWorkerUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionReco { Thread* pThread = GetThread(); - _ASSERTE(pThread); - if (pExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) { if (IS_UNWINDING(pExceptionRecord->ExceptionFlags)) diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index acfa06f07e102..68c0b118734ac 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -43,7 +43,7 @@ void ExInfo::CopyAndClearSource(ExInfo *from) { NOTHROW; GC_NOTRIGGER; - if (GetThread() != NULL) MODE_COOPERATIVE; else MODE_ANY; + if (GetThreadNULLOk() != NULL) MODE_COOPERATIVE; else MODE_ANY; FORBID_FAULT; } CONTRACTL_END; @@ -165,7 +165,7 @@ void ExInfo::UnwindExInfo(VOID* limit) { NOTHROW; // This function does not throw. GC_NOTRIGGER; - if (GetThread() != NULL) MODE_COOPERATIVE; else MODE_ANY; + if (GetThreadNULLOk() != NULL) MODE_COOPERATIVE; else MODE_ANY; } CONTRACTL_END; @@ -173,7 +173,7 @@ void ExInfo::UnwindExInfo(VOID* limit) #ifdef DEBUGGING_SUPPORTED // The debugger thread will be using this, even though it has no // Thread object associated with it. - _ASSERTE((GetThread() != NULL && GetThread()->PreemptiveGCDisabled()) || + _ASSERTE((GetThreadNULLOk() != NULL && GetThread()->PreemptiveGCDisabled()) || ((g_pDebugInterface != NULL) && (g_pDebugInterface->GetRCThreadId() == GetCurrentThreadId()))); #endif // DEBUGGING_SUPPORTED diff --git a/src/coreclr/vm/exstate.cpp b/src/coreclr/vm/exstate.cpp index 992cd361b6ef8..fc3df10c0f647 100644 --- a/src/coreclr/vm/exstate.cpp +++ b/src/coreclr/vm/exstate.cpp @@ -533,8 +533,6 @@ ThreadExceptionFlagHolder::ThreadExceptionFlagHolder(ThreadExceptionState::Threa WRAPPER_NO_CONTRACT; Thread* pThread = GetThread(); - _ASSERTE(pThread); - m_pExState = pThread->GetExceptionState(); m_flag = flag; diff --git a/src/coreclr/vm/fcall.cpp b/src/coreclr/vm/fcall.cpp index 6e595c0cc578d..67ea3feeb4d3d 100644 --- a/src/coreclr/vm/fcall.cpp +++ b/src/coreclr/vm/fcall.cpp @@ -257,10 +257,7 @@ FCallTransitionState::FCallTransitionState () WRAPPER_NO_CONTRACT; m_pThread = GetThread(); - _ASSERTE(m_pThread); - m_pPreviousHelperMethodFrameCallerList = m_pThread->m_pHelperMethodFrameCallerList; - m_pThread->m_pHelperMethodFrameCallerList = NULL; } @@ -278,8 +275,6 @@ PermitHelperMethodFrameState::PermitHelperMethodFrameState () WRAPPER_NO_CONTRACT; m_pThread = GetThread(); - _ASSERTE(m_pThread); - CONSISTENCY_CHECK_MSG((HelperMethodFrameCallerList*)-1 != m_pThread->m_pHelperMethodFrameCallerList, "fcall entry point is missing a FCALL_TRANSITION_BEGIN or a FCIMPL\n"); @@ -320,8 +315,6 @@ VOID PermitHelperMethodFrameState::CheckHelperMethodFramePermitted () // Thread *pThread = GetThread(); - _ASSERTE(pThread); - HelperMethodFrameCallerList *pList = pThread->m_pHelperMethodFrameCallerList; PCODE CurrentIP; TADDR CurrentSP; @@ -378,10 +371,7 @@ CompletedFCallTransitionState::CompletedFCallTransitionState () WRAPPER_NO_CONTRACT; Thread *pThread = GetThread(); - _ASSERTE(pThread); - m_pLastHelperMethodFrameCallerList = pThread->m_pHelperMethodFrameCallerList; - pThread->m_pHelperMethodFrameCallerList = (HelperMethodFrameCallerList*)-1; } @@ -391,8 +381,6 @@ CompletedFCallTransitionState::~CompletedFCallTransitionState () WRAPPER_NO_CONTRACT; Thread *pThread = GetThread(); - _ASSERTE(pThread); - pThread->m_pHelperMethodFrameCallerList = m_pLastHelperMethodFrameCallerList; } diff --git a/src/coreclr/vm/finalizerthread.cpp b/src/coreclr/vm/finalizerthread.cpp index 26afdc9064cee..b09b028c9ae1a 100644 --- a/src/coreclr/vm/finalizerthread.cpp +++ b/src/coreclr/vm/finalizerthread.cpp @@ -35,7 +35,7 @@ BOOL FinalizerThread::IsCurrentThreadFinalizer() { LIMITED_METHOD_CONTRACT; - return GetThread() == g_pFinalizerThread; + return GetThreadNULLOk() == g_pFinalizerThread; } void FinalizerThread::EnableFinalization() @@ -489,8 +489,6 @@ void FinalizerThread::FinalizerThreadWait(DWORD timeout) GCX_PREEMP(); - Thread *pThread = GetThread(); - ULONGLONG startTime = CLRGetTickCount64(); ULONGLONG endTime; if (timeout == INFINITE) diff --git a/src/coreclr/vm/finalizerthread.h b/src/coreclr/vm/finalizerthread.h index fdfc2ab3c52db..5beabd3dbd310 100644 --- a/src/coreclr/vm/finalizerthread.h +++ b/src/coreclr/vm/finalizerthread.h @@ -55,7 +55,7 @@ class FinalizerThread EnableFinalization(); // Do not wait for FinalizerThread if the current one is FinalizerThread. - if (GetThread() != GetFinalizerThread()) + if (GetThreadNULLOk() != GetFinalizerThread()) { // This wait must be alertable to handle cases where the current // thread's context is needed (i.e. RCW cleanup) diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index 1908bc775a153..10ba921108d8c 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1509,7 +1509,7 @@ struct IsObjRefProtectedScanContext : public ScanContext BOOL oref_protected; IsObjRefProtectedScanContext (OBJECTREF * oref) { - thread_under_crawl = GetThread (); + thread_under_crawl = GetThread(); promotion = TRUE; oref_to_check = oref; oref_protected = FALSE; diff --git a/src/coreclr/vm/gccover.cpp b/src/coreclr/vm/gccover.cpp index 64f8613e8b9f9..9765633eb5996 100644 --- a/src/coreclr/vm/gccover.cpp +++ b/src/coreclr/vm/gccover.cpp @@ -1312,7 +1312,7 @@ BOOL OnGcCoverageInterrupt(PCONTEXT regs) BYTE * savedInstrPtr = &gcCover->savedCode[offset]; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (!pThread) { // No thread at the moment so we aren't doing coverage for this function. @@ -1400,7 +1400,6 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) DWORD offset = codeInfo.GetRelOffset(); Thread *pThread = GetThread(); - _ASSERTE(pThread); // There is a race condition with the computation of `atCall`. Multiple threads could enter // this function (DoGcStress) at the same time. If one reads `*instrPtr` and sets `atCall` @@ -1799,4 +1798,3 @@ void DoGcStress (PCONTEXT regs, NativeCodeVersion nativeCodeVersion) } #endif // HAVE_GCCOVER - diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp index 9c6509d81a5c7..7849958d06874 100644 --- a/src/coreclr/vm/gcenv.ee.cpp +++ b/src/coreclr/vm/gcenv.ee.cpp @@ -87,7 +87,7 @@ static void ScanStackRoots(Thread * pThread, promote_func* fn, ScanContext* sc) // the threadstore lock. _ASSERTE(dbgOnly_IsSpecialEEThread() || - GetThread() == NULL || + GetThreadNULLOk() == NULL || // this is for background GC threads which always call this when EE is suspended. IsGCSpecialThread() || (GetThread() == ThreadSuspend::GetSuspensionThread() && ThreadStore::HoldingThreadStore())); @@ -376,7 +376,7 @@ gc_alloc_context * GCToEEInterface::GetAllocContext() { WRAPPER_NO_CONTRACT; - Thread* pThread = ::GetThread(); + Thread* pThread = ::GetThreadNULLOk(); if (!pThread) { return nullptr; @@ -425,7 +425,7 @@ bool GCToEEInterface::IsPreemptiveGCDisabled() { WRAPPER_NO_CONTRACT; - Thread* pThread = ::GetThread(); + Thread* pThread = ::GetThreadNULLOk(); return (pThread && pThread->PreemptiveGCDisabled()); } @@ -434,7 +434,7 @@ bool GCToEEInterface::EnablePreemptiveGC() { WRAPPER_NO_CONTRACT; - Thread* pThread = ::GetThread(); + Thread* pThread = ::GetThreadNULLOk(); if (pThread && pThread->PreemptiveGCDisabled()) { @@ -449,7 +449,7 @@ void GCToEEInterface::DisablePreemptiveGC() { WRAPPER_NO_CONTRACT; - Thread* pThread = ::GetThread(); + Thread* pThread = ::GetThreadNULLOk(); if (pThread) { pThread->DisablePreemptiveGC(); @@ -460,7 +460,7 @@ Thread* GCToEEInterface::GetThread() { WRAPPER_NO_CONTRACT; - return ::GetThread(); + return ::GetThreadNULLOk(); } // @@ -1125,7 +1125,7 @@ bool GCToEEInterface::GetBooleanConfigValue(const char* privateKey, const char* // otherwise, ask the config subsystem. if (CLRConfig::IsConfigOptionSpecified(configKey)) { - CLRConfig::ConfigDWORDInfo info { configKey , 0, CLRConfig::EEConfig_default }; + CLRConfig::ConfigDWORDInfo info { configKey , 0, CLRConfig::LookupOptions::Default }; *value = CLRConfig::GetConfigValue(info) != 0; return true; } @@ -1170,7 +1170,7 @@ bool GCToEEInterface::GetIntConfigValue(const char* privateKey, const char* publ // so have to fake it with getting the string and converting to uint64_t if (CLRConfig::IsConfigOptionSpecified(configKey)) { - CLRConfig::ConfigStringInfo info { configKey, CLRConfig::EEConfig_default }; + CLRConfig::ConfigStringInfo info { configKey, CLRConfig::LookupOptions::Default }; LPWSTR out = CLRConfig::GetConfigValue(info); if (!out) { @@ -1226,7 +1226,7 @@ bool GCToEEInterface::GetStringConfigValue(const char* privateKey, const char* p return false; } - CLRConfig::ConfigStringInfo info { configKey, CLRConfig::EEConfig_default }; + CLRConfig::ConfigStringInfo info { configKey, CLRConfig::LookupOptions::Default }; LPWSTR fromClrConfig = CLRConfig::GetConfigValue(info); LPCWSTR out = fromClrConfig; if (out == NULL) @@ -1689,4 +1689,3 @@ void GCToEEInterface::LogStressMsg(unsigned level, unsigned facility, const Stre { StressLog::LogMsg(level, facility, msg); } - diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 52c2c3c649c0d..8ca20eaca743f 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -1508,7 +1508,7 @@ OBJECTREF* GcInfoDecoder::GetRegisterSlot( } -#ifdef TARGET_UNIX +#if defined(TARGET_UNIX) && !defined(FEATURE_REDHAWK) OBJECTREF* GcInfoDecoder::GetCapturedRegister( int regNum, PREGDISPLAY pRD @@ -1524,7 +1524,7 @@ OBJECTREF* GcInfoDecoder::GetCapturedRegister( return (OBJECTREF*)(pR0 + regNum); } -#endif // TARGET_UNIX +#endif // TARGET_UNIX && !FEATURE_REDHAWK bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) @@ -1598,6 +1598,11 @@ OBJECTREF* GcInfoDecoder::GetRegisterSlot( _ASSERTE(regNum >= 0 && regNum <= 30); _ASSERTE(regNum != 18); // TEB +#ifdef FEATURE_REDHAWK + PTR_UIntNative* ppReg = &pRD->pX0; + + return (OBJECTREF*)*(ppReg + regNum); +#else DWORD64 **ppReg; if(regNum <= 17) @@ -1617,6 +1622,7 @@ OBJECTREF* GcInfoDecoder::GetRegisterSlot( ppReg = &pRD->pCurrentContextPointers->X19; return (OBJECTREF*)*(ppReg + regNum-19); +#endif } bool GcInfoDecoder::IsScratchRegister(int regNum, PREGDISPLAY pRD) @@ -1658,7 +1664,7 @@ void GcInfoDecoder::ReportRegisterToGC( // ARM64 LOG((LF_GCROOTS, LL_INFO1000, "Reporting " FMT_REG, regNum )); OBJECTREF* pObjRef = GetRegisterSlot( regNum, pRD ); -#if defined(TARGET_UNIX) && !defined(SOS_TARGET_ARM64) +#if defined(TARGET_UNIX) && !defined(FEATURE_REDHAWK) && !defined(SOS_TARGET_AMD64) // On PAL, we don't always have the context pointers available due to // a limitation of an unwinding library. In such case, the context // pointers for some nonvolatile registers are NULL. @@ -1700,7 +1706,7 @@ void GcInfoDecoder::ReportRegisterToGC( // ARM64 pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(regNum, 0, false))); } -#ifdef TARGET_UNIX +#if defined(TARGET_UNIX) && !defined(FEATURE_REDHAWK) OBJECTREF* GcInfoDecoder::GetCapturedRegister( int regNum, PREGDISPLAY pRD @@ -1725,7 +1731,7 @@ OBJECTREF* GcInfoDecoder::GetCapturedRegister( return (OBJECTREF*)(pX0 + regNum); } -#endif // TARGET_UNIX +#endif // TARGET_UNIX && !FEATURE_REDHAWK #else // Unknown platform diff --git a/src/coreclr/vm/gcstress.h b/src/coreclr/vm/gcstress.h index 30c3ca7062d30..7980a436fd008 100644 --- a/src/coreclr/vm/gcstress.h +++ b/src/coreclr/vm/gcstress.h @@ -464,7 +464,7 @@ namespace _GCStress GcStressBase::MaybeTrigger(acontext); #ifdef _DEBUG - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread) { pThread->EnableStressHeap(); diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 3b67c67e49469..2d2a3965d9db1 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -1667,6 +1667,16 @@ BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNo SigTypeContext typeContext(this,thParent); InstantiationContext instContext(&typeContext, NULL); + bool typicalInstMatchesMethodInst = true; + for (DWORD i = 0; i < methodInst.GetNumArgs(); i++) + { + if (typicalInst[i] != methodInst[i]) + { + typicalInstMatchesMethodInst = false; + break; + } + } + for (DWORD i = 0; i < methodInst.GetNumArgs(); i++) { TypeHandle thArg = methodInst[i]; @@ -1679,7 +1689,8 @@ BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNo tyvar->LoadConstraints(); //TODO: is this necessary for anything but the typical method? // Pass in the InstatiationContext so contraints can be correctly evaluated - if (!tyvar->SatisfiesConstraints(&typeContext,thArg, &instContext)) + // if this is an instantiation where the type variable is in its open position + if (!tyvar->SatisfiesConstraints(&typeContext,thArg, typicalInstMatchesMethodInst ? &instContext : NULL)) { if (fThrowIfNotSatisfied) { diff --git a/src/coreclr/vm/hash.cpp b/src/coreclr/vm/hash.cpp index a6708d2188ec4..1b0c697406d02 100644 --- a/src/coreclr/vm/hash.cpp +++ b/src/coreclr/vm/hash.cpp @@ -101,7 +101,7 @@ PTR_Bucket HashMap::Buckets() LIMITED_METHOD_DAC_CONTRACT; #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) - _ASSERTE (!g_fEEStarted || !m_fAsyncMode || GetThread() == NULL || GetThread()->PreemptiveGCDisabled() || IsGCThread()); + _ASSERTE (!g_fEEStarted || !m_fAsyncMode || GetThreadNULLOk() == NULL || GetThread()->PreemptiveGCDisabled() || IsGCThread()); #endif return m_rgBuckets + 1; } @@ -872,7 +872,7 @@ void HashMap::Rehash() GCX_MAYBE_COOP_NO_THREAD_BROKEN(m_fAsyncMode); #ifndef CROSSGEN_COMPILE - _ASSERTE (!g_fEEStarted || !m_fAsyncMode || GetThread() == NULL || GetThread()->PreemptiveGCDisabled()); + _ASSERTE (!g_fEEStarted || !m_fAsyncMode || GetThreadNULLOk() == NULL || GetThread()->PreemptiveGCDisabled()); _ASSERTE (OwnLock()); #endif diff --git a/src/coreclr/vm/hosting.cpp b/src/coreclr/vm/hosting.cpp index f8439265d7355..66896fe79268a 100644 --- a/src/coreclr/vm/hosting.cpp +++ b/src/coreclr/vm/hosting.cpp @@ -274,7 +274,7 @@ BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount) NOTHROW; GC_NOTRIGGER; MODE_ANY; - PRECONDITION(dwSleepMSec < 10000 || GetThread() == NULL || !GetThread()->PreemptiveGCDisabled()); + PRECONDITION(dwSleepMSec < 10000 || GetThreadNULLOk() == NULL || !GetThread()->PreemptiveGCDisabled()); } CONTRACTL_END; diff --git a/src/coreclr/vm/i386/cgenx86.cpp b/src/coreclr/vm/i386/cgenx86.cpp index caab476b15faf..c8a38e2f3b3fd 100644 --- a/src/coreclr/vm/i386/cgenx86.cpp +++ b/src/coreclr/vm/i386/cgenx86.cpp @@ -109,7 +109,7 @@ void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo) const DWORD cpuDefault = 0xFFFFFFFF; static ConfigDWORD cpuFamily; - DWORD configCpuFamily = cpuFamily.val_DontUse_(CLRConfig::INTERNAL_CPUFamily, cpuDefault); + DWORD configCpuFamily = cpuFamily.val(CLRConfig::INTERNAL_CPUFamily); if (configCpuFamily != cpuDefault) { assert((configCpuFamily & 0xFFF) == configCpuFamily); @@ -125,7 +125,7 @@ void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo) const DWORD cpuFeaturesDefault = 0xFFFFFFFF; static ConfigDWORD cpuFeatures; - DWORD configCpuFeatures = cpuFeatures.val_DontUse_(CLRConfig::INTERNAL_CPUFeatures, cpuFeaturesDefault); + DWORD configCpuFeatures = cpuFeatures.val(CLRConfig::INTERNAL_CPUFeatures); if (configCpuFeatures != cpuFeaturesDefault) { tempVal.dwFeatures = configCpuFeatures; diff --git a/src/coreclr/vm/i386/excepcpu.h b/src/coreclr/vm/i386/excepcpu.h index 4e84183030418..c4c500214ca69 100644 --- a/src/coreclr/vm/i386/excepcpu.h +++ b/src/coreclr/vm/i386/excepcpu.h @@ -23,11 +23,6 @@ #ifndef FEATURE_EH_FUNCLETS class Thread; -#if defined(_MSC_VER) -#pragma warning(disable:4733) // Inline asm assigning to `FS:0` : handler not registered as safe handler - // Actually, the handler getting set is properly registered -#endif - #define INSTALL_SEH_RECORD(record) \ { \ (record)->Next = (PEXCEPTION_REGISTRATION_RECORD)__readfsdword(0); \ diff --git a/src/coreclr/vm/i386/excepx86.cpp b/src/coreclr/vm/i386/excepx86.cpp index 75e42c2b34e86..3d2e0e4dddeca 100644 --- a/src/coreclr/vm/i386/excepx86.cpp +++ b/src/coreclr/vm/i386/excepx86.cpp @@ -172,7 +172,7 @@ Frame *GetCurrFrame(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame) pFrame = ((FrameHandlerExRecord *)pEstablisherFrame)->GetCurrFrame(); // Assert that the exception frame is on the thread or that the exception frame is the top frame. - _ASSERTE(GetThread() == NULL || GetThread()->GetFrame() == (Frame*)-1 || GetThread()->GetFrame() <= pFrame); + _ASSERTE(GetThreadNULLOk() == NULL || GetThread()->GetFrame() == (Frame*)-1 || GetThread()->GetFrame() <= pFrame); return pFrame; } @@ -280,7 +280,7 @@ void VerifyValidTransitionFromManagedCode(Thread *pThread, CrawlFrame *pCF) _ASSERTE(ExecutionManager::IsManagedCode(GetControlPC(pCF->GetRegisterSet()))); // Cannot get to the TEB of other threads. So ignore them. - if (pThread != GetThread()) + if (pThread != GetThreadNULLOk()) { return; } @@ -1864,7 +1864,6 @@ LPVOID STDCALL COMPlusEndCatch(LPVOID ebp, DWORD ebx, DWORD edi, DWORD esi, LPVO // Set up m_OSContext for the call to COMPlusCheckForAbort // Thread* pThread = GetThread(); - _ASSERTE(pThread != NULL); SetIP(pThread->m_OSContext, (PCODE)*pRetAddress); SetSP(pThread->m_OSContext, (TADDR)esp); @@ -3304,7 +3303,6 @@ EXCEPTION_HANDLER_IMPL(COMPlusNestedExceptionHandler) // the unwind. Thread* pThread = GetThread(); - _ASSERTE(pThread); ExInfo* pExInfo = &(pThread->GetExceptionState()->m_currentExInfo); ExInfo* pPrevNestedInfo = pExInfo->m_pPrevNestedInfo; @@ -3424,7 +3422,6 @@ EXCEPTION_HANDLER_IMPL(UMThunkPrestubHandler) GCX_COOP(); // Must be cooperative to modify frame chain. Thread *pThread = GetThread(); - _ASSERTE(pThread); Frame *pFrame = pThread->GetFrame(); pFrame->ExceptionUnwind(); pFrame->Pop(pThread); @@ -3503,7 +3500,7 @@ AdjustContextForVirtualStub( { LIMITED_METHOD_CONTRACT; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); // We may not have a managed thread object. Example is an AV on the helper thread. // (perhaps during StubManager::IsStub) diff --git a/src/coreclr/vm/i386/jitinterfacex86.cpp b/src/coreclr/vm/i386/jitinterfacex86.cpp index 4cba1165bff34..cefe7ecadc5e9 100644 --- a/src/coreclr/vm/i386/jitinterfacex86.cpp +++ b/src/coreclr/vm/i386/jitinterfacex86.cpp @@ -1226,7 +1226,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {GC_NOTRIGGER;} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {GC_NOTRIGGER;} } CONTRACTL_END; int stompWBCompleteActions = SWB_PASS; @@ -1253,7 +1253,7 @@ int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) // Check if we need to use the upper bounds checking barrier stub. if (bReqUpperBoundsCheck) { - GCX_MAYBE_COOP_NO_THREAD_BROKEN((GetThread()!=NULL)); + GCX_MAYBE_COOP_NO_THREAD_BROKEN((GetThreadNULLOk()!=NULL)); if( !isRuntimeSuspended && !(stompWBCompleteActions & SWB_EE_RESTART) ) { ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_GC_PREP); stompWBCompleteActions |= SWB_EE_RESTART; diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index e6753a67a5014..8817bf1cc4fe4 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -2412,7 +2412,7 @@ VOID StubLinkerCPU::X86EmitCurrentThreadFetch(X86Reg dstreg, unsigned preservedR X86EmitPushRegs(preservedRegSet & ((1 << kEAX) | (1 << kEDX) | (1 << kECX))); // call GetThread - X86EmitCall(NewExternalCodeLabel((LPVOID)GetThread), sizeof(void*)); + X86EmitCall(NewExternalCodeLabel((LPVOID)GetThreadHelper), sizeof(void*)); // mov dstreg, eax X86EmitMovRegReg(dstreg, kEAX); diff --git a/src/coreclr/vm/ibclogger.cpp b/src/coreclr/vm/ibclogger.cpp index eded1487a20de..e564c9a8eb770 100644 --- a/src/coreclr/vm/ibclogger.cpp +++ b/src/coreclr/vm/ibclogger.cpp @@ -146,7 +146,7 @@ void IBCLogger::LogAccessThreadSafeHelper(const void * p, pfnIBCAccessCallback c if (p == NULL) return; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); /* This could be called by the concurrent GC thread*/ /* where GetThread() returns NULL. In such cases,*/ diff --git a/src/coreclr/vm/ilmarshalers.cpp b/src/coreclr/vm/ilmarshalers.cpp index 564cc8f10a95e..3b45dc673c233 100644 --- a/src/coreclr/vm/ilmarshalers.cpp +++ b/src/coreclr/vm/ilmarshalers.cpp @@ -2146,14 +2146,31 @@ void ILLayoutClassPtrMarshalerBase::EmitConvertSpaceCLRToNative(ILCodeStream* ps EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + + ILCodeLabel* pTypeMismatchedLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, pTypeMismatchedLabel); + DWORD sizeLocal = pslILEmit->NewLocal(LocalDesc(ELEMENT_TYPE_I4)); + pslILEmit->EmitLDC(uNativeSize); + if (emittedTypeCheck) + { + ILCodeLabel* pHaveSizeLabel = pslILEmit->NewCodeLabel(); + pslILEmit->EmitBR(pHaveSizeLabel); + pslILEmit->EmitLabel(pTypeMismatchedLabel); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitCALL(METHOD__MARSHAL__SIZEOF_TYPE, 1, 1); + pslILEmit->EmitLabel(pHaveSizeLabel); + } + pslILEmit->EmitSTLOC(sizeLocal); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitCALL(METHOD__MARSHAL__ALLOC_CO_TASK_MEM, 1, 1); pslILEmit->EmitDUP(); // for INITBLK EmitStoreNativeValue(pslILEmit); // initialize local block we just allocated pslILEmit->EmitLDC(0); - pslILEmit->EmitLDC(uNativeSize); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitINITBLK(); pslILEmit->EmitLabel(pNullRefLabel); @@ -2178,14 +2195,31 @@ void ILLayoutClassPtrMarshalerBase::EmitConvertSpaceCLRToNativeTemp(ILCodeStream EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* pTypeMismatchedLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, pTypeMismatchedLabel); + DWORD sizeLocal = pslILEmit->NewLocal(LocalDesc(ELEMENT_TYPE_I4)); + pslILEmit->EmitLDC(uNativeSize); + if (emittedTypeCheck) + { + ILCodeLabel* pHaveSizeLabel = pslILEmit->NewCodeLabel(); + pslILEmit->EmitBR(pHaveSizeLabel); + pslILEmit->EmitLabel(pTypeMismatchedLabel); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitCALL(METHOD__MARSHAL__SIZEOF_TYPE, 1, 1); + pslILEmit->EmitLabel(pHaveSizeLabel); + } + pslILEmit->EmitSTLOC(sizeLocal); + pslILEmit->EmitLDLOC(sizeLocal); + pslILEmit->EmitLOCALLOC(); pslILEmit->EmitDUP(); // for INITBLK EmitStoreNativeValue(pslILEmit); // initialize local block we just allocated pslILEmit->EmitLDC(0); - pslILEmit->EmitLDC(uNativeSize); + pslILEmit->EmitLDLOC(sizeLocal); pslILEmit->EmitINITBLK(); pslILEmit->EmitLabel(pNullRefLabel); @@ -2261,7 +2295,24 @@ void ILLayoutClassPtrMarshalerBase::EmitClearNativeTemp(ILCodeStream* pslILEmit) } } +bool ILLayoutClassPtrMarshalerBase::EmitExactTypeCheck(ILCodeStream* pslILEmit, ILCodeLabel* isNotMatchingTypeLabel) +{ + STANDARD_VM_CONTRACT; + if (m_pargs->m_pMT->IsSealed()) + { + // If the provided type cannot be derived from, then we don't need to emit the type check. + return false; + } + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitLDTOKEN(pslILEmit->GetToken(m_pargs->m_pMT)); + pslILEmit->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + pslILEmit->EmitCALLVIRT(pslILEmit->GetToken(CoreLibBinder::GetMethod(METHOD__OBJECT__EQUALS)), 1, 1); + pslILEmit->EmitBRFALSE(isNotMatchingTypeLabel); + + return true; +} void ILLayoutClassPtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslILEmit) { @@ -2278,6 +2329,9 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* psl pslILEmit->EmitLDC(uNativeSize); pslILEmit->EmitINITBLK(); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + MethodDesc* pStructMarshalStub = NDirect::CreateStructMarshalILStub(m_pargs->m_pMT); EmitLoadManagedValue(pslILEmit); @@ -2287,6 +2341,18 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* psl EmitLoadCleanupWorkList(pslILEmit); pslILEmit->EmitCALL(pslILEmit->GetToken(pStructMarshalStub), 4, 0); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadManagedValue(pslILEmit); + EmitLoadNativeValue(pslILEmit); + pslILEmit->EmitLDC(0); + pslILEmit->EmitCALL(METHOD__MARSHAL__STRUCTURE_TO_PTR, 3, 0); + } + pslILEmit->EmitLabel(pNullRefLabel); } @@ -2299,6 +2365,9 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* psl EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + MethodDesc* pStructMarshalStub = NDirect::CreateStructMarshalILStub(m_pargs->m_pMT); EmitLoadManagedValue(pslILEmit); @@ -2308,6 +2377,15 @@ void ILLayoutClassPtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* psl EmitLoadCleanupWorkList(pslILEmit); pslILEmit->EmitCALL(pslILEmit->GetToken(pStructMarshalStub), 4, 0); + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__MARSHAL__PTR_TO_STRUCTURE, 2, 0); + } pslILEmit->EmitLabel(pNullRefLabel); } @@ -2315,6 +2393,10 @@ void ILLayoutClassPtrMarshaler::EmitClearNativeContents(ILCodeStream * pslILEmit { STANDARD_VM_CONTRACT; + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + ILCodeLabel* cleanedUpLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + MethodDesc* pStructMarshalStub = NDirect::CreateStructMarshalILStub(m_pargs->m_pMT); EmitLoadManagedValue(pslILEmit); @@ -2324,6 +2406,19 @@ void ILLayoutClassPtrMarshaler::EmitClearNativeContents(ILCodeStream * pslILEmit EmitLoadCleanupWorkList(pslILEmit); pslILEmit->EmitCALL(pslILEmit->GetToken(pStructMarshalStub), 4, 0); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(cleanedUpLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__OBJECT__GET_TYPE, 1, 1); + pslILEmit->EmitCALL(METHOD__MARSHAL__DESTROY_STRUCTURE, 2, 0); + } + + pslILEmit->EmitLabel(cleanedUpLabel); } @@ -2338,6 +2433,9 @@ void ILBlittablePtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslIL EmitLoadNativeValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); // dest EmitLoadManagedValue(pslILEmit); @@ -2346,6 +2444,17 @@ void ILBlittablePtrMarshaler::EmitConvertContentsCLRToNative(ILCodeStream* pslIL pslILEmit->EmitLDC(uNativeSize); // size pslILEmit->EmitCPBLK(); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadManagedValue(pslILEmit); + EmitLoadNativeValue(pslILEmit); + pslILEmit->EmitLDC(0); + pslILEmit->EmitCALL(METHOD__MARSHAL__STRUCTURE_TO_PTR, 3, 0); + } pslILEmit->EmitLabel(pNullRefLabel); } @@ -2360,6 +2469,9 @@ void ILBlittablePtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslIL EmitLoadManagedValue(pslILEmit); pslILEmit->EmitBRFALSE(pNullRefLabel); + ILCodeLabel* isNotMatchingTypeLabel = pslILEmit->NewCodeLabel(); + bool emittedTypeCheck = EmitExactTypeCheck(pslILEmit, isNotMatchingTypeLabel); + EmitLoadManagedValue(pslILEmit); pslILEmit->EmitLDFLDA(fieldDef); // dest @@ -2368,12 +2480,26 @@ void ILBlittablePtrMarshaler::EmitConvertContentsNativeToCLR(ILCodeStream* pslIL pslILEmit->EmitLDC(uNativeSize); // size pslILEmit->EmitCPBLK(); + + if (emittedTypeCheck) + { + pslILEmit->EmitBR(pNullRefLabel); + + pslILEmit->EmitLabel(isNotMatchingTypeLabel); + EmitLoadNativeValue(pslILEmit); + EmitLoadManagedValue(pslILEmit); + pslILEmit->EmitCALL(METHOD__MARSHAL__PTR_TO_STRUCTURE, 2, 0); + } + pslILEmit->EmitLabel(pNullRefLabel); } bool ILBlittablePtrMarshaler::CanMarshalViaPinning() { - return IsCLRToNative(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags) && !IsFieldMarshal(m_dwMarshalFlags); + return IsCLRToNative(m_dwMarshalFlags) && + !IsByref(m_dwMarshalFlags) && + !IsFieldMarshal(m_dwMarshalFlags) && + m_pargs->m_pMT->IsSealed(); // We can't marshal via pinning if we might need to marshal differently at runtime. See calls to EmitExactTypeCheck where we check the runtime type of the object being marshalled. } void ILBlittablePtrMarshaler::EmitMarshalViaPinning(ILCodeStream* pslILEmit) diff --git a/src/coreclr/vm/ilmarshalers.h b/src/coreclr/vm/ilmarshalers.h index 12a1824ca7ba3..ba07cd5b55c17 100644 --- a/src/coreclr/vm/ilmarshalers.h +++ b/src/coreclr/vm/ilmarshalers.h @@ -2791,6 +2791,7 @@ class ILLayoutClassPtrMarshalerBase : public ILMarshaler bool NeedsClearNative() override; void EmitClearNative(ILCodeStream* pslILEmit) override; void EmitClearNativeTemp(ILCodeStream* pslILEmit) override; + bool EmitExactTypeCheck(ILCodeStream* pslILEmit, ILCodeLabel* isNotMatchingTypeLabel); }; class ILLayoutClassPtrMarshaler : public ILLayoutClassPtrMarshalerBase diff --git a/src/coreclr/vm/interoputil.cpp b/src/coreclr/vm/interoputil.cpp index 90f11497866fd..7389189118809 100644 --- a/src/coreclr/vm/interoputil.cpp +++ b/src/coreclr/vm/interoputil.cpp @@ -210,7 +210,7 @@ void FillExceptionData( if (pErrInfo != NULL) { - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread != NULL) { GCX_PREEMP(); @@ -1471,7 +1471,7 @@ VOID EnsureComStarted(BOOL fCoInitCurrentThread) THROWS; GC_TRIGGERS; MODE_ANY; - PRECONDITION(GetThread() || !fCoInitCurrentThread); + PRECONDITION(GetThreadNULLOk() || !fCoInitCurrentThread); PRECONDITION(g_fEEStarted); } CONTRACTL_END; @@ -1502,7 +1502,7 @@ HRESULT EnsureComStartedNoThrow(BOOL fCoInitCurrentThread) GC_TRIGGERS; MODE_ANY; PRECONDITION(g_fEEStarted); - PRECONDITION(GetThread() != NULL); // Should always be inside BEGIN_EXTERNAL_ENTRYPOINT + PRECONDITION(GetThreadNULLOk() != NULL); // Should always be inside BEGIN_EXTERNAL_ENTRYPOINT } CONTRACTL_END; @@ -3890,13 +3890,12 @@ void InitializeComInterop() //------------------------------------------------------------------- static int g_TraceCount = 0; -static IUnknown* g_pTraceIUnknown = 0; +static IUnknown* g_pTraceIUnknown = NULL; VOID IntializeInteropLogging() { WRAPPER_NO_CONTRACT; - g_pTraceIUnknown = g_pConfig->GetTraceIUnknown(); g_TraceCount = g_pConfig->GetTraceWrapper(); } diff --git a/src/coreclr/vm/interpreter.cpp b/src/coreclr/vm/interpreter.cpp index 325aaf4590b9e..17c27e6c04528 100644 --- a/src/coreclr/vm/interpreter.cpp +++ b/src/coreclr/vm/interpreter.cpp @@ -3313,8 +3313,6 @@ bool Interpreter::MethodHandlesException(OBJECTREF orThrowable) if (orThrowable != NULL) { - PTR_Thread pCurThread = GetThread(); - // Don't catch ThreadAbort and other uncatchable exceptions if (!IsUncatchable(&orThrowable)) { diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index b2d54172fd645..9dbdeabb8e7ee 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -3946,7 +3946,6 @@ HCIMPL_MONHELPER(JIT_MonEnterStatic_Portable, AwareLock *lock) MONHELPER_STATE(_ASSERTE(pbLockTaken != NULL && *pbLockTaken == 0)); Thread *pCurThread = GetThread(); - if (pCurThread->CatchAtSafePointOpportunistic()) { goto FramedLockHelper; diff --git a/src/coreclr/vm/jithost.cpp b/src/coreclr/vm/jithost.cpp index d9b73512a3ca5..504e389faa62a 100644 --- a/src/coreclr/vm/jithost.cpp +++ b/src/coreclr/vm/jithost.cpp @@ -26,7 +26,7 @@ int JitHost::getIntConfigValue(const WCHAR* name, int defaultValue) WRAPPER_NO_CONTRACT; // Translate JIT call into runtime configuration query - CLRConfig::ConfigDWORDInfo info{ name, (DWORD)defaultValue, CLRConfig::EEConfig_default }; + CLRConfig::ConfigDWORDInfo info{ name, (DWORD)defaultValue, CLRConfig::LookupOptions::Default }; // Perform a CLRConfig look up on behalf of the JIT. return CLRConfig::GetConfigValue(info); @@ -37,7 +37,7 @@ const WCHAR* JitHost::getStringConfigValue(const WCHAR* name) WRAPPER_NO_CONTRACT; // Translate JIT call into runtime configuration query - CLRConfig::ConfigStringInfo info{ name, CLRConfig::EEConfig_default }; + CLRConfig::ConfigStringInfo info{ name, CLRConfig::LookupOptions::Default }; // Perform a CLRConfig look up on behalf of the JIT. return CLRConfig::GetConfigValue(info); @@ -63,7 +63,7 @@ void* JitHost::allocateSlab(size_t size, size_t* pActualSize) { size = max(size, sizeof(Slab)); - Thread* pCurrentThread = GetThread(); + Thread* pCurrentThread = GetThreadNULLOk(); if (m_pCurrentCachedList != NULL || m_pPreviousCachedList != NULL) { CrstHolder lock(&m_jitSlabAllocatorCrst); @@ -124,7 +124,7 @@ void JitHost::freeSlab(void* slab, size_t actualSize) Slab* pSlab = (Slab*)slab; pSlab->size = actualSize; - pSlab->affinity = GetThread(); + pSlab->affinity = GetThreadNULLOk(); pSlab->pNext = m_pCurrentCachedList; m_pCurrentCachedList = pSlab; return; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4c1ba0afb5c10..2423c4887d61e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -76,7 +76,6 @@ // #define JIT_TO_EE_TRANSITION() MAKE_CURRENT_THREAD_AVAILABLE_EX(m_pThread); \ - _ASSERTE(CURRENT_THREAD == GetThread()); \ INSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE; \ COOPERATIVE_TRANSITION_BEGIN(); \ @@ -12910,6 +12909,10 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO flags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO); } +#ifdef ARM_SOFTFP + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SOFTFP_ABI); +#endif // ARM_SOFTFP + #ifdef FEATURE_PGO // Instrument, if @@ -14104,6 +14107,14 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, actualBaseOffset = ReadyToRunInfo::GetFieldBaseOffset(pEnclosingMT); } + if (baseOffset == 0) + { + // Relative verification of just the field offset when the base class + // is outside of the current R2R version bubble + actualFieldOffset -= actualBaseOffset; + actualBaseOffset = 0; + } + if ((fieldOffset != actualFieldOffset) || (baseOffset != actualBaseOffset)) { // Verification failures are failfast events diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index e98cd19ca8100..719bda03a420e 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -505,7 +505,7 @@ class CEEInfo : public ICorJitInfo m_pOverride(NULL), m_pMethodBeingCompiled(fd), m_fVerifyOnly(fVerifyOnly), - m_pThread(GetThread()), + m_pThread(GetThreadNULLOk()), m_hMethodForSecurity_Key(NULL), m_pMethodForSecurity_Value(NULL), #if defined(FEATURE_GDBJIT) diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index 84948f65242cd..a8f5b28aca14d 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -592,6 +592,10 @@ DEFINE_METASIG_T(SM(Array_Int_Array_Int_Int_RetVoid, C(ARRAY) i C(ARRAY) i i, v) DEFINE_METASIG_T(SM(Array_Int_Obj_RetVoid, C(ARRAY) i j, v)) DEFINE_METASIG_T(SM(Array_Int_PtrVoid_RetRefObj, C(ARRAY) i P(v), r(j))) +DEFINE_METASIG(SM(Obj_IntPtr_Bool_RetVoid, j I F, v)) +DEFINE_METASIG(SM(IntPtr_Obj_RetVoid, I j, v)) +DEFINE_METASIG_T(SM(IntPtr_Type_RetVoid, I C(TYPE), v)) + // Undefine macros in case we include the file again in the compilation unit #undef DEFINE_METASIG diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index ac097cdec2fb8..a6f1048333e1a 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3186,9 +3186,7 @@ void MethodTable::DoRunClassInitThrowing() // policy, keep this unless it proves intractable to remove all premature classinits in the system. EnsureActive(); - Thread *pThread; - pThread = GetThread(); - _ASSERTE(pThread); + Thread* pThread = GetThread(); AppDomain *pDomain = GetAppDomain(); @@ -3633,7 +3631,7 @@ void CallFinalizerOnThreadObject(Object *obj) // finalization. if ((g_fEEShutDown & ShutDown_Finalize2) == 0) { - if (GetThread() != thread) + if (GetThreadNULLOk() != thread) { refThis->ClearInternal(); } @@ -3821,7 +3819,7 @@ void MethodTable::GetSavedExtent(TADDR *pStart, TADDR *pEnd) TADDR start; - if (ContainsPointersOrCollectible()) + if (ContainsPointers()) start = dac_cast(this) - CGCDesc::GetCGCDescFromMT(this)->GetSize(); else start = dac_cast(this); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index afae54246186b..f14d3e2c0202c 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -8743,19 +8743,6 @@ MethodTableBuilder::HandleGCForExplicitLayout() MethodTable *pMT = GetHalfBakedMethodTable(); -#ifdef FEATURE_COLLECTIBLE_TYPES - if (bmtFP->NumGCPointerSeries == 0 && pMT->Collectible()) - { - // For collectible types, insert empty gc series - CGCDescSeries *pSeries; - - CGCDesc::Init( (PVOID) pMT, 1); - pSeries = ((CGCDesc*)pMT)->GetLowestSeries(); - pSeries->SetSeriesSize( (size_t) (0) - (size_t) pMT->GetBaseSize()); - pSeries->SetSeriesOffset(OBJECT_SIZE); - } - else -#endif // FEATURE_COLLECTIBLE_TYPES if (bmtFP->NumGCPointerSeries != 0) { pMT->SetContainsPointers(); @@ -9024,7 +9011,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT) pMT->GetAssembly()->ThrowTypeLoadException(pMT->GetMDImport(), pMT->GetCl(), IDS_CLASSLOAD_BADFORMAT); } #ifdef _DEBUG - duplicates |= EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_AlwaysUseMetadataInterfaceMapLayout, FALSE); + duplicates |= CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AlwaysUseMetadataInterfaceMapLayout); //#InjectInterfaceDuplicates_LoadExactInterfaceMap // If we are injecting duplicates also for non-generic interfaces in check builds, we have to use @@ -10087,10 +10074,6 @@ MethodTableBuilder::SetupMethodTable2( cbDictAllocSize = DictionaryLayout::GetDictionarySizeFromLayout(bmtGenerics->GetNumGenericArgs(), pClass->GetDictionaryLayout(), &cbDictSlotSize); } -#ifdef FEATURE_COLLECTIBLE_TYPES - BOOL fCollectible = pLoaderModule->IsCollectible(); -#endif // FEATURE_COLLECTIBLE_TYPES - DWORD dwGCSize; if (bmtFP->NumGCPointerSeries > 0) @@ -10099,12 +10082,7 @@ MethodTableBuilder::SetupMethodTable2( } else { -#ifdef FEATURE_COLLECTIBLE_TYPES - if (fCollectible) - dwGCSize = (DWORD)CGCDesc::ComputeSize(1); - else -#endif // FEATURE_COLLECTIBLE_TYPES - dwGCSize = 0; + dwGCSize = 0; } pClass->SetNumMethods(bmtVT->cTotalSlots); @@ -11200,19 +11178,6 @@ VOID MethodTableBuilder::HandleGCForValueClasses(MethodTable ** pByValueClassCac // Note that for value classes, the following calculation is only appropriate // when the instance is in its "boxed" state. -#ifdef FEATURE_COLLECTIBLE_TYPES - if (bmtFP->NumGCPointerSeries == 0 && pMT->Collectible()) - { - // For collectible types, insert empty gc series - CGCDescSeries *pSeries; - - CGCDesc::Init( (PVOID) pMT, 1); - pSeries = ((CGCDesc*)pMT)->GetLowestSeries(); - pSeries->SetSeriesSize( (size_t) (0) - (size_t) pMT->GetBaseSize()); - pSeries->SetSeriesOffset(OBJECT_SIZE); - } - else -#endif // FEATURE_COLLECTIBLE_TYPES if (bmtFP->NumGCPointerSeries != 0) { CGCDescSeries *pSeries; @@ -11627,7 +11592,7 @@ MethodTableBuilder::GatherGenericsInfo( CONTRACTL { STANDARD_VM_CHECK; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(CheckPointer(pModule)); PRECONDITION(CheckPointer(bmtGenericsInfo)); } @@ -11875,7 +11840,7 @@ ClassLoader::CreateTypeHandleForTypeDefThrowing( CONTRACT(TypeHandle) { STANDARD_VM_CHECK; - PRECONDITION(GetThread() != NULL); + PRECONDITION(GetThreadNULLOk() != NULL); PRECONDITION(CheckPointer(pModule)); POSTCONDITION(!RETVAL.IsNull()); POSTCONDITION(CheckPointer(RETVAL.GetMethodTable())); diff --git a/src/coreclr/vm/mlinfo.cpp b/src/coreclr/vm/mlinfo.cpp index 71788db9efd5d..cc4bcdc710bd4 100644 --- a/src/coreclr/vm/mlinfo.cpp +++ b/src/coreclr/vm/mlinfo.cpp @@ -1133,6 +1133,8 @@ MarshalInfo::MarshalInfo(Module* pModule, CorElementType corElemType = ELEMENT_TYPE_END; m_pMT = NULL; m_pMD = pMD; + // [Compat] For backward compatibility reasons, some marshalers imply [In, Out] behavior when marked as [In], [Out], or not marked with either. + BOOL byValAlwaysInOut = FALSE; #ifdef FEATURE_COMINTEROP m_fDispItf = FALSE; @@ -1877,6 +1879,7 @@ MarshalInfo::MarshalInfo(Module* pModule, } m_type = IsFieldScenario() ? MARSHAL_TYPE_BLITTABLE_LAYOUTCLASS : MARSHAL_TYPE_BLITTABLEPTR; m_args.m_pMT = m_pMT; + byValAlwaysInOut = TRUE; } else if (m_pMT->HasLayout()) { @@ -2369,10 +2372,16 @@ MarshalInfo::MarshalInfo(Module* pModule, } } - // If neither IN nor OUT are true, this signals the URT to use the default - // rules. - if (!m_in && !m_out) + if (!m_byref && byValAlwaysInOut) { + // Some marshalers expect [In, Out] behavior with [In], [Out], or no directional attributes. + m_in = TRUE; + m_out = TRUE; + } + else if (!m_in && !m_out) + { + // If neither IN nor OUT are true, this signals the URT to use the default + // rules. if (m_byref || (mtype == ELEMENT_TYPE_CLASS && !(sig.IsStringType(pModule, pTypeContext)) @@ -2386,7 +2395,6 @@ MarshalInfo::MarshalInfo(Module* pModule, m_in = TRUE; m_out = FALSE; } - } } diff --git a/src/coreclr/vm/nativeimage.cpp b/src/coreclr/vm/nativeimage.cpp index f09517b341ba6..9067273ed2ec2 100644 --- a/src/coreclr/vm/nativeimage.cpp +++ b/src/coreclr/vm/nativeimage.cpp @@ -67,6 +67,7 @@ void NativeImage::Initialize(READYTORUN_HEADER *pHeader, LoaderAllocator *pLoade m_pReadyToRunInfo = new ReadyToRunInfo(/*pModule*/ NULL, pLoaderAllocator, m_pImageLayout, pHeader, /*compositeImage*/ NULL, pamTracker); m_pComponentAssemblies = m_pReadyToRunInfo->FindSection(ReadyToRunSectionType::ComponentAssemblies); + m_pComponentAssemblyMvids = m_pReadyToRunInfo->FindSection(ReadyToRunSectionType::ManifestAssemblyMvids); m_componentAssemblyCount = m_pComponentAssemblies->Size / sizeof(READYTORUN_COMPONENT_ASSEMBLIES_ENTRY); // Check if the current module's image has native manifest metadata, otherwise the current->GetNativeAssemblyImport() asserts. @@ -251,6 +252,49 @@ PTR_READYTORUN_CORE_HEADER NativeImage::GetComponentAssemblyHeader(LPCUTF8 simpl } #endif +#ifndef DACCESS_COMPILE +void NativeImage::CheckAssemblyMvid(Assembly *assembly) const +{ + STANDARD_VM_CONTRACT; + if (m_pComponentAssemblyMvids == NULL) + { + return; + } + + const AssemblyNameIndex *assemblyNameIndex = m_assemblySimpleNameToIndexMap.LookupPtr(assembly->GetSimpleName()); + if (assemblyNameIndex == NULL) + { + return; + } + + GUID assemblyMvid; + assembly->GetManifestImport()->GetScopeProps(NULL, &assemblyMvid); + + const byte *pImageBase = (const BYTE *)m_pImageLayout->GetBase(); + const GUID *componentMvid = (const GUID *)&pImageBase[m_pComponentAssemblyMvids->VirtualAddress] + assemblyNameIndex->Index; + if (IsEqualGUID(*componentMvid, assemblyMvid)) + { + return; + } + + static const size_t MVID_TEXT_LENGTH = 39; + WCHAR assemblyMvidText[MVID_TEXT_LENGTH]; + StringFromGUID2(assemblyMvid, assemblyMvidText, MVID_TEXT_LENGTH); + + WCHAR componentMvidText[MVID_TEXT_LENGTH]; + StringFromGUID2(*componentMvid, componentMvidText, MVID_TEXT_LENGTH); + + SString message; + message.Printf(W("MVID mismatch between loaded assembly '%s' (MVID = %s) and an assembly with the same simple name embedded in the native image '%s' (MVID = %s)"), + SString(SString::Utf8, assembly->GetSimpleName()).GetUnicode(), + assemblyMvidText, + SString(SString::Utf8, GetFileName()).GetUnicode(), + componentMvidText); + + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_FAILFAST, message.GetUnicode()); +} +#endif + #ifndef DACCESS_COMPILE IMDInternalImport *NativeImage::LoadManifestMetadata() { diff --git a/src/coreclr/vm/nativeimage.h b/src/coreclr/vm/nativeimage.h index 641f026391a1b..8ce2c43fe7fd1 100644 --- a/src/coreclr/vm/nativeimage.h +++ b/src/coreclr/vm/nativeimage.h @@ -84,6 +84,7 @@ class NativeImage PTR_Assembly *m_pNativeMetadataAssemblyRefMap; IMAGE_DATA_DIRECTORY *m_pComponentAssemblies; + IMAGE_DATA_DIRECTORY *m_pComponentAssemblyMvids; uint32_t m_componentAssemblyCount; uint32_t m_manifestAssemblyCount; SHash m_assemblySimpleNameToIndexMap; @@ -121,7 +122,9 @@ class NativeImage Assembly *LoadManifestAssembly(uint32_t rowid, DomainAssembly *pParentAssembly); PTR_READYTORUN_CORE_HEADER GetComponentAssemblyHeader(LPCUTF8 assemblySimpleName); - + + void CheckAssemblyMvid(Assembly *assembly) const; + private: IMDInternalImport *LoadManifestMetadata(); }; diff --git a/src/coreclr/vm/nativeoverlapped.cpp b/src/coreclr/vm/nativeoverlapped.cpp index 46a8600d3ddfd..9440f8d21c298 100644 --- a/src/coreclr/vm/nativeoverlapped.cpp +++ b/src/coreclr/vm/nativeoverlapped.cpp @@ -33,8 +33,6 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, Thread *pThread = GetThread(); size_t key=0; - _ASSERTE(pThread); - //Poll and wait if GC is in progress, to avoid blocking GC for too long. FC_GC_POLL(); diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index fad929a331d6f..36b100d6e9125 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -464,8 +464,7 @@ VOID Object::Validate(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock) #ifdef _DEBUG { - BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread != NULL && !(pThread->PreemptiveGCDisabled())) { @@ -480,7 +479,6 @@ VOID Object::Validate(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock) if (!dbgOnly_IsSpecialEEThread() && !IsGCSpecialThread()) _ASSERTE(!"OBJECTREF being accessed while thread is in preemptive GC mode."); } - END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; } #endif @@ -1303,7 +1301,7 @@ void* __cdecl GCSafeMemCpy(void * dest, const void * src, size_t len) if (!(((*(BYTE**)&dest) < g_lowest_address ) || ((*(BYTE**)&dest) >= g_highest_address))) { - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); // GCHeapUtilities::IsHeapPointer has race when called in preemptive mode. It walks the list of segments // that can be modified by GC. Do the check below only if it is safe to do so. @@ -1388,7 +1386,7 @@ void StackTraceArray::CheckState() const if (!m_array) return; - assert(GetObjectThread() == GetThread()); + assert(GetObjectThread() == GetThreadNULLOk()); size_t size = Size(); StackTraceElement const * p; @@ -1443,7 +1441,7 @@ void StackTraceArray::EnsureThreadAffinity() if (!m_array) return; - if (GetObjectThread() != GetThread()) + if (GetObjectThread() != GetThreadNULLOk()) { // object is being changed by a thread different from the one which created it // make a copy of the array to prevent a race condition when two different threads try to change it diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 375691aabbef6..01fa3287d3f70 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1921,7 +1921,7 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method ETWOnStartup(PrestubWorker_V1, PrestubWorkerEnd_V1); - MAKE_CURRENT_THREAD_AVAILABLE(); + MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThreadNULLOk()); // Attempt to check what GC mode we are running under. if (CURRENT_THREAD == NULL diff --git a/src/coreclr/vm/profilinghelper.inl b/src/coreclr/vm/profilinghelper.inl index 364f574bbd350..f642630760098 100644 --- a/src/coreclr/vm/profilinghelper.inl +++ b/src/coreclr/vm/profilinghelper.inl @@ -18,8 +18,7 @@ FORCEINLINE SetCallbackStateFlagsHolder::SetCallbackStateFlagsHolder(DWORD dwFla { // This is called before entering a profiler. We set the specified dwFlags on // the Thread object, and remember the previous flags for later. - BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; - m_pThread = GetThread(); + m_pThread = GetThreadNULLOk(); if (m_pThread != NULL) { m_dwOriginalFullState = m_pThread->SetProfilerCallbackStateFlags(dwFlags); @@ -28,7 +27,6 @@ FORCEINLINE SetCallbackStateFlagsHolder::SetCallbackStateFlagsHolder(DWORD dwFla { m_dwOriginalFullState = 0; } - END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; } FORCEINLINE SetCallbackStateFlagsHolder::~SetCallbackStateFlagsHolder() @@ -44,9 +42,7 @@ FORCEINLINE SetCallbackStateFlagsHolder::~SetCallbackStateFlagsHolder() // original flag set here. if (m_pThread != NULL) { - BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; m_pThread->SetProfilerCallbackFullState(m_dwOriginalFullState); - END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; } } diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.cpp b/src/coreclr/vm/proftoeeinterfaceimpl.cpp index ac4d9c7727b40..ccf2af5410122 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.cpp +++ b/src/coreclr/vm/proftoeeinterfaceimpl.cpp @@ -3000,7 +3000,7 @@ HRESULT ProfToEEInterfaceImpl::GetRVAStaticAddress(ClassID classId, return E_INVALIDARG; } - if (GetThread() == NULL) + if (GetThreadNULLOk() == NULL) { return CORPROF_E_NOT_MANAGED_THREAD; } @@ -3275,12 +3275,12 @@ HRESULT ProfToEEInterfaceImpl::GetThreadStaticAddress(ClassID classId, // // Verify the value of threadId, which must be the current thread ID or NULL, which means using curernt thread ID. // - if ((threadId != NULL) && (threadId != ((ThreadID)GetThread()))) + if ((threadId != NULL) && (threadId != ((ThreadID)GetThreadNULLOk()))) { return E_INVALIDARG; } - threadId = reinterpret_cast(GetThread()); + threadId = reinterpret_cast(GetThreadNULLOk()); AppDomainID appDomainId = reinterpret_cast(GetAppDomain()); // @@ -3352,12 +3352,12 @@ HRESULT ProfToEEInterfaceImpl::GetThreadStaticAddress2(ClassID classId, if (threadId == NULL) { - if (GetThread() == NULL) + if (GetThreadNULLOk() == NULL) { return CORPROF_E_NOT_MANAGED_THREAD; } - threadId = reinterpret_cast(GetThread()); + threadId = reinterpret_cast(GetThreadNULLOk()); } // @@ -8914,7 +8914,7 @@ HRESULT ProfToEEInterfaceImpl::GetNotifiedExceptionClauseInfo(COR_PRF_EX_CLAUSE_ EHClauseInfo* pCurrentEHClauseInfo = NULL; // notification requires that we are on a managed thread with an exception in flight - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // If pThread is null, then the thread has never run managed code if (pThread == NULL) @@ -8985,9 +8985,9 @@ HRESULT ProfToEEInterfaceImpl::GetObjectGeneration(ObjectID objectId, "**PROF: GetObjectGeneration 0x%p.\n", objectId)); - BEGIN_GETTHREAD_ALLOWED; - _ASSERTE((GetThread() == NULL) || (GetThread()->PreemptiveGCDisabled())); - END_GETTHREAD_ALLOWED; + + _ASSERTE((GetThreadNULLOk() == NULL) || (GetThreadNULLOk()->PreemptiveGCDisabled())); + // Announce we are using the generation table now CounterHolder genTableLock(&s_generationTableLock); @@ -9076,12 +9076,12 @@ HRESULT ProfToEEInterfaceImpl::SetupThreadForReJIT() HRESULT hr = S_OK; EX_TRY { - if (GetThread() == NULL) + if (GetThreadNULLOk() == NULL) { SetupThread(); } - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); pThread->SetProfilerCallbackStateFlags(COR_PRF_CALLBACKSTATE_REJIT_WAS_CALLED); } EX_CATCH_HRESULT(hr); @@ -10102,7 +10102,7 @@ HRESULT ProfToEEInterfaceImpl::InitializeCurrentThread() LL_INFO10, "**PROF: InitializeCurrentThread.\n")); - SetupTLSForThread(GetThread()); + SetupTLSForThread(); return S_OK; } diff --git a/src/coreclr/vm/proftoeeinterfaceimpl.inl b/src/coreclr/vm/proftoeeinterfaceimpl.inl index 929dd707e7ae5..18404dfe91920 100644 --- a/src/coreclr/vm/proftoeeinterfaceimpl.inl +++ b/src/coreclr/vm/proftoeeinterfaceimpl.inl @@ -60,7 +60,6 @@ inline BOOL AreCallbackStateFlagsSet(DWORD dwFlags) } BOOL fRet; - BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; DWORD dwProfilerCallbackFullStateFlags = pThread->GetProfilerCallbackFullState(); if (((dwProfilerCallbackFullStateFlags & COR_PRF_CALLBACKSTATE_FORCEGC_WAS_CALLED) != 0) || ((dwProfilerCallbackFullStateFlags & COR_PRF_CALLBACKSTATE_REJIT_WAS_CALLED) != 0)) @@ -71,7 +70,6 @@ inline BOOL AreCallbackStateFlagsSet(DWORD dwFlags) } fRet = ((dwProfilerCallbackFullStateFlags & dwFlags) == dwFlags); - END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; return fRet; } diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index d91a48ada7323..a7a7e0394a1f4 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -635,16 +635,7 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat // In multi-assembly composite images, per assembly sections are stored next to their core headers. m_pCompositeInfo = pNativeImage->GetReadyToRunInfo(); m_pComposite = m_pCompositeInfo->GetComponentInfo(); - if (pNativeImage->GetComponentAssemblyCount() == 1) - { - // When there's just 1 component assembly in the composite image, we're skipping the - // assembly headers and store all sections directly in the main R2R header. - m_component = *m_pComposite; - } - else - { - m_component = ReadyToRunCoreInfo(m_pComposite->GetLayout(), pNativeImage->GetComponentAssemblyHeader(pModule->GetSimpleName())); - } + m_component = ReadyToRunCoreInfo(m_pComposite->GetLayout(), pNativeImage->GetComponentAssemblyHeader(pModule->GetSimpleName())); m_isComponentAssembly = true; } else diff --git a/src/coreclr/vm/reflectclasswriter.cpp b/src/coreclr/vm/reflectclasswriter.cpp index 40688a9d629ac..c8c2b94ca7449 100644 --- a/src/coreclr/vm/reflectclasswriter.cpp +++ b/src/coreclr/vm/reflectclasswriter.cpp @@ -18,7 +18,7 @@ STDAPI GetMetaDataInternalInterfaceFromPublic( //* constructor for RefClassWriter //* //****************************************************** -HRESULT RefClassWriter::Init(ICeeGen *pCeeGen, IUnknown *pUnk, LPCWSTR szName) +HRESULT RefClassWriter::Init(ICeeGenInternal *pCeeGen, IUnknown *pUnk, LPCWSTR szName) { CONTRACT(HRESULT) { NOTHROW; diff --git a/src/coreclr/vm/reflectclasswriter.h b/src/coreclr/vm/reflectclasswriter.h index 574eb78c8faf6..98c22ca220c47 100644 --- a/src/coreclr/vm/reflectclasswriter.h +++ b/src/coreclr/vm/reflectclasswriter.h @@ -17,7 +17,7 @@ class RefClassWriter { IMetaDataEmit2* m_emitter; // Emit interface. IMetaDataImport* m_importer; // Import interface. IMDInternalImport* m_internalimport; // Scopeless internal import interface - ICeeGen* m_pCeeGen; + ICeeGenInternal* m_pCeeGen; ICeeFileGen* m_pCeeFileGen; HCEEFILE m_ceeFile; IMetaDataEmitHelper* m_pEmitHelper; @@ -29,7 +29,7 @@ class RefClassWriter { LIMITED_METHOD_CONTRACT; } - HRESULT Init(ICeeGen *pCeeGen, IUnknown *pUnk, LPCWSTR szName); + HRESULT Init(ICeeGenInternal *pCeeGen, IUnknown *pUnk, LPCWSTR szName); IMetaDataEmit2* GetEmitter() { LIMITED_METHOD_CONTRACT; @@ -51,7 +51,7 @@ class RefClassWriter { return m_internalimport; } - ICeeGen* GetCeeGen() { + ICeeGenInternal* GetCeeGen() { LIMITED_METHOD_CONTRACT; return m_pCeeGen; } diff --git a/src/coreclr/vm/runtimecallablewrapper.cpp b/src/coreclr/vm/runtimecallablewrapper.cpp index c26f311c9876b..17f58b0f80b66 100644 --- a/src/coreclr/vm/runtimecallablewrapper.cpp +++ b/src/coreclr/vm/runtimecallablewrapper.cpp @@ -1241,7 +1241,7 @@ HRESULT RCWCleanupList::ReleaseRCWListInCorrectCtx(LPVOID pData) // into cooperative GC mode. This "fix" will prevent us from doing so. if (g_fEEShutDown & ShutDown_Finalize2) { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread && !FinalizerThread::IsCurrentThreadFinalizer()) pThread->SetThreadStateNC(Thread::TSNC_UnsafeSkipEnterCooperative); } @@ -1254,7 +1254,7 @@ HRESULT RCWCleanupList::ReleaseRCWListInCorrectCtx(LPVOID pData) // the MTA context), we will infinitely loop. So, we short circuit this with ctxTried. Thread *pHeadThread = pHead->GetSTAThread(); - BOOL fCorrectThread = (pHeadThread == NULL) ? TRUE : (pHeadThread == GetThread()); + BOOL fCorrectThread = (pHeadThread == NULL) ? TRUE : (pHeadThread == GetThreadNULLOk()); BOOL fCorrectCookie = (pCurrCtxCookie == NULL) ? TRUE : (pHead->GetWrapperCtxCookie() == pCurrCtxCookie); if ( pHead->IsFreeThreaded() || // Avoid context transition if the list is for free threaded RCW @@ -1281,7 +1281,7 @@ HRESULT RCWCleanupList::ReleaseRCWListInCorrectCtx(LPVOID pData) // Reset the bit indicating we cannot transition into cooperative GC mode. if (g_fEEShutDown & ShutDown_Finalize2) { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread && !FinalizerThread::IsCurrentThreadFinalizer()) pThread->ResetThreadStateNC(Thread::TSNC_UnsafeSkipEnterCooperative); } @@ -1433,7 +1433,6 @@ void RCW::Initialize(IUnknown* pUnk, DWORD dwSyncBlockIndex, MethodTable *pClass // if this thread is an STA thread, then when the STA dies // we need to cleanup this wrapper m_pCreatorThread = GetThread(); - _ASSERTE(m_pCreatorThread != NULL); m_pRCWCache = RCWCache::GetRCWCache(); diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 15c68402d65d4..6e28b00fd8d45 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -5350,7 +5350,7 @@ MetaSig::TryGetUnmanagedCallingConventionFromModOpt( _ASSERTE(pWalk <= pSig + cSig); *callConvOut = CorInfoCallConvExtension::Managed; - bool found = false; + CallingConventionModifiers modifiers = CALL_CONV_MOD_NONE; while ((pWalk < (pSig + cSig)) && ((*pWalk == ELEMENT_TYPE_CMOD_OPT) || (*pWalk == ELEMENT_TYPE_CMOD_REQD))) { BOOL fIsOptional = (*pWalk == ELEMENT_TYPE_CMOD_OPT); @@ -5378,42 +5378,113 @@ MetaSig::TryGetUnmanagedCallingConventionFromModOpt( if (::strcmp(typeNamespace, CMOD_CALLCONV_NAMESPACE) != 0) continue; - if (::strcmp(typeName, CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION) == 0) + if (!TryApplyModOptToCallingConvention( + typeName, + ::strlen(typeName), + CallConvModOptNameType::TypeName, + callConvOut, + &modifiers)) { - *suppressGCTransitionOut = true; - continue; + // Error if there are multiple recognized base calling conventions + *errorResID = IDS_EE_MULTIPLE_CALLCONV_UNSUPPORTED; + return COR_E_INVALIDPROGRAM; } + } - const struct { - LPCSTR name; - CorInfoCallConvExtension value; - } knownCallConvs[] = { - { CMOD_CALLCONV_NAME_CDECL, CorInfoCallConvExtension::C }, - { CMOD_CALLCONV_NAME_STDCALL, CorInfoCallConvExtension::Stdcall }, - { CMOD_CALLCONV_NAME_THISCALL, CorInfoCallConvExtension::Thiscall }, - { CMOD_CALLCONV_NAME_FASTCALL, CorInfoCallConvExtension::Fastcall } }; + *suppressGCTransitionOut = ((modifiers & CALL_CONV_MOD_SUPPRESSGCTRANSITION) != 0); - for (const auto &callConv : knownCallConvs) + if (modifiers & CALL_CONV_MOD_MEMBERFUNCTION) + { + if (*callConvOut == CorInfoCallConvExtension::Managed) { - // Look for a recognized calling convention in metadata. - if (::strcmp(typeName, callConv.name) == 0) - { - // Error if there are multiple recognized calling conventions - if (found) - { - *errorResID = IDS_EE_MULTIPLE_CALLCONV_UNSUPPORTED; - return COR_E_INVALIDPROGRAM; - } - - *callConvOut = callConv.value; - found = true; - } + // In this case, the only specified calling convention is CallConvMemberFunction. + // Set *callConvOut to the default unmanaged calling convention. + *callConvOut = MetaSig::GetDefaultUnmanagedCallingConvention(); } + + *callConvOut = MetaSig::GetMemberFunctionUnmanagedCallingConventionVariant(*callConvOut); } - return found ? S_OK : S_FALSE; + return *callConvOut != CorInfoCallConvExtension::Managed ? S_OK : S_FALSE; +} + +// According to ECMA-335, type name strings are UTF-8. Since we are +// looking for type names that are equivalent in ASCII and UTF-8, +// using a const char constant is acceptable. Type name strings are +// in Fully Qualified form, so we include the ',' delimiter. +#define MAKE_FULLY_QUALIFIED_CALLCONV_TYPE_NAME_PREFIX(callConvTypeName) CMOD_CALLCONV_NAMESPACE "." callConvTypeName "," + +namespace +{ + // Templated function to compute if a char string begins with a constant string. + template + bool BeginsWith(size_t s1Len, const char* s1, const char (&s2)[S2LEN]) + { + WRAPPER_NO_CONTRACT; + + size_t s2Len = S2LEN - 1; // Remove null + + if (s1Len < s2Len) + return false; + + return (0 == strncmp(s1, s2, s2Len)); + } } +bool MetaSig::TryApplyModOptToCallingConvention( + _In_z_ LPCSTR callConvModOptName, + _In_ size_t callConvModOptNameLength, + _In_ CallConvModOptNameType nameType, + _Inout_ CorInfoCallConvExtension* pBaseCallConv, + _Inout_ CallingConventionModifiers* pCallConvModifiers) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pBaseCallConv)); + PRECONDITION(CheckPointer(pCallConvModifiers)); + } + CONTRACTL_END; + +#define BASE_CALL_CONV(callConvModOptMetadataName, CallConvExtensionMember) \ + if (COMPARE_CALLCONV_NAME(callConvModOptName, callConvModOptMetadataName)) \ + { \ + if (*pBaseCallConv != CorInfoCallConvExtension::Managed) return false; \ + *pBaseCallConv = CorInfoCallConvExtension::CallConvExtensionMember; \ + return true; \ + } + \ +#define CALL_CONV_MODIFIER(callConvModOptMetadataName, CallConvModifiersMember) \ + if (COMPARE_CALLCONV_NAME(callConvModOptName, callConvModOptMetadataName)) \ + { \ + *pCallConvModifiers = (CallingConventionModifiers)(*pCallConvModifiers | CallConvModifiersMember); \ + return true; \ + } + +#define PARSE_CALL_CONVS \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_CDECL, C) \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_STDCALL, Stdcall) \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_THISCALL, Thiscall) \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_FASTCALL, Fastcall) \ + CALL_CONV_MODIFIER(CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION, CALL_CONV_MOD_SUPPRESSGCTRANSITION) \ + CALL_CONV_MODIFIER(CMOD_CALLCONV_NAME_MEMBERFUNCTION, CALL_CONV_MOD_MEMBERFUNCTION) \ + + if (nameType == CallConvModOptNameType::TypeName) + { +#define COMPARE_CALLCONV_NAME(userProvidedName, metadataName) ::strcmp(userProvidedName, metadataName) == 0 + PARSE_CALL_CONVS; +#undef COMPARE_CALLCONV_NAME + } + else + { + _ASSERTE(nameType == CallConvModOptNameType::FullyQualifiedName); + +#define COMPARE_CALLCONV_NAME(userProvidedName, metadataName) BeginsWith(callConvModOptNameLength, userProvidedName, MAKE_FULLY_QUALIFIED_CALLCONV_TYPE_NAME_PREFIX(metadataName)) + PARSE_CALL_CONVS; +#undef COMPARE_CALLCONV_NAME + } + return true; +} //--------------------------------------------------------------------------------------- // // Substitution from a token (TypeDef and TypeRef have empty instantiation, TypeSpec gets it from MetaData). diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index 718604f71191c..ab49876cd3bfb 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -798,6 +798,30 @@ class MetaSig _Out_ bool* suppressGCTransitionOut, _Out_ UINT *errorResID); + enum CallingConventionModifiers + { + CALL_CONV_MOD_NONE = 0, + CALL_CONV_MOD_SUPPRESSGCTRANSITION = 0x1, + CALL_CONV_MOD_MEMBERFUNCTION = 0x2 + }; + + enum class CallConvModOptNameType + { + TypeName, + FullyQualifiedName + }; + + // Attempt to parse the provided calling convention name and add it to the collected modifiers and calling convention + // Returns false if the modopt is known and cannot be applied. + // This occurs when the modopt is a base calling convention and a base calling convention has already been supplied. + // Otherwise, returns true. + static bool TryApplyModOptToCallingConvention( + _In_z_ LPCSTR callConvModOptName, + _In_ size_t callConvModOptNameLength, + _In_ CallConvModOptNameType nameType, + _Inout_ CorInfoCallConvExtension* pBaseCallConv, + _Inout_ CallingConventionModifiers* pCallConvModifiers); + static CorInfoCallConvExtension GetDefaultUnmanagedCallingConvention() { #ifdef TARGET_UNIX @@ -807,6 +831,24 @@ class MetaSig #endif // !TARGET_UNIX } + static CorInfoCallConvExtension GetMemberFunctionUnmanagedCallingConventionVariant(CorInfoCallConvExtension baseCallConv) + { + switch (baseCallConv) + { + case CorInfoCallConvExtension::C: + return CorInfoCallConvExtension::CMemberFunction; + case CorInfoCallConvExtension::Stdcall: + return CorInfoCallConvExtension::StdcallMemberFunction; + case CorInfoCallConvExtension::Fastcall: + return CorInfoCallConvExtension::FastcallMemberFunction; + case CorInfoCallConvExtension::Thiscall: + return CorInfoCallConvExtension::Thiscall; + default: + _ASSERTE("Calling convention is not an unmanaged base calling convention."); + return baseCallConv; + } + } + //------------------------------------------------------------------ // Like NextArg, but return only normalized type (enums flattned to // underlying type ... diff --git a/src/coreclr/vm/simplerwlock.cpp b/src/coreclr/vm/simplerwlock.cpp index b434e7b416d10..dfe54a987f10b 100644 --- a/src/coreclr/vm/simplerwlock.cpp +++ b/src/coreclr/vm/simplerwlock.cpp @@ -238,9 +238,9 @@ void SimpleRWLock::PreEnter() CONTRACTL_END; if (m_gcMode == PREEMPTIVE) - _ASSERTE(!GetThread() || !GetThread()->PreemptiveGCDisabled()); + _ASSERTE(!GetThreadNULLOk() || !GetThread()->PreemptiveGCDisabled()); else if (m_gcMode == COOPERATIVE) - _ASSERTE(!GetThread() || GetThread()->PreemptiveGCDisabled()); + _ASSERTE(!GetThreadNULLOk() || GetThread()->PreemptiveGCDisabled()); } //===================================================================== diff --git a/src/coreclr/vm/spinlock.cpp b/src/coreclr/vm/spinlock.cpp index 2d7542b53db39..c9173198f620f 100644 --- a/src/coreclr/vm/spinlock.cpp +++ b/src/coreclr/vm/spinlock.cpp @@ -277,7 +277,7 @@ void SpinLock::dbg_PreEnterLock() } CONTRACTL_END; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread) { // SpinLock can not be nested. @@ -300,7 +300,7 @@ void SpinLock::dbg_EnterLock() } CONTRACTL_END; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread) { INCONTRACT(pThread->BeginNoTriggerGC(__FILE__, __LINE__)); @@ -317,7 +317,7 @@ void SpinLock::dbg_LeaveLock() } CONTRACTL_END; - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread) { _ASSERTE ((pThread->m_StateNC & Thread::TSNC_OwnsSpinLock) != 0); diff --git a/src/coreclr/vm/stackingallocator.cpp b/src/coreclr/vm/stackingallocator.cpp index 911132afc86d7..8806bceabb2ff 100644 --- a/src/coreclr/vm/stackingallocator.cpp +++ b/src/coreclr/vm/stackingallocator.cpp @@ -26,21 +26,8 @@ #include "common.h" #include "excep.h" - -#if 0 -#define INC_COUNTER(_name, _amount) do { \ - unsigned _count = REGUTIL::GetLong(W("AllocCounter_") _name, 0, NULL, HKEY_CURRENT_USER); \ - REGUTIL::SetLong(W("AllocCounter_") _name, _count+(_amount), NULL, HKEY_CURRENT_USER); \ - } while (0) -#define MAX_COUNTER(_name, _amount) do { \ - unsigned _count = REGUTIL::GetLong(W("AllocCounter_") _name, 0, NULL, HKEY_CURRENT_USER); \ - REGUTIL::SetLong(W("AllocCounter_") _name, max(_count, (_amount)), NULL, HKEY_CURRENT_USER); \ - } while (0) -#else #define INC_COUNTER(_name, _amount) #define MAX_COUNTER(_name, _amount) -#endif - StackingAllocator::StackingAllocator() { diff --git a/src/coreclr/vm/stackwalk.cpp b/src/coreclr/vm/stackwalk.cpp index a13127e3c6569..0971334af4d31 100644 --- a/src/coreclr/vm/stackwalk.cpp +++ b/src/coreclr/vm/stackwalk.cpp @@ -946,7 +946,7 @@ StackWalkAction Thread::StackWalkFrames(PSTACKWALKFRAMESCALLBACK pCallback, bool fUseInitRegDisplay; #ifndef DACCESS_COMPILE - _ASSERTE(GetThread() == this || (flags & ALLOW_ASYNC_STACK_WALK)); + _ASSERTE(GetThreadNULLOk() == this || (flags & ALLOW_ASYNC_STACK_WALK)); BOOL fDebuggerHasInitialContext = (GetFilterContext() != NULL); BOOL fProfilerHasInitialContext = (GetProfilerFilterContext() != NULL); @@ -1496,9 +1496,9 @@ BOOL StackFrameIterator::IsValid(void) // In particular, is thread's starting frame the same as it was when // we started? BOOL bIsRealStartFrameUnchanged = - (m_pStartFrame != NULL) || - (m_flags & POPFRAMES) || - (m_pRealStartFrame == m_pThread->GetFrame()); + (m_pStartFrame != NULL) + || (m_flags & POPFRAMES) + || (m_pRealStartFrame == m_pThread->GetFrame()); #ifdef FEATURE_HIJACK // In GCStress >= 4 two threads could race on triggering GC; @@ -1508,15 +1508,17 @@ BOOL StackFrameIterator::IsValid(void) // In normal case (no GCStress), after p/invoke, IL_STUB will check if GC is in progress and synchronize. // NOTE: This condition needs to be evaluated after the previous one to prevent a subtle race condition // (https://github.com/dotnet/runtime/issues/11678) - bIsRealStartFrameUnchanged = bIsRealStartFrameUnchanged || - ((GCStress::IsEnabled()) && - (m_pRealStartFrame != NULL) && - (m_pRealStartFrame != FRAME_TOP) && - (m_pRealStartFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()) && - (m_pThread->GetFrame() != NULL) && - (m_pThread->GetFrame() != FRAME_TOP) && - ((m_pThread->GetFrame()->GetVTablePtr() == ResumableFrame::GetMethodFrameVPtr()) || - (m_pThread->GetFrame()->GetVTablePtr() == RedirectedThreadFrame::GetMethodFrameVPtr()))); + if (bIsRealStartFrameUnchanged == FALSE) + { + _ASSERTE(GCStress::IsEnabled()); + _ASSERTE(m_pRealStartFrame != NULL); + _ASSERTE(m_pRealStartFrame != FRAME_TOP); + _ASSERTE(m_pRealStartFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr()); + _ASSERTE(m_pThread->GetFrame() != NULL); + _ASSERTE(m_pThread->GetFrame() != FRAME_TOP); + bIsRealStartFrameUnchanged = (m_pThread->GetFrame()->GetVTablePtr() == ResumableFrame::GetMethodFrameVPtr()) + || (m_pThread->GetFrame()->GetVTablePtr() == RedirectedThreadFrame::GetMethodFrameVPtr()); + } #endif // FEATURE_HIJACK _ASSERTE(bIsRealStartFrameUnchanged); diff --git a/src/coreclr/vm/stringliteralmap.h b/src/coreclr/vm/stringliteralmap.h index ef30622281ff2..22622733c779b 100644 --- a/src/coreclr/vm/stringliteralmap.h +++ b/src/coreclr/vm/stringliteralmap.h @@ -217,7 +217,7 @@ class StringLiteralEntry CONTRACTL { NOTHROW; - if(GetThread()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);}; + if(GetThreadNULLOk()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);}; PRECONDITION(CheckPointer(this)); } CONTRACTL_END; @@ -232,7 +232,7 @@ class StringLiteralEntry CONTRACTL { NOTHROW; - if(GetThread()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);}; + if(GetThreadNULLOk()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);}; PRECONDITION(CheckPointer(this)); } CONTRACTL_END; @@ -244,7 +244,7 @@ class StringLiteralEntry CONTRACTL { NOTHROW; - if(GetThread()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);}; + if(GetThreadNULLOk()){GC_NOTRIGGER;}else{DISABLED(GC_TRIGGERS);}; MODE_COOPERATIVE; PRECONDITION(CheckPointer(this)); PRECONDITION(CheckPointer(pStringData)); diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index 7da6a1b7c8748..f829212a8ef6e 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -2813,6 +2813,18 @@ void ILStubLinker::SetStubTargetCallingConv(CorInfoCallConvExtension callConv) case CorInfoCallConvExtension::Fastcall: m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_FASTCALL))); break; + case CorInfoCallConvExtension::CMemberFunction: + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_CDECL))); + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_MEMBERFUNCTION))); + break; + case CorInfoCallConvExtension::StdcallMemberFunction: + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_STDCALL))); + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_MEMBERFUNCTION))); + break; + case CorInfoCallConvExtension::FastcallMemberFunction: + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_FASTCALL))); + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_MEMBERFUNCTION))); + break; default: _ASSERTE("Unknown calling convention. Unable to encode it in the native function pointer signature."); break; diff --git a/src/coreclr/vm/stublink.cpp b/src/coreclr/vm/stublink.cpp index 00d5c84c9db2e..261cd15f7607a 100644 --- a/src/coreclr/vm/stublink.cpp +++ b/src/coreclr/vm/stublink.cpp @@ -1343,7 +1343,7 @@ bool StubLinker::EmitUnwindInfo(Stub* pStub, int globalsize, LoaderHeap* pHeap) #ifdef _DEBUG static SIZE_T MaxSegmentSize = -1; if (MaxSegmentSize == (SIZE_T)-1) - MaxSegmentSize = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_MaxStubUnwindInfoSegmentSize, DYNAMIC_FUNCTION_TABLE_MAX_RANGE); + MaxSegmentSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MaxStubUnwindInfoSegmentSize, DYNAMIC_FUNCTION_TABLE_MAX_RANGE); #else const SIZE_T MaxSegmentSize = DYNAMIC_FUNCTION_TABLE_MAX_RANGE; #endif diff --git a/src/coreclr/vm/syncblk.cpp b/src/coreclr/vm/syncblk.cpp index 55736ec2cd4fa..a86cd61a8993c 100644 --- a/src/coreclr/vm/syncblk.cpp +++ b/src/coreclr/vm/syncblk.cpp @@ -1692,7 +1692,7 @@ BOOL ObjHeader::LeaveObjMonitor() for (;;) { - AwareLock::LeaveHelperAction action = thisObj->GetHeader ()->LeaveObjMonitorHelper(GetThread()); + AwareLock::LeaveHelperAction action = thisObj->GetHeader()->LeaveObjMonitorHelper(GetThread()); switch(action) { @@ -1923,7 +1923,7 @@ DEBUG_NOINLINE void ObjHeader::EnterSpinLock() __SwitchToThread(0, ++dwSwitchCount); } - INCONTRACT(Thread* pThread = GetThread()); + INCONTRACT(Thread* pThread = GetThreadNULLOk()); INCONTRACT(if (pThread != NULL) pThread->BeginNoTriggerGC(__FILE__, __LINE__)); } #else @@ -1959,7 +1959,7 @@ DEBUG_NOINLINE void ObjHeader::EnterSpinLock() __SwitchToThread(0, ++dwSwitchCount); } - INCONTRACT(Thread* pThread = GetThread()); + INCONTRACT(Thread* pThread = GetThreadNULLOk()); INCONTRACT(if (pThread != NULL) pThread->BeginNoTriggerGC(__FILE__, __LINE__)); } #endif //MP_LOCKS @@ -1969,7 +1969,7 @@ DEBUG_NOINLINE void ObjHeader::ReleaseSpinLock() SCAN_SCOPE_END; LIMITED_METHOD_CONTRACT; - INCONTRACT(Thread* pThread = GetThread()); + INCONTRACT(Thread* pThread = GetThreadNULLOk()); INCONTRACT(if (pThread != NULL) pThread->EndNoTriggerGC()); FastInterlockAnd(&m_SyncBlockValue, ~BIT_SBLK_SPIN_LOCK); @@ -2953,4 +2953,3 @@ void ObjHeader::IllegalAlignPad() } #endif // HOST_64BIT && _DEBUG - diff --git a/src/coreclr/vm/syncclean.cpp b/src/coreclr/vm/syncclean.cpp index 55e8dec1bf650..4ffb3d2f109cb 100644 --- a/src/coreclr/vm/syncclean.cpp +++ b/src/coreclr/vm/syncclean.cpp @@ -30,9 +30,7 @@ void SyncClean::AddHashMap (Bucket *bucket) return; } - BEGIN_GETTHREAD_ALLOWED - _ASSERTE (GetThread() == NULL || GetThread()->PreemptiveGCDisabled()); - END_GETTHREAD_ALLOWED + _ASSERTE (GetThreadNULLOk() == NULL || GetThread()->PreemptiveGCDisabled()); Bucket * pTempBucket = NULL; do @@ -52,9 +50,7 @@ void SyncClean::AddEEHashTable (EEHashEntry** entry) return; } - BEGIN_GETTHREAD_ALLOWED - _ASSERTE (GetThread() == NULL || GetThread()->PreemptiveGCDisabled()); - END_GETTHREAD_ALLOWED + _ASSERTE (GetThreadNULLOk() == NULL || GetThread()->PreemptiveGCDisabled()); EEHashEntry ** pTempHashEntry = NULL; do @@ -72,7 +68,7 @@ void SyncClean::CleanUp () // Only GC thread can call this. _ASSERTE (g_fProcessDetach || IsGCSpecialThread() || - (GCHeapUtilities::IsGCInProgress() && GetThread() == ThreadSuspend::GetSuspensionThread())); + (GCHeapUtilities::IsGCInProgress() && GetThreadNULLOk() == ThreadSuspend::GetSuspensionThread())); if (m_HashMap) { Bucket * pTempBucket = FastInterlockExchangePointer(m_HashMap.GetPointer(), NULL); diff --git a/src/coreclr/vm/synch.cpp b/src/coreclr/vm/synch.cpp index bc60f8c005bae..2f9acbe476b06 100644 --- a/src/coreclr/vm/synch.cpp +++ b/src/coreclr/vm/synch.cpp @@ -118,7 +118,7 @@ void CLREventBase::CreateMonitorEvent(SIZE_T Cookie) GC_NOTRIGGER; // disallow creation of Crst before EE starts PRECONDITION((g_fEEStarted)); - PRECONDITION((GetThread() != NULL)); + PRECONDITION((GetThreadNULLOk() != NULL)); PRECONDITION((!IsOSEvent())); } CONTRACTL_END; @@ -426,7 +426,7 @@ DWORD CLREventBase::WaitEx(DWORD dwMilliseconds, WaitMode mode, PendingSync *syn { NOTHROW; } - if (GetThread()) + if (GetThreadNULLOk()) { if (alertable) GC_TRIGGERS; @@ -444,7 +444,7 @@ DWORD CLREventBase::WaitEx(DWORD dwMilliseconds, WaitMode mode, PendingSync *syn _ASSERTE(Thread::Debug_AllowCallout()); - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); #ifdef _DEBUG // If a CLREvent is OS event only, we can not wait for the event on a managed thread @@ -518,7 +518,7 @@ DWORD CLRSemaphore::Wait(DWORD dwMilliseconds, BOOL alertable) { CONTRACTL { - if (GetThread() && alertable) + if (GetThreadNULLOk() && alertable) { THROWS; // Thread::DoAppropriateWait can throw } @@ -526,7 +526,8 @@ DWORD CLRSemaphore::Wait(DWORD dwMilliseconds, BOOL alertable) { NOTHROW; } - if (GetThread()) + + if (GetThreadNULLOk()) { if (alertable) GC_TRIGGERS; @@ -537,12 +538,13 @@ DWORD CLRSemaphore::Wait(DWORD dwMilliseconds, BOOL alertable) { DISABLED(GC_TRIGGERS); } + PRECONDITION(m_handle != INVALID_HANDLE_VALUE); // Invalid to have invalid handle } CONTRACTL_END; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); _ASSERTE (pThread || !g_fEEStarted || dbgOnly_IsSpecialEEThread()); { diff --git a/src/coreclr/vm/threadpoolrequest.cpp b/src/coreclr/vm/threadpoolrequest.cpp index 0e918a3c5cad3..ff3fc9c93cf92 100644 --- a/src/coreclr/vm/threadpoolrequest.cpp +++ b/src/coreclr/vm/threadpoolrequest.cpp @@ -525,7 +525,7 @@ void UnManagedPerAppDomainTPCount::DispatchWorkItem(bool* foundWork, bool* wasNo } *wasNotRecalled = ThreadpoolMgr::ShouldWorkerKeepRunning(); - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread) { _ASSERTE(!pThread->IsAbortRequested()); @@ -631,7 +631,7 @@ void ManagedPerAppDomainTPCount::DispatchWorkItem(bool* foundWork, bool* wasNotR *wasNotRecalled = true; HRESULT hr; - Thread * pThread = GetThread(); + Thread * pThread = GetThreadNULLOk(); if (pThread == NULL) { ClrFlsSetThreadType(ThreadType_Threadpool_Worker); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 2bcc4edf2a92f..62f2b7eacd919 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -60,6 +60,11 @@ TailCallTls::TailCallTls() { } +Thread* STDCALL GetThreadHelper() +{ + return GetThreadNULLOk(); +} + TailCallArgBuffer* TailCallTls::AllocArgBuffer(int size, void* gcDesc) { CONTRACTL @@ -406,13 +411,13 @@ DWORD Thread::JoinEx(DWORD timeout, WaitMode mode) { CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; BOOL alertable = (mode & WaitMode_Alertable)?TRUE:FALSE; - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); _ASSERTE(pCurThread || dbgOnly_IsSpecialEEThread()); { @@ -593,13 +598,13 @@ Thread* SetupThreadNoThrow(HRESULT *pHR) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; HRESULT hr = S_OK; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread != NULL) { return pThread; @@ -635,7 +640,7 @@ void DeleteThread(Thread* pThread) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -668,7 +673,7 @@ void DeleteThread(Thread* pThread) void EnsurePreemptive() { WRAPPER_NO_CONTRACT; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread && pThread->PreemptiveGCDisabled()) { pThread->EnablePreemptiveGC(); @@ -681,12 +686,12 @@ Thread* SetupThread() { CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; Thread* pThread; - if ((pThread = GetThread()) != NULL) + if ((pThread = GetThreadNULLOk()) != NULL) return pThread; // For interop debugging, we must mark that we're in a can't-stop region @@ -768,7 +773,7 @@ Thread* SetupThread() Holder,DeleteThread> threadHolder(pThread); - SetupTLSForThread(pThread); + SetupTLSForThread(); if (!pThread->InitThread() || !pThread->PrepareApartmentAndContext()) @@ -867,7 +872,7 @@ Thread* SetupUnstartedThread(BOOL bRequiresTSL) { CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -1184,7 +1189,7 @@ void InitThreadManager() #endif // FEATURE_WRITEBARRIER_COPY #ifndef TARGET_UNIX - _ASSERTE(GetThread() == NULL); + _ASSERTE(GetThreadNULLOk() == NULL); size_t offsetOfCurrentThreadInfo = Thread::GetOffsetOfThreadStatic(&gCurrentThreadInfo); @@ -1348,7 +1353,7 @@ Thread::Thread() { CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -1557,7 +1562,6 @@ Thread::Thread() m_ioThreadPoolCompletionCount = 0; m_monitorLockContentionCount = 0; - Thread *pThread = GetThread(); InitContext(); // Do not expose thread until it is fully constructed @@ -1612,7 +1616,7 @@ BOOL Thread::InitThread() { CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -1796,11 +1800,11 @@ BOOL Thread::HasStarted(BOOL bRequiresTSL) // the runtime. But sometimes that thread is used for DLL_THREAD_ATTACH notifications // that call into managed code. In that case, the second HasStarted call is // redundant and should be ignored. - if (GetThread() == this) + if (GetThreadNULLOk() == this) return TRUE; - _ASSERTE(GetThread() == 0); + _ASSERTE(GetThreadNULLOk() == 0); _ASSERTE(HasValidThreadHandle()); BOOL fKeepTLS = FALSE; @@ -1822,7 +1826,7 @@ BOOL Thread::HasStarted(BOOL bRequiresTSL) // Initialization must happen in the following order - hosts like SQL Server depend on this. // - SetupTLSForThread(this); + SetupTLSForThread(); fCanCleanupCOMState = TRUE; res = PrepareApartmentAndContext(); @@ -1864,7 +1868,7 @@ BOOL Thread::HasStarted(BOOL bRequiresTSL) SetThreadState(TS_FailStarted); - if (GetThread() != NULL && IsAbortRequested()) + if (GetThreadNULLOk() != NULL && IsAbortRequested()) UnmarkThreadForAbort(); if (!fKeepTLS) @@ -1977,7 +1981,7 @@ void Thread::HandleThreadStartupFailure() } CONTRACTL_END; - _ASSERTE(GetThread() != NULL); + _ASSERTE(GetThreadNULLOk() != NULL); struct ProtectArgs { @@ -2336,11 +2340,11 @@ int Thread::IncExternalCount() { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); _ASSERTE(m_ExternalRefCount > 0); int retVal = FastInterlockIncrement((LONG*)&m_ExternalRefCount); @@ -2367,13 +2371,13 @@ int Thread::DecExternalCount(BOOL holdingLock) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; // Note that it's possible to get here with a NULL current thread (during // shutdown of the thread manager). - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); _ASSERTE (pCurThread == NULL || IsAtProcessExit() || (!holdingLock && !ThreadStore::HoldingThreadStore(pCurThread)) || (holdingLock && ThreadStore::HoldingThreadStore(pCurThread))); @@ -2535,7 +2539,7 @@ Thread::~Thread() { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -2824,7 +2828,7 @@ void Thread::CleanupCOMState() { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -2871,7 +2875,7 @@ void Thread::OnThreadTerminate(BOOL holdingLock) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -2887,7 +2891,7 @@ void Thread::OnThreadTerminate(BOOL holdingLock) // Should not use OSThreadId: // OSThreadId may change for the current thread is the thread is blocked and rescheduled // by host. - Thread *pCurrentThread = GetThread(); + Thread *pCurrentThread = GetThreadNULLOk(); DWORD CurrentThreadID = pCurrentThread?pCurrentThread->GetThreadId():0; DWORD ThisThreadID = GetThreadId(); @@ -2895,7 +2899,7 @@ void Thread::OnThreadTerminate(BOOL holdingLock) // If the currently running thread is the thread that died and it is an STA thread, then we // need to release all the RCW's in the current context. However, we cannot do this if we // are in the middle of process detach. - if (!IsAtProcessExit() && this == GetThread()) + if (!IsAtProcessExit() && this == GetThreadNULLOk()) { CleanupCOMState(); } @@ -4097,7 +4101,7 @@ void WINAPI Thread::UserInterruptAPC(ULONG_PTR data) _ASSERTE(data == APC_Code); - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); if (pCurThread) { // We should only take action if an interrupt is currently being @@ -4220,7 +4224,7 @@ OBJECTREF Thread::GetExposedObject() TRIGGERSGC(); - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); _ASSERTE (!(pCurThread == NULL || IsAtProcessExit())); _ASSERTE(pCurThread->PreemptiveGCDisabled()); @@ -4289,13 +4293,13 @@ void Thread::SetExposedObject(OBJECTREF exposed) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; if (exposed != NULL) { - _ASSERTE (GetThread() != this); + _ASSERTE (GetThreadNULLOk() != this); _ASSERTE(IsUnstarted()); _ASSERTE(ObjectFromHandle(m_ExposedObject) == NULL); // The exposed object keeps us alive until it is GC'ed. This doesn't mean the @@ -4800,7 +4804,7 @@ Thread::ApartmentState Thread::GetApartmentRare(Thread::ApartmentState as) } CONTRACTL_END; - if (this == GetThread()) + if (this == GetThreadNULLOk()) { THDTYPE type; HRESULT hr = S_OK; @@ -5230,7 +5234,7 @@ void ThreadStore::AddThread(Thread *newThread, BOOL bRequiresTSL) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -5372,7 +5376,7 @@ void ThreadStore::TransferStartedThread(Thread *thread, BOOL bRequiresTSL) } CONTRACTL_END; - _ASSERTE(GetThread() == thread); + _ASSERTE(GetThreadNULLOk() == thread); LOG((LF_SYNC, INFO3, "TransferUnstartedThread obtain lock\n")); ThreadStoreLockHolder TSLockHolder(FALSE); @@ -5791,7 +5795,7 @@ BOOL ThreadStore::DbgFindThread(Thread *target) // Cache the current change stamp for g_TrapReturningThreads LONG chgStamp = g_trtChgStamp; - STRESS_LOG3(LF_STORE, LL_INFO100, "ThreadStore::DbgFindThread - [thread=%p]. trt=%d. chgStamp=%d\n", GetThread(), g_TrapReturningThreads.Load(), chgStamp); + STRESS_LOG3(LF_STORE, LL_INFO100, "ThreadStore::DbgFindThread - [thread=%p]. trt=%d. chgStamp=%d\n", GetThreadNULLOk(), g_TrapReturningThreads.Load(), chgStamp); #if 0 // g_TrapReturningThreads debug code. int iRetry = 0; @@ -5864,7 +5868,7 @@ BOOL ThreadStore::DbgFindThread(Thread *target) } #endif // g_TrapReturningThreads debug code. - STRESS_LOG4(LF_STORE, LL_INFO100, "ThreadStore::DbgFindThread - [thread=%p]. trt=%d. chg=%d. cnt=%d\n", GetThread(), g_TrapReturningThreads.Load(), g_trtChgStamp.Load(), cntReturn); + STRESS_LOG4(LF_STORE, LL_INFO100, "ThreadStore::DbgFindThread - [thread=%p]. trt=%d. chg=%d. cnt=%d\n", GetThreadNULLOk(), g_TrapReturningThreads.Load(), g_trtChgStamp.Load(), cntReturn); // Because of race conditions and the fact that the GC places its // own count, I can't assert this precisely. But I do want to be @@ -6281,8 +6285,8 @@ BOOL Thread::UniqueStack(void* stackStart) { CrstHolder ch(g_pUniqueStackCrst); #ifdef _DEBUG - if (GetThread ()) - GetThread ()->m_bUniqueStacking = TRUE; + if (GetThreadNULLOk()) + GetThread()->m_bUniqueStacking = TRUE; #endif if (g_pUniqueStackMap->LookupValue (stackTraceHash, stackTrace) != (LPVOID)INVALIDENTRY) { @@ -6295,8 +6299,8 @@ BOOL Thread::UniqueStack(void* stackStart) UniqueStackHelper(stackTraceHash, stackTrace); } #ifdef _DEBUG - if (GetThread ()) - GetThread ()->m_bUniqueStacking = FALSE; + if (GetThreadNULLOk()) + GetThread()->m_bUniqueStacking = FALSE; #endif } @@ -6668,7 +6672,7 @@ void Thread::DebugLogStackMBIs() } CONTRACTL_END; - Thread* pThread = GetThread(); // N.B. this can be NULL! + Thread* pThread = GetThreadNULLOk(); // N.B. this can be NULL! UINT_PTR uStackLimit = (UINT_PTR)GetStackLowerBound(); UINT_PTR uStackBase = (UINT_PTR)GetStackUpperBound(); @@ -7028,7 +7032,7 @@ bool Thread::InitRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, bool validCo #else pctx->ContextFlags = CONTEXT_FULL; - _ASSERTE(this != GetThread()); // do not call GetThreadContext on the active thread + _ASSERTE(this != GetThreadNULLOk()); // do not call GetThreadContext on the active thread BOOL ret = EEGetThreadContext(this, pctx); if (!ret) @@ -7103,7 +7107,6 @@ void CommonTripThread() CONTRACTL_END; Thread *thread = GetThread(); - thread->HandleThreadAbort (); if (thread->CatchAtSafePoint()) @@ -7151,7 +7154,7 @@ void Thread::InitContext() { CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -7165,7 +7168,7 @@ void Thread::ClearContext() { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -7416,7 +7419,6 @@ static LONG ThreadBaseRedirectingFilter(PEXCEPTION_POINTERS pExceptionInfo, LPVO // Get the reference to the current thread.. Thread *pCurThread = GetThread(); - _ASSERTE(pCurThread); // // In the default domain, when an exception goes unhandled on a managed thread whose threadbase is in the VM (e.g. explicitly spawned threads, @@ -7456,7 +7458,7 @@ static void ManagedThreadBase_DispatchOuter(ManagedThreadCallState *pCallState) STATIC_CONTRACT_MODE_COOPERATIVE; // HasStarted() must have already been performed by our caller - _ASSERTE(GetThread() != NULL); + _ASSERTE(GetThreadNULLOk() != NULL); Thread *pThread = GetThread(); #ifdef FEATURE_EH_FUNCLETS @@ -8094,9 +8096,6 @@ CHECK DeadlockAwareLock::CheckDeadlock(Thread *pThread) BOOL DeadlockAwareLock::CanEnterLock() { Thread * pThread = GetThread(); - - CONSISTENCY_CHECK_MSG(pThread != NULL, - "Cannot do deadlock detection on non-EE thread"); CONSISTENCY_CHECK_MSG(pThread->m_pBlockingLock.Load() == NULL, "Cannot block on two locks at once"); @@ -8144,9 +8143,6 @@ BOOL DeadlockAwareLock::TryBeginEnterLock() CONTRACTL_END; Thread * pThread = GetThread(); - - CONSISTENCY_CHECK_MSG(pThread != NULL, - "Cannot do deadlock detection on non-EE thread"); CONSISTENCY_CHECK_MSG(pThread->m_pBlockingLock.Load() == NULL, "Cannot block on two locks at once"); @@ -8196,9 +8192,6 @@ void DeadlockAwareLock::BeginEnterLock() CONTRACTL_END; Thread * pThread = GetThread(); - - CONSISTENCY_CHECK_MSG(pThread != NULL, - "Cannot do deadlock detection on non-EE thread"); CONSISTENCY_CHECK_MSG(pThread->m_pBlockingLock.Load() == NULL, "Cannot block on two locks at once"); @@ -8295,7 +8288,7 @@ BOOL dbgOnly_IsSpecialEEThread() #endif //Clean this up - if (GetThread() == NULL) + if (GetThreadNULLOk() == NULL) return TRUE; diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 5be5ae9d7ca3f..f4c66fe0c53f1 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -527,21 +527,6 @@ struct HijackArgs; #endif // FEATURE_HIJACK -//*************************************************************************** -#ifdef ENABLE_CONTRACTS_IMPL -inline Thread* GetThreadNULLOk() -{ - LIMITED_METHOD_CONTRACT; - Thread * pThread; - BEGIN_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; - pThread = GetThread(); - END_GETTHREAD_ALLOWED_IN_NO_THROW_REGION; - return pThread; -} -#else -#define GetThreadNULLOk() GetThread() -#endif - // manifest constant for waiting in the exposed classlibs const INT32 INFINITE_TIMEOUT = -1; @@ -634,6 +619,8 @@ EXTERN_C void WINAPI OnHijackFPTripThread(); // hijacked JIT code is returning void CommonTripThread(); +void SetupTLSForThread(); + // When we resume a thread at a new location, to get an exception thrown, we have to // pretend the exception originated elsewhere. EXTERN_C void ThrowControlForThread( @@ -1042,8 +1029,6 @@ class Thread #endif // DACCESS_COMPILE friend class ProfToEEInterfaceImpl; // HRESULT ProfToEEInterfaceImpl::GetHandleFromThread(ThreadID threadId, HANDLE *phThread); - friend void SetupTLSForThread(Thread* pThread); - friend class CheckAsmOffsets; friend class ExceptionTracker; @@ -3210,7 +3195,7 @@ class Thread static BOOL IsAddressInCurrentStack (PTR_VOID addr) { LIMITED_METHOD_DAC_CONTRACT; - Thread* currentThread = GetThread(); + Thread* currentThread = GetThreadNULLOk(); if (currentThread == NULL) { return FALSE; @@ -4185,7 +4170,7 @@ class Thread Thread * const m_pThread; public: AVInRuntimeImplOkayHolder() : - m_pThread(GetThread()) + m_pThread(GetThreadNULLOk()) { LIMITED_METHOD_CONTRACT; AVInRuntimeImplOkayAcquire(m_pThread); @@ -4833,7 +4818,7 @@ class ThreadStore WRAPPER_NO_CONTRACT; // Note that GetThread() may be 0 if it is the debugger thread // or perhaps a concurrent GC thread. - return HoldingThreadStore(GetThread()); + return HoldingThreadStore(GetThreadNULLOk()); } static BOOL HoldingThreadStore(Thread *pThread); @@ -5247,13 +5232,9 @@ class GCHolderBase if (m_WasCoop) { // m_WasCoop is only TRUE if we've already verified there's an EE thread. - BEGIN_GETTHREAD_ALLOWED; - _ASSERTE(m_Thread != NULL); // Cannot switch to cooperative with no thread if (!m_Thread->PreemptiveGCDisabled()) m_Thread->DisablePreemptiveGC(); - - END_GETTHREAD_ALLOWED; } else { @@ -5267,10 +5248,8 @@ class GCHolderBase // when it's TRUE, so we check it for perf. if (THREAD_EXISTS || m_Thread != NULL) { - BEGIN_GETTHREAD_ALLOWED; if (m_Thread->PreemptiveGCDisabled()) m_Thread->EnablePreemptiveGC(); - END_GETTHREAD_ALLOWED; } } @@ -5303,7 +5282,6 @@ class GCHolderBase if (m_Thread != NULL) { - BEGIN_GETTHREAD_ALLOWED; m_WasCoop = m_Thread->PreemptiveGCDisabled(); if (conditional && !m_WasCoop) @@ -5311,7 +5289,6 @@ class GCHolderBase m_Thread->DisablePreemptiveGC(); _ASSERTE(m_Thread->PreemptiveGCDisabled()); } - END_GETTHREAD_ALLOWED; } else { @@ -5330,15 +5307,12 @@ class GCHolderBase m_fThreadMustExist = false; if (m_Thread != NULL && conditional) { - BEGIN_GETTHREAD_ALLOWED; GCHOLDER_CHECK_FOR_PREEMP_IN_NOTRIGGER(m_Thread); - END_GETTHREAD_ALLOWED; } #endif // ENABLE_CONTRACTS_IMPL if (m_Thread != NULL) { - BEGIN_GETTHREAD_ALLOWED; m_WasCoop = m_Thread->PreemptiveGCDisabled(); if (conditional && m_WasCoop) @@ -5346,7 +5320,6 @@ class GCHolderBase m_Thread->EnablePreemptiveGC(); _ASSERTE(!m_Thread->PreemptiveGCDisabled()); } - END_GETTHREAD_ALLOWED; } else { @@ -5359,7 +5332,7 @@ class GCHolderBase { // This is the perf version. So we deliberately restrict the calls // to already setup threads to avoid the null checks and GetThread call - _ASSERTE(pThread && (pThread == GetThread())); + _ASSERTE(pThread == GetThread()); #ifdef ENABLE_CONTRACTS_IMPL m_fThreadMustExist = true; #endif // ENABLE_CONTRACTS_IMPL @@ -5381,7 +5354,7 @@ class GCHolderBase { // This is the perf version. So we deliberately restrict the calls // to already setup threads to avoid the null checks and GetThread call - _ASSERTE(!THREAD_EXISTS || (pThread && (pThread == GetThread()))); + _ASSERTE(!THREAD_EXISTS || (pThread == GetThread())); #ifdef ENABLE_CONTRACTS_IMPL m_fThreadMustExist = !!THREAD_EXISTS; #endif // ENABLE_CONTRACTS_IMPL @@ -5695,7 +5668,7 @@ class AutoCleanupGCAssert FORCEINLINE void DoCheck() { WRAPPER_NO_CONTRACT; - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (COOPERATIVE) { _ASSERTE(pThread != NULL); @@ -6006,7 +5979,7 @@ class FCallGCCanTrigger inline BOOL GC_ON_TRANSITIONS(BOOL val) { WRAPPER_NO_CONTRACT; #ifdef _DEBUG - Thread* thread = GetThread(); + Thread* thread = GetThreadNULLOk(); if (thread == 0) return(FALSE); BOOL ret = thread->m_GCOnTransitionsOK; @@ -6200,7 +6173,6 @@ class DeadlockAwareLock static void ReleaseBlockingLock() { Thread *pThread = GetThread(); - _ASSERTE (pThread); pThread->m_pBlockingLock = NULL; } public: @@ -6237,7 +6209,7 @@ class ThreadStateHolder ThreadStateHolder (BOOL fNeed, DWORD state) { LIMITED_METHOD_CONTRACT; - _ASSERTE (GetThread()); + _ASSERTE (GetThreadNULLOk()); m_fNeed = fNeed; m_state = state; } @@ -6248,7 +6220,6 @@ class ThreadStateHolder if (m_fNeed) { Thread *pThread = GetThread(); - _ASSERTE (pThread); FastInterlockAnd((ULONG *) &pThread->m_State, ~m_state); } } @@ -6271,15 +6242,13 @@ class ThreadStateNCStackHolder { LIMITED_METHOD_CONTRACT; - _ASSERTE (GetThread()); + _ASSERTE (GetThreadNULLOk()); m_fNeed = fNeed; m_state = state; if (fNeed) { Thread *pThread = GetThread(); - _ASSERTE (pThread); - if (fNeed < 0) { // if the state is set, reset it @@ -6315,8 +6284,6 @@ class ThreadStateNCStackHolder if (m_fNeed) { Thread *pThread = GetThread(); - _ASSERTE (pThread); - if (m_fNeed < 0) { pThread->SetThreadStateNC(m_state); // set it diff --git a/src/coreclr/vm/threads.inl b/src/coreclr/vm/threads.inl index 5dc6ef8d00e1c..e6b0595d804a4 100644 --- a/src/coreclr/vm/threads.inl +++ b/src/coreclr/vm/threads.inl @@ -32,11 +32,18 @@ __declspec(selectany) __declspec(thread) ThreadLocalInfo gCurrentThreadInfo; EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo; #endif -EXTERN_C inline Thread* STDCALL GetThread() +inline Thread* GetThreadNULLOk() { return gCurrentThreadInfo.m_pThread; } +inline Thread* GetThread() +{ + Thread* pThread = gCurrentThreadInfo.m_pThread; + _ASSERTE(pThread); + return pThread; +} + EXTERN_C inline AppDomain* STDCALL GetAppDomain() { return AppDomain::GetCurrentDomain(); @@ -173,7 +180,6 @@ inline Thread::CurrentPrepareCodeConfigHolder::CurrentPrepareCodeConfigHolder(Th #endif { LIMITED_METHOD_CONTRACT; - _ASSERTE(thread != nullptr); _ASSERTE(thread == GetThread()); _ASSERTE(config != nullptr); diff --git a/src/coreclr/vm/threadstatics.h b/src/coreclr/vm/threadstatics.h index 14d909fee39f8..1755bf7230d20 100644 --- a/src/coreclr/vm/threadstatics.h +++ b/src/coreclr/vm/threadstatics.h @@ -536,7 +536,6 @@ class ThreadStatics { // Get the current thread Thread * pThread = GetThread(); - return &pThread->m_ThreadLocalBlock; } diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 6514bf8bc15e1..2302e2e4619dc 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -27,10 +27,6 @@ bool ThreadSuspend::s_fSuspended = false; CLREvent* ThreadSuspend::g_pGCSuspendEvent = NULL; ThreadSuspend::SUSPEND_REASON ThreadSuspend::m_suspendReason; -Thread* ThreadSuspend::m_pThreadAttemptingSuspendForGC; - -CLREventBase * ThreadSuspend::s_hAbortEvt = NULL; -CLREventBase * ThreadSuspend::s_hAbortEvtCache = NULL; // If you add any thread redirection function, make sure the debugger can 1) recognize the redirection // function, and 2) retrieve the original CONTEXT. See code:Debugger.InitializeHijackFunctionAddress and @@ -331,7 +327,7 @@ Thread::SuspendThreadResult Thread::SuspendThread(BOOL fOneTryOnly, DWORD *pdwSu } // We suspend the right thread #ifdef _DEBUG - Thread * pCurThread = GetThread(); + Thread * pCurThread = GetThreadNULLOk(); if (pCurThread != NULL) { pCurThread->dbg_m_cSuspendedThreads ++; @@ -445,7 +441,7 @@ DWORD Thread::ResumeThread() _ASSERTE (!m_Creater.IsCurrentThread()); if ((res != (DWORD)-1) && (res != 0)) { - Thread * pCurThread = GetThread(); + Thread * pCurThread = GetThreadNULLOk(); if (pCurThread != NULL) { _ASSERTE(pCurThread->dbg_m_cSuspendedThreads > 0); @@ -496,7 +492,7 @@ static inline BOOL CheckSuspended(Thread *pThread) } CONTRACTL_END; - _ASSERTE(GetThread() != pThread); + _ASSERTE(GetThreadNULLOk() != pThread); _ASSERTE(CheckPointer(pThread)); #ifndef DISABLE_THREADSUSPEND @@ -684,7 +680,7 @@ static StackWalkAction TAStackCrawlCallBackWorker(CrawlFrame* pCf, StackCrawlCon // !!! is to check if the target thread is processing exception. // !!! If exception is in flight, we don't induce ThreadAbort. Instead at the end of Jit_EndCatch // !!! we will handle abort. - if (pData->pAbortee != GetThread() && !IsFaultOrFinally(&EHClause)) + if (pData->pAbortee != GetThreadNULLOk() && !IsFaultOrFinally(&EHClause)) { continue; } @@ -876,7 +872,6 @@ BOOL Thread::IsExecutingWithinCer() return FALSE; Thread *pThread = GetThread(); - _ASSERTE (pThread); StackCrawlContext sContext = { pThread, StackCrawlContext::SCC_CheckWithinCer, FALSE, @@ -965,7 +960,7 @@ BOOL Thread::ReadyForAsyncException() return FALSE; } - if (GetThread() == this && HasThreadStateNC (TSNC_PreparingAbort) && !IsRudeAbort() ) + if (GetThreadNULLOk() == this && HasThreadStateNC (TSNC_PreparingAbort) && !IsRudeAbort() ) { STRESS_LOG0(LF_APPDOMAIN, LL_INFO10, "in Thread::ReadyForAbort PreparingAbort\n"); // Avoid recursive call @@ -1055,7 +1050,7 @@ BOOL Thread::ReadyForAsyncException() StackWalkFramesEx(&rd, TAStackCrawlCallBack, &TAContext, QUICKUNWIND, pStartFrame); - _ASSERTE(TAContext.fHasManagedCodeOnStack || !IsAbortInitiated() || (GetThread() != this)); + _ASSERTE(TAContext.fHasManagedCodeOnStack || !IsAbortInitiated() || (GetThreadNULLOk() != this)); if (TAContext.fWithinCer) { @@ -1193,7 +1188,7 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout) CONTRACTL { THROWS; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -1214,7 +1209,7 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout) MarkThreadForAbort(abortType); - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); // If aborting self if (this == pCurThread) @@ -1840,7 +1835,7 @@ void ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_REASON reason) CONTRACTL { NOTHROW; // any thread entering with `PreemptiveGCDisabled` should be prepared to switch mode, thus GC_TRIGGERS - if ((GetThread() != NULL) && GetThread()->PreemptiveGCDisabled()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if ((GetThreadNULLOk() != NULL) && GetThread()->PreemptiveGCDisabled()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -1853,14 +1848,12 @@ void ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_REASON reason) { BOOL gcOnTransitions; - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); gcOnTransitions = GC_ON_TRANSITIONS(FALSE); // dont do GC for GCStress 3 - // There are two ways to get blocked here: - // 1) we may be blocked on s_pThreadStore - // 2) threads with reasons other than GC, GC_PREP, and DEBUGGER_SWEEP may also be blocked on s_hAbortEvt - // In either case we should be in preemptive mode when blocked or we could cause suspension in progress + // We may be blocked while acquiring s_pThreadStore + // We should be in preemptive mode when blocked or we could cause suspension in progress // to loop forever because it needs to see all threads in preemptive mode. BOOL toggleGC = (pCurThread != NULL && pCurThread->PreemptiveGCDisabled()); if (toggleGC) @@ -1874,27 +1867,6 @@ void ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_REASON reason) if (pCurThread) IncCantStopCount(); - // This is used to avoid GC starvation if non-GC threads are competing for - // the thread store lock when there is a real GC-thread waiting to get in. - // This is initialized lazily when the first non-GC thread backs out because of - // a waiting GC thread. - if (s_hAbortEvt != NULL && - !(reason == ThreadSuspend::SUSPEND_FOR_GC || - reason == ThreadSuspend::SUSPEND_FOR_GC_PREP || - reason == ThreadSuspend::SUSPEND_FOR_DEBUGGER_SWEEP) && - m_pThreadAttemptingSuspendForGC != NULL && - m_pThreadAttemptingSuspendForGC != pCurThread) - { - CLREventBase * hAbortEvt = s_hAbortEvt; - - if (hAbortEvt != NULL) - { - LOG((LF_SYNC, INFO3, "Performing suspend abort wait.\n")); - hAbortEvt->Wait(INFINITE, FALSE); - LOG((LF_SYNC, INFO3, "Release from suspend abort wait.\n")); - } - } - // This is shutdown aware. If we're in shutdown, and not helper/finalizer/shutdown // then this will not take the lock and just block forever. ThreadStore::s_pThreadStore->Enter(); @@ -1934,14 +1906,14 @@ void ThreadSuspend::UnlockThreadStore(BOOL bThreadDestroyed, ThreadSuspend::SUSP // 10 of our COM BVTs. if (!IsAtProcessExit()) { - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); LOG((LF_SYNC, INFO3, "Unlocking thread store\n")); - _ASSERTE(GetThread() == NULL || ThreadStore::s_pThreadStore->m_HoldingThread == GetThread()); + _ASSERTE(pCurThread == NULL || ThreadStore::s_pThreadStore->m_HoldingThread == pCurThread); #ifdef _DEBUG // If Thread object has been destroyed, we need to reset the ownership info in Crst. - _ASSERTE(!bThreadDestroyed || GetThread() == NULL); + _ASSERTE(!bThreadDestroyed || pCurThread == NULL); if (bThreadDestroyed) { ThreadStore::s_pThreadStore->m_Crst.m_holderthreadid.SetToCurrentThread(); } @@ -2556,8 +2528,6 @@ int RedirectedHandledJITCaseExceptionFilter( // Get the thread handle Thread *pThread = GetThread(); - _ASSERTE(pThread); - STRESS_LOG2(LF_SYNC, LL_INFO100, "In RedirectedHandledJITCaseExceptionFilter fDone = %d pFrame = %p\n", fDone, pFrame); @@ -2668,7 +2638,6 @@ void __stdcall Thread::RedirectedHandledJITCase(RedirectReason reason) DWORD dwLastError = GetLastError(); // BEGIN_PRESERVE_LAST_ERROR Thread *pThread = GetThread(); - _ASSERTE(pThread); // Get the saved context CONTEXT *pCtx = pThread->GetSavedRedirectContext(); @@ -2900,7 +2869,7 @@ BOOL Thread::RedirectThreadAtHandledJITCase(PFN_REDIRECTTARGET pTgt) CONTRACTL_END; _ASSERTE(HandledJITCase()); - _ASSERTE(GetThread() != this); + _ASSERTE(GetThreadNULLOk() != this); _ASSERTE(ThreadStore::HoldingThreadStore()); //////////////////////////////////////////////////////////////// @@ -2997,7 +2966,7 @@ BOOL Thread::CheckForAndDoRedirect(PFN_REDIRECTTARGET pRedirectTarget) } CONTRACTL_END; - _ASSERTE(this != GetThread()); + _ASSERTE(this != GetThreadNULLOk()); _ASSERTE(PreemptiveGCDisabledOther()); _ASSERTE(IsAddrOfRedirectFunc(pRedirectTarget)); @@ -3227,11 +3196,11 @@ COR_PRF_SUSPEND_REASON GCSuspendReasonToProfSuspendReason(ThreadSuspend::SUSPEND // which leaves cooperative mode and waits for the GC to complete. // // See code:Thread#SuspendingTheRuntime for more -HRESULT ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) +void ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) { CONTRACTL { NOTHROW; - if (GetThread()) + if (GetThreadNULLOk()) { GC_TRIGGERS; // CLREvent::Wait is GC_TRIGGERS } @@ -3243,7 +3212,7 @@ HRESULT ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) CONTRACTL_END; // This thread - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); // Caller is expected to be holding the ThreadStore lock. Also, caller must // have set GcInProgress before coming here, or things will break; @@ -3276,26 +3245,6 @@ HRESULT ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) } #endif // PROFILING_SUPPORTED - // If m_pThreadAttemptingSuspendForGC is set, it means: - // 1) someone is trying to suspend for GC and - // 2) it is not us. - // In such case, before doing much work, we back off with ERROR_TIMEOUT and will try again later - // This should be rare, but if too many non-GC suspensions are trying to happen, GC could starve. - if (m_pThreadAttemptingSuspendForGC != NULL) - { -#ifdef PROFILING_SUPPORTED - // Must let the profiler know that this thread is aborting its attempt at suspending - { - BEGIN_PIN_PROFILER(CORProfilerTrackSuspends()); - g_profControlBlock.pProfInterface->RuntimeSuspendAborted(); - END_PIN_PROFILER(); - } -#endif // PROFILING_SUPPORTED - - STRESS_LOG0(LF_SYNC, LL_ALWAYS, "Thread::SuspendRuntime() - Yielding to GC.\n"); - return (ERROR_TIMEOUT); - } - // // If this thread is running at low priority, boost its priority. We remember the old // priority so that we can restore it in ResumeRuntime. @@ -3674,7 +3623,6 @@ HRESULT ThreadSuspend::SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason) #endif // HAVE_GCCOVER STRESS_LOG0(LF_SYNC, LL_INFO1000, "Thread::SuspendRuntime() - Success\n"); - return S_OK; } #ifdef HAVE_GCCOVER @@ -3743,11 +3691,11 @@ void ThreadSuspend::ResumeRuntime(BOOL bFinishedGC, BOOL SuspendSucceded) { CONTRACTL { NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); // Caller is expected to be holding the ThreadStore lock. But they must have // reset GcInProgress, or threads will continue to suspend themselves and won't @@ -3839,8 +3787,6 @@ int RedirectedThrowControlExceptionFilter( // Get the thread handle Thread *pThread = GetThread(); - _ASSERTE(pThread); - STRESS_LOG0(LF_SYNC, LL_INFO100, "In RedirectedThrowControlExceptionFilter\n"); @@ -3897,7 +3843,6 @@ ThrowControlForThread( STATIC_CONTRACT_GC_NOTRIGGER; Thread *pThread = GetThread(); - _ASSERTE(pThread); _ASSERTE(pThread->m_OSContext); _ASSERTE(pThread->PreemptiveGCDisabled()); @@ -4110,7 +4055,7 @@ bool Thread::SysStartSuspendForDebug(AppDomain *pAppDomain) } CONTRACTL_END; - Thread *pCurThread = GetThread(); + Thread *pCurThread = GetThreadNULLOk(); Thread *thread = NULL; if (IsAtProcessExit()) @@ -4322,7 +4267,7 @@ bool Thread::SysSweepThreadsForDebug(bool forceSync) // We assume that only the "real" helper thread ever calls this (not somebody doing helper thread duty). PRECONDITION(ThreadStore::HoldingThreadStore()); PRECONDITION(IsDbgHelperSpecialThread()); - PRECONDITION(GetThread() == NULL); + PRECONDITION(GetThreadNULLOk() == NULL); // Iff we return true, then we have the TSL (or the aux lock used in workarounds). POSTCONDITION(ThreadStore::HoldingThreadStore()); @@ -5383,7 +5328,7 @@ BOOL Thread::HandledJITCase() #ifdef _DEBUG // We know IP is in managed code, mark current thread as safe for calls into host - Thread * pCurThread = GetThread(); + Thread * pCurThread = GetThreadNULLOk(); if (pCurThread != NULL) { pCurThread->dbg_m_cSuspendedThreadsWithoutOSLock ++; @@ -5624,7 +5569,7 @@ void ThreadSuspend::SuspendEE(SUSPEND_REASON reason) ETW::GCLog::ETW_GC_INFO Info; Info.SuspendEE.Reason = reason; Info.SuspendEE.GcCount = (((reason == SUSPEND_FOR_GC) || (reason == SUSPEND_FOR_GC_PREP)) ? - (ULONG)GCHeapUtilities::GetGCHeap()->GetGcCount() : (ULONG)-1); + (ULONG)GCHeapUtilities::GetGCHeap()->GetGcCount() : (ULONG)-1); FireEtwGCSuspendEEBegin_V1(Info.SuspendEE.Reason, Info.SuspendEE.GcCount, GetClrInstanceId()); @@ -5632,24 +5577,12 @@ void ThreadSuspend::SuspendEE(SUSPEND_REASON reason) gcOnTransitions = GC_ON_TRANSITIONS(FALSE); // dont do GC for GCStress 3 - Thread* pCurThread = GetThread(); + Thread* pCurThread = GetThreadNULLOk(); DWORD dwSwitchCount = 0; - // Note: we need to make sure to re-set m_pThreadAttemptingSuspendForGC when we retry - // due to the debugger case below! retry_for_debugger: - // - // Set variable to indicate that this thread is preforming a true GC - // This gives this thread priority over other threads that are trying to acquire the ThreadStore Lock - // for other reasons. - // - if (reason == ThreadSuspend::SUSPEND_FOR_GC || reason == ThreadSuspend::SUSPEND_FOR_GC_PREP) - { - m_pThreadAttemptingSuspendForGC = pCurThread; - } - #ifdef TIME_SUSPEND DWORD startAcquire = g_SuspendStatistics.GetTime(); #endif @@ -5661,188 +5594,103 @@ void ThreadSuspend::SuspendEE(SUSPEND_REASON reason) #ifdef TIME_SUSPEND g_SuspendStatistics.acquireTSL.Accumulate(SuspendStatistics::GetElapsed(startAcquire, - g_SuspendStatistics.GetTime())); + g_SuspendStatistics.GetTime())); #endif // - // If we've blocked other threads that are waiting for the ThreadStore lock, unblock them now - // (since we already got it). This allows them to get the TSL after we release it. + // Now we're going to acquire an exclusive lock on managed code execution (including + // "manually managed" code in GCX_COOP regions). // - if ( s_hAbortEvtCache != NULL && - (reason == ThreadSuspend::SUSPEND_FOR_GC || reason == ThreadSuspend::SUSPEND_FOR_GC_PREP)) - { - LOG((LF_SYNC, INFO3, "GC thread is backing out the suspend abort event.\n")); - s_hAbortEvt = NULL; + // First, we reset the event that we're about to tell other threads to wait for. + // + GCHeapUtilities::GetGCHeap()->ResetWaitForGCEvent(); - LOG((LF_SYNC, INFO3, "GC thread is signalling the suspend abort event.\n")); - s_hAbortEvtCache->Set(); - } + // + // Remember that we're the one suspending the EE + // + g_pSuspensionThread = pCurThread; // - // Also null-out m_pThreadAttemptingSuspendForGC since it should only matter if s_hAbortEvt is - // in play. + // Tell all threads, globally, to wait for WaitForGCEvent. // - if (reason == ThreadSuspend::SUSPEND_FOR_GC || reason == ThreadSuspend::SUSPEND_FOR_GC_PREP) - { - m_pThreadAttemptingSuspendForGC = NULL; - } + ThreadStore::TrapReturningThreads(TRUE); - { - // - // Now we're going to acquire an exclusive lock on managed code execution (including - // "manually managed" code in GCX_COOP regions). - // - // First, we reset the event that we're about to tell other threads to wait for. - // - GCHeapUtilities::GetGCHeap()->ResetWaitForGCEvent(); + // + // Remember why we're doing this. + // + m_suspendReason = reason; + + // + // There's a GC in progress. (again, not necessarily - we suspend the EE for other reasons. + // I wonder how much confusion this has caused....) + // It seems like much of the above is redundant. We should investigate reducing the number + // of mechanisms we use to indicate that a suspension is in progress. + GCHeapUtilities::GetGCHeap()->SetGCInProgress(true); + + // set tls flags for compat with SOS + ClrFlsSetThreadType(ThreadType_DynamicSuspendEE); + + _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); + // + // Now that we've instructed all threads to please stop, + // go interrupt the ones that are running managed code and force them to stop. + // This does not return until all threads have acknowledged that they + // will not run managed code. + // + SuspendRuntime(reason); + +#ifdef DEBUGGING_SUPPORTED + // If the debugging services are attached, then its possible + // that there is a thread which appears to be stopped at a gc + // safe point, but which really is not. If that is the case, + // back off and try again. + if ((CORDebuggerAttached() && g_pDebugInterface->ThreadsAtUnsafePlaces())) + { + // In this case, the debugger has stopped at least one + // thread at an unsafe place. The debugger will usually + // have already requested that we stop. If not, it will + // usually either do so shortly, or resume the thread that is + // at the unsafe place. Either way, we have to wait for the + // debugger to decide what it wants to do. // - // Remember that we're the one doing the GC. Actually, maybe we're not doing a GC - - // what this really indicates is that we are trying to acquire the "managed execution lock." + // In some rare cases, the end-user debugger may have frozen + // a thread at a gc-unsafe place, and so we'll loop forever + // here and never resolve the deadlock. Unfortunately we can't + // easily abort a GC + // and so for now we just wait for the debugger to timeout and + // hopefully thaw that thread. Maybe instead we should try to + // detect this situation sooner (when thread abort is possible) + // and notify the debugger with NotifyOfCrossThreadDependency, giving + // it the chance to thaw other threads or abort us before getting + // wedged in the GC. // - { - g_pSuspensionThread = pCurThread; + // Note: we've still got the ThreadStore lock held. + // + LOG((LF_GCROOTS | LF_GC | LF_CORDB, + LL_INFO10, + "***** Giving up on current GC suspension due to debugger *****\n")); - // - // Tell all threads, globally, to wait for WaitForGCEvent. - // - ThreadStore::TrapReturningThreads(TRUE); + // Mark that we're done with the gc, so that the debugger can proceed. + RestartEE(FALSE, FALSE); - // - // Remember why we're doing this. - // - m_suspendReason = reason; + LOG((LF_GCROOTS | LF_GC | LF_CORDB, LL_INFO10, "The EE is free now...\n")); - // - // There's a GC in progress. (again, not necessarily - we suspend the EE for other reasons. - // I wonder how much confusion this has caused....) - // It seems like much of the above is redundant. We should investigate reducing the number - // of mechanisms we use to indicate that a suspension is in progress. - GCHeapUtilities::GetGCHeap()->SetGCInProgress(true); - - // set tls flags for compat with SOS - ClrFlsSetThreadType (ThreadType_DynamicSuspendEE); + // If someone's trying to suspent *this* thread, this is a good opportunity. + if (pCurThread && pCurThread->CatchAtSafePointOpportunistic()) + { + pCurThread->PulseGCMode(); // Go suspend myself. } - - HRESULT hr; + else { - _ASSERTE(ThreadStore::HoldingThreadStore() || g_fProcessDetach); - - // - // Now that we've instructed all threads to please stop, - // go interrupt the ones that are running managed code and force them to stop. - // This does not return successfully until all threads have acknowledged that they - // will not run managed code. - // - hr = SuspendRuntime(reason); - ASSERT( hr == S_OK || hr == ERROR_TIMEOUT); - -#ifdef TIME_SUSPEND - if (hr == ERROR_TIMEOUT) - g_SuspendStatistics.cntCollideRetry++; -#endif + // otherwise, just yield so the debugger can finish what it's doing. + __SwitchToThread(0, ++dwSwitchCount); } - if (hr == ERROR_TIMEOUT) - STRESS_LOG0(LF_SYNC, LL_INFO1000, "SysSuspension colission"); - - // If this is not the GC thread and another thread has triggered - // a GC, then we yield by resuming all the threads and trying again. - if ((hr == ERROR_TIMEOUT) -#ifdef DEBUGGING_SUPPORTED - // If the debugging services are attached, then its possible - // that there is a thread which appears to be stopped at a gc - // safe point, but which really is not. If that is the case, - // back off and try again. - || (CORDebuggerAttached() && g_pDebugInterface->ThreadsAtUnsafePlaces()) + goto retry_for_debugger; + } #endif // DEBUGGING_SUPPORTED - ) - { - // In this case, the debugger has stopped at least one - // thread at an unsafe place. The debugger will usually - // have already requested that we stop. If not, it will - // usually either do so shortly, or resume the thread that is - // at the unsafe place. Either way, we have to wait for the - // debugger to decide what it wants to do. - // - // In some rare cases, the end-user debugger may have frozen - // a thread at a gc-unsafe place, and so we'll loop forever - // here and never resolve the deadlock. Unfortunately we can't - // easily abort a GC - // and so for now we just wait for the debugger to timeout and - // hopefully thaw that thread. Maybe instead we should try to - // detect this situation sooner (when thread abort is possible) - // and notify the debugger with NotifyOfCrossThreadDependency, giving - // it the chance to thaw other threads or abort us before getting - // wedged in the GC. - // - // Note: we've still got the ThreadStore lock held. - // - // The below manipulation of two static variables (s_hAbortEvtCache and s_hAbortEvt) - // is protected by the ThreadStore lock, which we are still holding. But we access these - // in ThreadSuspend::LockThreadStore, prior to obtaining the lock. - // - LOG((LF_GCROOTS | LF_GC | LF_CORDB, - LL_INFO10, - "***** Giving up on current GC suspension due " - "to debugger or yielding to a GC suspension *****\n")); - if (s_hAbortEvtCache == NULL) - { - LOG((LF_SYNC, INFO3, "Creating suspend abort event.\n")); - - CLREvent * pEvent = NULL; - - EX_TRY - { - pEvent = new CLREvent(); - pEvent->CreateManualEvent(FALSE); - s_hAbortEvtCache = pEvent; - } - EX_CATCH - { - // Couldn't init the abort event. Its a shame, but not fatal. We'll simply not use it - // on this iteration and try again next time. - if (pEvent) { - _ASSERTE(!pEvent->IsValid()); - pEvent->CloseEvent(); - delete pEvent; - } - } - EX_END_CATCH(SwallowAllExceptions) - } - - if (s_hAbortEvtCache != NULL) - { - LOG((LF_SYNC, INFO3, "Using suspend abort event.\n")); - s_hAbortEvt = s_hAbortEvtCache; - s_hAbortEvt->Reset(); - } - - // Mark that we're done with the gc, so that the debugger can proceed. - RestartEE(FALSE, FALSE); - - LOG((LF_GCROOTS | LF_GC | LF_CORDB, - LL_INFO10, "The EE is free now...\n")); - - // If someone's trying to suspent *this* thread, this is a good opportunity. - // This call to CatchAtSafePoint is redundant - PulseGCMode already checks this. - if (pCurThread && pCurThread->CatchAtSafePoint()) - { - // This assert is fired on BGC thread 'cause we - // got timeout. - //_ASSERTE((pCurThread->PreemptiveGCDisabled()) || IsGCSpecialThread()); - pCurThread->PulseGCMode(); // Go suspend myself. - } - else - { - // otherwise, just yield so the debugger can finish what it's doing. - __SwitchToThread (0, ++dwSwitchCount); - } - - goto retry_for_debugger; - } - } GC_ON_TRANSITIONS(gcOnTransitions); FireEtwGCSuspendEEEnd_V1(GetClrInstanceId()); @@ -5869,7 +5717,7 @@ void ThreadSuspend::SuspendEE(SUSPEND_REASON reason) // is in a function where we can safely inject activation. BOOL CheckActivationSafePoint(SIZE_T ip, BOOL checkingCurrentThread) { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); // It is safe to call the ExecutionManager::IsManagedCode only if we are making the check for // a thread different from the current one or if the current thread is in the cooperative mode. // Otherwise ExecutionManager::IsManagedCode could deadlock if the activation happened when the @@ -6017,7 +5865,7 @@ BOOL Debug_IsLockedViaThreadSuspension() return GCHeapUtilities::IsGCInProgress() && (dbgOnly_IsSpecialEEThread() || IsGCSpecialThread() || - GetThread() == ThreadSuspend::GetSuspensionThread()); + GetThreadNULLOk() == ThreadSuspend::GetSuspensionThread()); } #endif @@ -6220,9 +6068,8 @@ void SuspendStatistics::DisplayAndUpdate() cntWaitTimeouts - g_LastSuspendStatistics.cntWaitTimeouts, cntWaitTimeouts, cntHijackTrap - g_LastSuspendStatistics.cntHijackTrap, cntHijackTrap); - fprintf(logFile, "Redirected EIP Failures %d (%d), Collided GC/Debugger %d (%d)\n", - cntFailedRedirections - g_LastSuspendStatistics.cntFailedRedirections, cntFailedRedirections, - cntCollideRetry - g_LastSuspendStatistics.cntCollideRetry, cntCollideRetry); + fprintf(logFile, "Redirected EIP Failures %d (%d)\n", + cntFailedRedirections - g_LastSuspendStatistics.cntFailedRedirections, cntFailedRedirections); fprintf(logFile, "Suspend: All %d (%d). NonGC: %d (%d). InBGC: %d (%d). NonGCInBGC: %d (%d)\n\n", cntSuspends - g_LastSuspendStatistics.cntSuspends, cntSuspends, diff --git a/src/coreclr/vm/threadsuspend.h b/src/coreclr/vm/threadsuspend.h index cff7556ea16ac..f36b39f4f7f7a 100644 --- a/src/coreclr/vm/threadsuspend.h +++ b/src/coreclr/vm/threadsuspend.h @@ -130,10 +130,6 @@ struct SuspendStatistics /////////////////////////////////////////////////////////////////////////////////////////////// // And there are some "failure" cases that should never or almost never occur. - // number of times we have a collision between e.g. Debugger suspension & GC suspension. - // In these cases, everyone yields to the GC but at some cost. - int cntCollideRetry; - // number of times the OS or Host was unable to ::SuspendThread a thread for us. This count should be // approximately 0. int cntFailedSuspends; @@ -187,15 +183,13 @@ class ThreadSuspend } SUSPEND_REASON; private: - static SUSPEND_REASON m_suspendReason; // This contains the reason - // that the runtime was suspended - static Thread* m_pThreadAttemptingSuspendForGC; + static SUSPEND_REASON m_suspendReason; // This contains the reason why the runtime is suspended - static HRESULT SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason); - static void ResumeRuntime(BOOL bFinishedGC, BOOL SuspendSucceded); + static void SuspendRuntime(ThreadSuspend::SUSPEND_REASON reason); + static void ResumeRuntime(BOOL bFinishedGC, BOOL SuspendSucceded); public: // Initialize thread suspension support - static void Initialize(); + static void Initialize(); private: static CLREvent * g_pGCSuspendEvent; @@ -254,14 +248,6 @@ class ThreadSuspend } private: - // This is used to avoid thread starvation if non-GC threads are competing for - // the thread store lock when there is a real GC-thread waiting to get in. - // This is initialized lazily when the first non-GC thread backs out because of - // a waiting GC thread. The s_hAbortEvtCache is used to store the handle when - // it is not being used. - static CLREventBase *s_hAbortEvt; - static CLREventBase *s_hAbortEvtCache; - static LONG m_DebugWillSyncCount; }; diff --git a/src/coreclr/vm/util.cpp b/src/coreclr/vm/util.cpp index 9cfe20dc82830..64130d699ea93 100644 --- a/src/coreclr/vm/util.cpp +++ b/src/coreclr/vm/util.cpp @@ -2098,7 +2098,7 @@ int GetRandomInt(int maxVal) { #ifndef CROSSGEN_COMPILE // Use the thread-local Random instance if possible - Thread* pThread = GetThread(); + Thread* pThread = GetThreadNULLOk(); if (pThread) return pThread->GetRandom()->Next(maxVal); #endif diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp index 3fdc90ebf0511..5004599bc779c 100644 --- a/src/coreclr/vm/vars.cpp +++ b/src/coreclr/vm/vars.cpp @@ -180,6 +180,10 @@ bool g_fEEInit = false; // code:IsAtProcessExit to read this. GVAL_IMPL(bool, g_fProcessDetach); +#ifdef EnC_SUPPORTED +GVAL_IMPL_INIT(bool, g_metadataUpdatesApplied, false); +#endif + GVAL_IMPL_INIT(DWORD, g_fEEShutDown, 0); #ifndef TARGET_UNIX diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp index 5c287716ff398..9400fd130132f 100644 --- a/src/coreclr/vm/vars.hpp +++ b/src/coreclr/vm/vars.hpp @@ -472,6 +472,9 @@ EXTERN DWORD g_fFastExitProcess; EXTERN BOOL g_fFatalErrorOccurredOnGCThread; EXTERN Volatile g_fForbidEnterEE; GVAL_DECL(bool, g_fProcessDetach); +#ifdef EnC_SUPPORTED +GVAL_DECL(bool, g_metadataUpdatesApplied); +#endif EXTERN bool g_fManagedAttach; EXTERN bool g_fNoExceptions; diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index f0aa1db4accf2..878e67285685d 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -4011,8 +4011,7 @@ VirtualCallStubManager *VirtualCallStubManagerManager::FindVirtualCallStubManage // Check the current and shared domains. { - Thread *pThread = GetThread(); - + Thread *pThread = GetThreadNULLOk(); if (pThread != NULL) { // Check the current domain diff --git a/src/coreclr/vm/wellknownattributes.h b/src/coreclr/vm/wellknownattributes.h index 9c3bce5cf456b..1ab118e300a7e 100644 --- a/src/coreclr/vm/wellknownattributes.h +++ b/src/coreclr/vm/wellknownattributes.h @@ -8,7 +8,6 @@ enum class WellKnownAttribute : DWORD { ParamArray, DefaultMember, - DisablePrivateReflectionType, FixedAddressValueType, UnsafeValueType, BestFitMapping, @@ -48,8 +47,6 @@ inline const char *GetWellKnownAttributeName(WellKnownAttribute attribute) return "System.ParamArrayAttribute"; case WellKnownAttribute::DefaultMember: return "System.Reflection.DefaultMemberAttribute"; - case WellKnownAttribute::DisablePrivateReflectionType: - return "System.Runtime.CompilerServices.DisablePrivateReflectionAttribute"; case WellKnownAttribute::FixedAddressValueType: return "System.Runtime.CompilerServices.FixedAddressValueTypeAttribute"; case WellKnownAttribute::UnsafeValueType: diff --git a/src/coreclr/vm/win32threadpool.cpp b/src/coreclr/vm/win32threadpool.cpp index 48e5253ff7f2f..4619c74d8c98b 100644 --- a/src/coreclr/vm/win32threadpool.cpp +++ b/src/coreclr/vm/win32threadpool.cpp @@ -790,7 +790,7 @@ void QueueUserWorkItemHelp(LPTHREAD_START_ROUTINE Function, PVOID Context) Function(Context); - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread) { _ASSERTE(!pThread->IsAbortRequested()); @@ -990,7 +990,7 @@ void ThreadpoolMgr::AdjustMaxWorkersActive() CONTRACTL { NOTHROW; - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; } CONTRACTL_END; @@ -1075,7 +1075,7 @@ void ThreadpoolMgr::MaybeAddWorkingWorker() CONTRACTL { NOTHROW; - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; } CONTRACTL_END; @@ -1232,7 +1232,7 @@ void WINAPI ThreadpoolMgr::ManagedWaitIOCompletionCallback( DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) { - Thread *pThread = GetThread(); + Thread *pThread = GetThreadNULLOk(); if (pThread == NULL) { ClrFlsSetThreadType(ThreadType_Threadpool_Worker); @@ -1535,8 +1535,6 @@ BOOL ThreadpoolMgr::SetAppDomainRequestsActive(BOOL UnmanagedTP) else { Thread* pCurThread = GetThread(); - _ASSERTE( pCurThread); - AppDomain* pAppDomain = pCurThread->GetDomain(); _ASSERTE(pAppDomain); @@ -1586,8 +1584,6 @@ void ThreadpoolMgr::ClearAppDomainRequestsActive(BOOL UnmanagedTP, LONG id) else { Thread* pCurThread = GetThread(); - _ASSERTE( pCurThread); - AppDomain* pAppDomain = pCurThread->GetDomain(); _ASSERTE(pAppDomain); @@ -1688,7 +1684,7 @@ void ThreadpoolMgr::RecycleMemory(LPVOID mem, enum MemType memType) Thread* ThreadpoolMgr::CreateUnimpersonatedThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpArgs, BOOL *pIsCLRThread) { STATIC_CONTRACT_NOTHROW; - if (GetThread()) { STATIC_CONTRACT_GC_TRIGGERS;} else {DISABLED(STATIC_CONTRACT_GC_NOTRIGGER);} + if (GetThreadNULLOk()) { STATIC_CONTRACT_GC_TRIGGERS;} else {DISABLED(STATIC_CONTRACT_GC_NOTRIGGER);} STATIC_CONTRACT_MODE_ANY; /* cannot use contract because of SEH CONTRACTL @@ -1770,7 +1766,7 @@ BOOL ThreadpoolMgr::CreateWorkerThread() { CONTRACTL { - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} NOTHROW; MODE_ANY; // We may try to add a worker thread while queuing a work item thru an fcall } @@ -1935,7 +1931,7 @@ DWORD WINAPI ThreadpoolMgr::WorkerThreadStart(LPVOID lpArgs) { // Reset TLS etc. for next WorkRequest. if (pThread == NULL) - pThread = GetThread(); + pThread = GetThreadNULLOk(); if (pThread) { @@ -2164,7 +2160,7 @@ BOOL ThreadpoolMgr::RegisterWaitForSingleObject(PHANDLE phNewWaitObject, { THROWS; MODE_ANY; - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -2744,8 +2740,7 @@ DWORD WINAPI ThreadpoolMgr::AsyncCallbackCompletion(PVOID pArgs) } CONTRACTL_END; - Thread * pThread = GetThread(); - + Thread * pThread = GetThreadNULLOk(); if (pThread == NULL) { HRESULT hr = ERROR_SUCCESS; @@ -2860,7 +2855,7 @@ void ThreadpoolMgr::DeleteWait(WaitInfo* waitInfo) { if (waitInfo->ExternalEventSafeHandle != NULL) { THROWS;} else { NOTHROW; } MODE_ANY; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -2905,7 +2900,7 @@ BOOL ThreadpoolMgr::UnregisterWaitEx(HANDLE hWaitObject,HANDLE Event) CONTRACTL { THROWS; //NOTHROW; - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; } CONTRACTL_END; @@ -3070,7 +3065,7 @@ BOOL ThreadpoolMgr::BindIoCompletionCallback(HANDLE FileHandle, CONTRACTL { THROWS; // EnsureInitialized can throw - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; } CONTRACTL_END; @@ -3114,7 +3109,7 @@ BOOL ThreadpoolMgr::CreateCompletionPortThread(LPVOID lpArgs) CONTRACTL { NOTHROW; - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} MODE_ANY; } CONTRACTL_END; @@ -3153,8 +3148,8 @@ DWORD WINAPI ThreadpoolMgr::CompletionPortThreadStart(LPVOID lpArgs) CONTRACTL { THROWS; - if (GetThread()) { MODE_PREEMPTIVE;} else { DISABLED(MODE_ANY);} - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { MODE_PREEMPTIVE;} else { DISABLED(MODE_ANY);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; @@ -3268,12 +3263,11 @@ DWORD WINAPI ThreadpoolMgr::CompletionPortThreadStart(LPVOID lpArgs) if (pThread == NULL) { - pThread = GetThread(); + pThread = GetThreadNULLOk(); } if (pThread) { - context = (PIOCompletionContext) pThread->GetIOCompletionContext(); if (context->lpOverlapped != NULL) @@ -3510,7 +3504,7 @@ DWORD WINAPI ThreadpoolMgr::CompletionPortThreadStart(LPVOID lpArgs) if (pThread == NULL) { - pThread = GetThread(); + pThread = GetThreadNULLOk(); } if (pThread) @@ -3713,7 +3707,7 @@ void ThreadpoolMgr::GrowCompletionPortThreadpoolIfNeeded() { CONTRACTL { - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} NOTHROW; MODE_ANY; } @@ -4326,7 +4320,7 @@ BOOL ThreadpoolMgr::CreateTimerQueueTimer(PHANDLE phNewTimer, CONTRACTL { THROWS; // EnsureInitialized, CreateAutoEvent can throw - if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} // There can be calls thru ICorThreadpool + if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} // There can be calls thru ICorThreadpool MODE_ANY; INJECT_FAULT(COMPlusThrowOM()); } @@ -4561,8 +4555,8 @@ DWORD ThreadpoolMgr::FireTimers() CONTRACTL { THROWS; // QueueUserWorkItem can throw - if (GetThread()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} - if (GetThread()) { MODE_PREEMPTIVE;} else { DISABLED(MODE_ANY);} + if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + if (GetThreadNULLOk()) { MODE_PREEMPTIVE;} else { DISABLED(MODE_ANY);} } CONTRACTL_END; @@ -4662,8 +4656,7 @@ DWORD WINAPI ThreadpoolMgr::AsyncTimerCallbackCompletion(PVOID pArgs) } CONTRACTL_END; - Thread* pThread = GetThread(); - + Thread* pThread = GetThreadNULLOk(); if (pThread == NULL) { HRESULT hr = ERROR_SUCCESS; @@ -4715,8 +4708,7 @@ DWORD WINAPI ThreadpoolMgr::AsyncDeleteTimer(PVOID pArgs) } CONTRACTL_END; - Thread * pThread = GetThread(); - + Thread * pThread = GetThreadNULLOk(); if (pThread == NULL) { HRESULT hr = ERROR_SUCCESS; @@ -4739,7 +4731,7 @@ void ThreadpoolMgr::DeleteTimer(TimerInfo* timerInfo) { CONTRACTL { - if (GetThread() == pTimerThread) { NOTHROW; } else { THROWS; } + if (GetThreadNULLOk() == pTimerThread) { NOTHROW; } else { THROWS; } GC_TRIGGERS; MODE_ANY; } @@ -4767,7 +4759,7 @@ void ThreadpoolMgr::DeleteTimer(TimerInfo* timerInfo) } // We cannot block the timer thread, so some cleanup is deferred to other threads. - if (GetThread() == pTimerThread) + if (GetThreadNULLOk() == pTimerThread) { // Notify the ExternalEventSafeHandle with an user work item if (timerInfo->ExternalEventSafeHandle != NULL) @@ -4842,7 +4834,6 @@ void ThreadpoolMgr::QueueTimerInfoForRelease(TimerInfo *pTimerInfo) // - This function wont go into an alertable state. That could trigger another APC. // Else two threads can be queueing timerinfos and a race could // lead to leaked memory and handles - _ASSERTE(GetThread()); _ASSERTE(pTimerThread == GetThread()); TimerInfo *pHead = NULL; @@ -5045,7 +5036,7 @@ BOOL ThreadpoolMgr::DeleteTimerQueueTimer( { _ASSERTE(timerInfo->ExternalEventSafeHandle == NULL); _ASSERTE(timerInfo->ExternalCompletionEvent == INVALID_HANDLE); - _ASSERTE(GetThread() != pTimerThread); + _ASSERTE(GetThreadNULLOk() != pTimerThread); timerInfo->InternalCompletionEvent.Wait(INFINITE,TRUE /*alertable*/); timerInfo->InternalCompletionEvent.CloseEvent(); diff --git a/src/coreclr/vm/win32threadpool.h b/src/coreclr/vm/win32threadpool.h index 551cc4da00645..f064ec7bc89ea 100644 --- a/src/coreclr/vm/win32threadpool.h +++ b/src/coreclr/vm/win32threadpool.h @@ -859,7 +859,7 @@ class ThreadpoolMgr WRAPPER_NO_CONTRACT; _ASSERTE(!UsePortableThreadPool()); - Thread::IncrementWorkerThreadPoolCompletionCount(GetThread()); + Thread::IncrementWorkerThreadPoolCompletionCount(GetThreadNULLOk()); UpdateLastDequeueTime(); } diff --git a/src/coreclr/zap/zapinfo.cpp b/src/coreclr/zap/zapinfo.cpp index 73a162497fcaa..12fe2ab074ce5 100644 --- a/src/coreclr/zap/zapinfo.cpp +++ b/src/coreclr/zap/zapinfo.cpp @@ -205,6 +205,10 @@ CORJIT_FLAGS ZapInfo::ComputeJitFlags(CORINFO_METHOD_HANDLE handle) } #endif // FEATURE_READYTORUN_COMPILER +#ifdef ARM_SOFTFP + jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_SOFTFP_ABI); +#endif + return jitFlags; } diff --git a/src/coreclr/zap/zaplog.h b/src/coreclr/zap/zaplog.h index e07cb29e51e3b..a9f5e2e27798e 100644 --- a/src/coreclr/zap/zaplog.h +++ b/src/coreclr/zap/zaplog.h @@ -27,7 +27,7 @@ inline void ThrowAndLog(HRESULT hr, INDEBUG_COMMA(__in_z const char * szMsg) IND // Log failures when StressLog is on static ConfigDWORD g_iStressLog; - BOOL bLog = g_iStressLog.val_DontUse_(CLRConfig::UNSUPPORTED_StressLog, 0); + BOOL bLog = g_iStressLog.val(CLRConfig::UNSUPPORTED_StressLog); if (bLog) { #ifdef _DEBUG diff --git a/src/coreclr/zap/zapper.cpp b/src/coreclr/zap/zapper.cpp index a98fc6cc2934d..c0d65d34c4649 100644 --- a/src/coreclr/zap/zapper.cpp +++ b/src/coreclr/zap/zapper.cpp @@ -121,8 +121,6 @@ STDAPI CreatePDBWorker(LPCWSTR pwzAssemblyPath, LPCWSTR pwzPlatformAssembliesPat BEGIN_ENTRYPOINT_NOTHROW; - Zapper* zap = NULL; - EX_TRY { GetCompileInfo()->SetIsGeneratingNgenPDB(TRUE); @@ -130,7 +128,7 @@ STDAPI CreatePDBWorker(LPCWSTR pwzAssemblyPath, LPCWSTR pwzPlatformAssembliesPat NGenOptions ngo = {0}; ngo.dwSize = sizeof(NGenOptions); - zap = Zapper::NewZapper(&ngo); + NewHolder zap(Zapper::NewZapper(&ngo)); #if !defined(FEATURE_MERGE_JIT_AND_ENGINE) zap->SetDontLoadJit(); @@ -1155,7 +1153,7 @@ void Zapper::InitializeCompilerFlags(CORCOMPILE_VERSION_INFO * pVersionInfo) // Set CORJIT_FLAG_MIN_OPT only if COMPlus_JitMinOpts == 1 static ConfigDWORD g_jitMinOpts; - if (g_jitMinOpts.val_DontUse_(CLRConfig::UNSUPPORTED_JITMinOpts, 0) == 1) + if (g_jitMinOpts.val(CLRConfig::UNSUPPORTED_JITMinOpts) == 1) { m_pOpt->m_compilerFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT); } diff --git a/src/installer/corehost.proj b/src/installer/corehost.proj index 6ba56f27aec80..09d6bc7ebb6bf 100644 --- a/src/installer/corehost.proj +++ b/src/installer/corehost.proj @@ -84,7 +84,7 @@ $(BuildArgs) incremental-native-build $(BuildArgs) rootdir $(RepoRoot) $(BuildArgs) coreclrartifacts $(CoreCLRArtifactsPath) - $(BuildArgs) ninja + $(BuildArgs) msbuild $(BuildArgs) runtimeflavor $(RuntimeFlavor) $(BuildArgs) runtimeconfiguration $(RuntimeConfiguration) diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs index 0d733815e625b..a1905096303fb 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/BinaryUtils.cs @@ -174,15 +174,16 @@ public static void CopyFile(string sourcePath, string destinationPath) File.Copy(sourcePath, destinationPath, overwrite: true); } - internal static void WriteToStream(MemoryMappedViewAccessor sourceViewAccessor, FileStream fileStream) + internal static void WriteToStream(MemoryMappedViewAccessor sourceViewAccessor, FileStream fileStream, long length) { int pos = 0; int bufSize = 16384; //16K byte[] buf = new byte[bufSize]; + length = Math.Min(length, sourceViewAccessor.Capacity); do { - int bytesRequested = Math.Min((int)sourceViewAccessor.Capacity - pos, bufSize); + int bytesRequested = Math.Min((int)length - pos, bufSize); if (bytesRequested <= 0) { break; diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs index 8bc479d31ee06..69ad0d439bd64 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs @@ -85,21 +85,27 @@ void UpdateResources() { RetryUtil.RetryOnIOError(() => { + FileStream appHostSourceStream = null; MemoryMappedFile memoryMappedFile = null; MemoryMappedViewAccessor memoryMappedViewAccessor = null; try { // Open the source host file. - memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostSourceFilePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); + appHostSourceStream = new FileStream(appHostSourceFilePath, FileMode.Open, FileAccess.Read, FileShare.Read); + memoryMappedFile = MemoryMappedFile.CreateFromFile(appHostSourceStream, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, true); memoryMappedViewAccessor = memoryMappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.CopyOnWrite); + // Get the size of the source app host to ensure that we don't write extra data to the destination. + // On Windows, the size of the view accessor is rounded up to the next page boundary. + long sourceAppHostLength = appHostSourceStream.Length; + // Transform the host file in-memory. RewriteAppHost(memoryMappedViewAccessor); // Save the transformed host. using (FileStream fileStream = new FileStream(appHostDestinationFilePath, FileMode.Create)) { - BinaryUtils.WriteToStream(memoryMappedViewAccessor, fileStream); + BinaryUtils.WriteToStream(memoryMappedViewAccessor, fileStream, sourceAppHostLength); // Remove the signature from MachO hosts. if (!appHostIsPEImage) @@ -112,6 +118,7 @@ void UpdateResources() { memoryMappedViewAccessor?.Dispose(); memoryMappedFile?.Dispose(); + appHostSourceStream?.Dispose(); } }); diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs index 646e1b094310d..22030386d2673 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleOptions.cs @@ -17,5 +17,6 @@ public enum BundleOptions BundleOtherFiles = 2, BundleSymbolFiles = 4, BundleAllContent = BundleNativeBinaries | BundleOtherFiles, + EnableCompression = 8, }; } diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs index 0505d2dcefbf3..98e8440e7ce9d 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs @@ -1,14 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.NET.HostModel.AppHost; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.IO; +using System.IO.Compression; +using System.Linq; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; +using Microsoft.NET.HostModel.AppHost; namespace Microsoft.NET.HostModel.Bundle { @@ -18,6 +19,9 @@ namespace Microsoft.NET.HostModel.Bundle /// public class Bundler { + public const uint BundlerMajorVersion = 6; + public const uint BundlerMinorVersion = 0; + private readonly string HostName; private readonly string OutputDir; private readonly string DepsJson; @@ -44,22 +48,72 @@ public Bundler(string hostName, OutputDir = Path.GetFullPath(string.IsNullOrEmpty(outputDir) ? Environment.CurrentDirectory : outputDir); Target = new TargetInfo(targetOS, targetArch, targetFrameworkVersion); + if (Target.BundleMajorVersion < 6 && + (options & BundleOptions.EnableCompression) != 0) + { + throw new ArgumentException("Compression requires framework version 6.0 or above", nameof(options)); + } + appAssemblyName ??= Target.GetAssemblyName(hostName); DepsJson = appAssemblyName + ".deps.json"; RuntimeConfigJson = appAssemblyName + ".runtimeconfig.json"; RuntimeConfigDevJson = appAssemblyName + ".runtimeconfig.dev.json"; - BundleManifest = new Manifest(Target.BundleVersion, netcoreapp3CompatMode: options.HasFlag(BundleOptions.BundleAllContent)); + BundleManifest = new Manifest(Target.BundleMajorVersion, netcoreapp3CompatMode: options.HasFlag(BundleOptions.BundleAllContent)); Options = Target.DefaultOptions | options; } + private bool ShouldCompress(FileType type) + { + if (!Options.HasFlag(BundleOptions.EnableCompression)) + { + return false; + } + + switch (type) + { + case FileType.Symbols: + case FileType.NativeBinary: + return true; + + default: + return false; + } + } + /// /// Embed 'file' into 'bundle' /// - /// Returns the offset of the start 'file' within 'bundle' - - private long AddToBundle(Stream bundle, Stream file, FileType type) + /// + /// startOffset: offset of the start 'file' within 'bundle' + /// compressedSize: size of the compressed data, if entry was compressed, otherwise 0 + /// + private (long startOffset, long compressedSize) AddToBundle(Stream bundle, Stream file, FileType type) { + long startOffset = bundle.Position; + if (ShouldCompress(type)) + { + long fileLength = file.Length; + file.Position = 0; + + // We use DeflateStream here. + // It uses GZip algorithm, but with a trivial header that does not contain file info. + using (DeflateStream compressionStream = new DeflateStream(bundle, CompressionLevel.Optimal, leaveOpen: true)) + { + file.CopyTo(compressionStream); + } + + long compressedSize = bundle.Position - startOffset; + if (compressedSize < fileLength * 0.75) + { + return (startOffset, compressedSize); + } + + // compression rate was not good enough + // roll back the bundle offset and let the uncompressed code path take care of the entry. + bundle.Seek(startOffset, SeekOrigin.Begin); + } + if (type == FileType.Assembly) { long misalignment = (bundle.Position % Target.AssemblyAlignment); @@ -72,10 +126,10 @@ private long AddToBundle(Stream bundle, Stream file, FileType type) } file.Position = 0; - long startOffset = bundle.Position; + startOffset = bundle.Position; file.CopyTo(bundle); - return startOffset; + return (startOffset, 0); } private bool IsHost(string fileRelativePath) @@ -186,8 +240,8 @@ private FileType InferType(FileSpec fileSpec) /// public string GenerateBundle(IReadOnlyList fileSpecs) { - Tracer.Log($"Bundler version: {Manifest.CurrentVersion}"); - Tracer.Log($"Bundler Header: {BundleManifest.DesiredVersion}"); + Tracer.Log($"Bundler Version: {BundlerMajorVersion}.{BundlerMinorVersion}"); + Tracer.Log($"Bundle Version: {BundleManifest.BundleVersion}"); Tracer.Log($"Target Runtime: {Target}"); Tracer.Log($"Bundler Options: {Options}"); @@ -206,13 +260,6 @@ public string GenerateBundle(IReadOnlyList fileSpecs) throw new ArgumentException("Invalid input specification: Must specify the host binary"); } - var bundleRelativePathCollision = fileSpecs.GroupBy(file => file.BundleRelativePath).FirstOrDefault(g => g.Count() > 1); - if (bundleRelativePathCollision != null) - { - string fileSpecPaths = string.Join(", ", bundleRelativePathCollision.Select(file => "'" + file.SourcePath + "'")); - throw new ArgumentException($"Invalid input specification: Found entries {fileSpecPaths} with the same BundleRelativePath '{bundleRelativePathCollision.Key}'"); - } - string bundlePath = Path.Combine(OutputDir, HostName); if (File.Exists(bundlePath)) { @@ -221,6 +268,11 @@ public string GenerateBundle(IReadOnlyList fileSpecs) BinaryUtils.CopyFile(hostSource, bundlePath); + // Note: We're comparing file paths both on the OS we're running on as well as on the target OS for the app + // We can't really make assumptions about the file systems (even on Linux there can be case insensitive file systems + // and vice versa for Windows). So it's safer to do case sensitive comparison everywhere. + var relativePathToSpec = new Dictionary(StringComparer.Ordinal); + long headerOffset = 0; using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(bundlePath))) { @@ -251,11 +303,26 @@ public string GenerateBundle(IReadOnlyList fileSpecs) continue; } + if (relativePathToSpec.TryGetValue(fileSpec.BundleRelativePath, out var existingFileSpec)) + { + if (!string.Equals(fileSpec.SourcePath, existingFileSpec.SourcePath, StringComparison.Ordinal)) + { + throw new ArgumentException($"Invalid input specification: Found entries '{fileSpec.SourcePath}' and '{existingFileSpec.SourcePath}' with the same BundleRelativePath '{fileSpec.BundleRelativePath}'"); + } + + // Exact duplicate - intentionally skip and don't include a second copy in the bundle + continue; + } + else + { + relativePathToSpec.Add(fileSpec.BundleRelativePath, fileSpec); + } + using (FileStream file = File.OpenRead(fileSpec.SourcePath)) { FileType targetType = Target.TargetSpecificFileType(type); - long startOffset = AddToBundle(bundle, file, targetType); - FileEntry entry = BundleManifest.AddEntry(targetType, relativePath, startOffset, file.Length); + (long startOffset, long compressedSize) = AddToBundle(bundle, file, targetType); + FileEntry entry = BundleManifest.AddEntry(targetType, relativePath, startOffset, file.Length, compressedSize, Target.BundleMajorVersion); Tracer.Log($"Embed: {entry}"); } } diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs index 7315c2a026313..e71a7faaa4578 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs @@ -15,32 +15,44 @@ namespace Microsoft.NET.HostModel.Bundle /// * Name ("NameLength" Bytes) /// * Offset (Int64) /// * Size (Int64) + /// === present only in bundle version 3+ + /// * CompressedSize (Int64) 0 indicates No Compression /// public class FileEntry { + public readonly uint BundleMajorVersion; + public readonly long Offset; public readonly long Size; + public readonly long CompressedSize; public readonly FileType Type; public readonly string RelativePath; // Path of an embedded file, relative to the Bundle source-directory. public const char DirectorySeparatorChar = '/'; - public FileEntry(FileType fileType, string relativePath, long offset, long size) + public FileEntry(FileType fileType, string relativePath, long offset, long size, long compressedSize, uint bundleMajorVersion) { + BundleMajorVersion = bundleMajorVersion; Type = fileType; RelativePath = relativePath.Replace('\\', DirectorySeparatorChar); Offset = offset; Size = size; + CompressedSize = compressedSize; } public void Write(BinaryWriter writer) { writer.Write(Offset); writer.Write(Size); + // compression is used only in version 6.0+ + if (BundleMajorVersion >= 6) + { + writer.Write(CompressedSize); + } writer.Write((byte)Type); writer.Write(RelativePath); } - public override string ToString() => $"{RelativePath} [{Type}] @{Offset} Sz={Size}"; + public override string ToString() => $"{RelativePath} [{Type}] @{Offset} Sz={Size} CompressedSz={CompressedSize}"; } } diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs index 0beddb2b8cacc..73e7b3b10fd24 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs @@ -66,32 +66,26 @@ private enum HeaderFlags : ulong // with path-names so that the AppHost can use it in // extraction path. public readonly string BundleID; - - public const uint CurrentMajorVersion = 2; - public readonly uint DesiredMajorVersion; + public readonly uint BundleMajorVersion; // The Minor version is currently unused, and is always zero - public const uint MinorVersion = 0; - - public static string CurrentVersion => $"{CurrentMajorVersion}.{MinorVersion}"; - public string DesiredVersion => $"{DesiredMajorVersion}.{MinorVersion}"; - + public const uint BundleMinorVersion = 0; private FileEntry DepsJsonEntry; private FileEntry RuntimeConfigJsonEntry; private HeaderFlags Flags; - public List Files; + public string BundleVersion => $"{BundleMajorVersion}.{BundleMinorVersion}"; - public Manifest(uint desiredVersion, bool netcoreapp3CompatMode = false) + public Manifest(uint bundleMajorVersion, bool netcoreapp3CompatMode = false) { - DesiredMajorVersion = desiredVersion; + BundleMajorVersion = bundleMajorVersion; Files = new List(); BundleID = Path.GetRandomFileName(); - Flags = (netcoreapp3CompatMode) ? HeaderFlags.NetcoreApp3CompatMode: HeaderFlags.None; + Flags = (netcoreapp3CompatMode) ? HeaderFlags.NetcoreApp3CompatMode : HeaderFlags.None; } - public FileEntry AddEntry(FileType type, string relativePath, long offset, long size) + public FileEntry AddEntry(FileType type, string relativePath, long offset, long size, long compressedSize, uint bundleMajorVersion) { - FileEntry entry = new FileEntry(type, relativePath, offset, size); + FileEntry entry = new FileEntry(type, relativePath, offset, size, compressedSize, bundleMajorVersion); Files.Add(entry); switch (entry.Type) @@ -118,12 +112,12 @@ public long Write(BinaryWriter writer) long startOffset = writer.BaseStream.Position; // Write the bundle header - writer.Write(DesiredMajorVersion); - writer.Write(MinorVersion); + writer.Write(BundleMajorVersion); + writer.Write(BundleMinorVersion); writer.Write(Files.Count); writer.Write(BundleID); - if (DesiredMajorVersion == 2) + if (BundleMajorVersion >= 2) { writer.Write((DepsJsonEntry != null) ? DepsJsonEntry.Offset : 0); writer.Write((DepsJsonEntry != null) ? DepsJsonEntry.Size : 0); diff --git a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs index 05d5ff9d89ee1..2d3f6566a5ad5 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/Bundle/TargetInfo.cs @@ -25,7 +25,7 @@ public class TargetInfo public readonly OSPlatform OS; public readonly Architecture Arch; public readonly Version FrameworkVersion; - public readonly uint BundleVersion; + public readonly uint BundleMajorVersion; public readonly BundleOptions DefaultOptions; public readonly int AssemblyAlignment; @@ -33,18 +33,23 @@ public TargetInfo(OSPlatform? os, Architecture? arch, Version targetFrameworkVer { OS = os ?? HostOS; Arch = arch ?? RuntimeInformation.OSArchitecture; - FrameworkVersion = targetFrameworkVersion ?? net50; + FrameworkVersion = targetFrameworkVersion ?? net60; Debug.Assert(IsLinux || IsOSX || IsWindows); - if (FrameworkVersion.CompareTo(net50) >= 0) + if (FrameworkVersion.CompareTo(net60) >= 0) { - BundleVersion = 2u; + BundleMajorVersion = 6u; + DefaultOptions = BundleOptions.None; + } + else if (FrameworkVersion.CompareTo(net50) >= 0) + { + BundleMajorVersion = 2u; DefaultOptions = BundleOptions.None; } else if (FrameworkVersion.Major == 3 && (FrameworkVersion.Minor == 0 || FrameworkVersion.Minor == 1)) { - BundleVersion = 1u; + BundleMajorVersion = 1u; DefaultOptions = BundleOptions.BundleAllContent; } else @@ -94,7 +99,7 @@ public override string ToString() // The .net core 3 apphost doesn't care about semantics of FileType -- all files are extracted at startup. // However, the apphost checks that the FileType value is within expected bounds, so set it to the first enumeration. - public FileType TargetSpecificFileType(FileType fileType) => (BundleVersion == 1) ? FileType.Unknown : fileType; + public FileType TargetSpecificFileType(FileType fileType) => (BundleMajorVersion == 1) ? FileType.Unknown : fileType; // In .net core 3.x, bundle processing happens within the AppHost. // Therefore HostFxr and HostPolicy can be bundled within the single-file app. @@ -105,6 +110,7 @@ public override string ToString() public bool ShouldExclude(string relativePath) => (FrameworkVersion.Major != 3) && (relativePath.Equals(HostFxr) || relativePath.Equals(HostPolicy)); + private readonly Version net60 = new Version(6, 0); private readonly Version net50 = new Version(5, 0); private string HostFxr => IsWindows ? "hostfxr.dll" : IsLinux ? "libhostfxr.so" : "libhostfxr.dylib"; private string HostPolicy => IsWindows ? "hostpolicy.dll" : IsLinux ? "libhostpolicy.so" : "libhostpolicy.dylib"; diff --git a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj index 923e75d0772df..64ac366ee3902 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj +++ b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj @@ -15,6 +15,7 @@ MicrosoftAspNetCore true + Microsoft.Net.HostModel.PGO diff --git a/src/installer/pkg/sfx/Directory.Build.props b/src/installer/pkg/sfx/Directory.Build.props index e5473445909cd..b0711d7f7ac91 100644 --- a/src/installer/pkg/sfx/Directory.Build.props +++ b/src/installer/pkg/sfx/Directory.Build.props @@ -8,9 +8,11 @@ $(NetCoreAppCurrent) Microsoft .NET - true - true true true + + true + true + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 3de0a508d2005..bc114a7ec5c1b 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -56,7 +56,7 @@ true 10.0.19041.1 - 14.12.25830.2 + 14.28.29715.1 @@ -100,6 +100,8 @@ + + @@ -162,6 +164,7 @@ + @@ -191,7 +194,6 @@ - diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj index 1084bd31a8201..08d9427c097f0 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj @@ -62,13 +62,18 @@ - <_diaSymArch Condition="'$(_hostArch)' != ''">$(_hostArch) + + <_diaSymArch>$(_hostArch) <_diaSymReaderPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymArch).dll - <_diaSymReaderPathIfExists Condition="Exists('$(_diaSymReaderPath)')">$(_diaSymReaderPath) + + + <_diaSymTargetArch>$(TargetArchitecture) + <_diaSymTargetArch Condition="'$(TargetArchitecture)' == 'x64'">amd64 + <_diaSymReaderTargetArchPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymTargetArch).dll - - + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj index 4e9c639a1060c..153612f0e2bad 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.sfxproj @@ -89,16 +89,16 @@ runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native - + tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture) - + tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture) - + tools/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture) - + @@ -113,13 +113,21 @@ - <_diaSymArch Condition="'$(_hostArch)' != ''">$(_hostArch) + + <_diaSymArch>$(_hostArch) <_diaSymReaderPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymArch).dll - <_diaSymReaderPathIfExists Condition="Exists('$(_diaSymReaderPath)')">$(_diaSymReaderPath) + + + <_diaSymTargetArch>$(TargetArchitecture) + <_diaSymTargetArch Condition="'$(TargetArchitecture)' == 'x64'">amd64 + <_diaSymReaderTargetArchPath>$(PkgMicrosoft_DiaSymReader_Native)/runtimes/win/native/Microsoft.DiaSymReader.Native.$(_diaSymTargetArch).dll - - + + + + runtimes/$(CoreCLRCrossTargetComponentDirName)_$(TargetArchitecture)/native + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets b/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets index 4af8a0f6061ed..5d3492a286ed6 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/ReadyToRun.targets @@ -1,12 +1,25 @@ + + + + + + + + + + + + x64 $(CoreCLRArtifactsPath)\$(CrossDir)\crossgen2 $(Crossgen2Dir)\crossgen2$(ExeSuffix) - --targetarch:$(TargetArchitecture) + true + @(PublishReadyToRunCrossgen2ExtraArgsList) unix win @@ -24,12 +37,12 @@ + DiaSymReader="$(_diaSymReaderPath)" /> + DiaSymReader="$(_diaSymReaderPath)" /> @@ -40,22 +53,36 @@ + + + + + + + + + + + + + + + - - + $(DOTNET_ROOT) - - $(DOTNET_ROOT) - $(RepoRoot) - + - - $(OriginalDotnetRootValue) - + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/monocrossaot.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/monocrossaot.sfxproj index 7e63e89f31ccf..efc97f22e8374 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/monocrossaot.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/monocrossaot.sfxproj @@ -2,11 +2,10 @@ <_MonoCrossAOTTargetOS Condition="'$(MonoCrossAOTTargetOS)' != ''">+$(MonoCrossAOTTargetOS.ToLowerInvariant())+ - $(MonoAotTargets);android-x64;android-arm64;android-x86;android-arm - $(MonoAotTargets);android-x64 + $(MonoAotTargets);android-x64;android-arm64;android-x86;android-arm $(MonoAotTargets);browser-wasm - $(MonoAotTargets);tvos-x64;tvos-arm64 - $(MonoAotTargets);ios-x64;ios-arm64;ios-x86;ios-arm + $(MonoAotTargets);tvossimulator-x64;tvossimulator-arm64;tvos-arm64 + $(MonoAotTargets);iossimulator-x64;iossimulator-arm64;iossimulator-x86;ios-arm64;ios-arm diff --git a/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj b/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj index 3c9a701c96371..307a2fdca4701 100644 --- a/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj +++ b/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj @@ -6,6 +6,7 @@ product band to upgrade in place. --> true + false .NET Core Shared Framework Bundle Installer $(MSBuildProjectDirectory) dotnet-runtime @@ -18,7 +19,7 @@ - + diff --git a/src/installer/snaps/dotnet-runtime-6.0/snap/snapcraft.yaml b/src/installer/pkg/snap/snapcraft.yaml old mode 100644 new mode 100755 similarity index 100% rename from src/installer/snaps/dotnet-runtime-6.0/snap/snapcraft.yaml rename to src/installer/pkg/snap/snapcraft.yaml diff --git a/src/installer/snaps/arm/dotnet-sdk-3.1/snap/snapcraft.yaml b/src/installer/snaps/arm/dotnet-sdk-3.1/snap/snapcraft.yaml deleted file mode 100644 index b9d9593496a84..0000000000000 --- a/src/installer/snaps/arm/dotnet-sdk-3.1/snap/snapcraft.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: dotnet-sdk -version: 3.1.201 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -architectures: - - build-on: [amd64] - run-on: [armhf] - -base: core - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/ccbcbf70-9911-40b1-a8cf-e018a13e720e/03c0621c6510f9c6f4cca6951f2cc1a4/dotnet-sdk-3.1.201-linux-arm.tar.gz - source-checksum: sha512/f37d0e55c9f593c6951bea5a6bb1ea3194486956efe08a2a0f266b419d912cdcbf4ac279358976f0bfa1fe560c333ca5d5437f8e8c718bb5963991e4395e0cd7 - stage-packages: - - libicu55 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu4 - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/arm/dotnet-sdk-5.0/snap/snapcraft.yaml b/src/installer/snaps/arm/dotnet-sdk-5.0/snap/snapcraft.yaml deleted file mode 100644 index c9e2ab7efb6c2..0000000000000 --- a/src/installer/snaps/arm/dotnet-sdk-5.0/snap/snapcraft.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: dotnet-sdk -version: 5.0.100-preview.2.20176.6 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -architectures: - - build-on: [amd64] - run-on: [armhf] - -base: core - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/f87574ee-c128-4e91-b436-68c99d801daf/b296bea9d987a4edaa71df47cd2e7aca/dotnet-sdk-5.0.100-preview.2.20176.6-linux-arm64.tar.gz - source-checksum: sha512/53cbf213e2e97b909b256d931f061178f26e5647424f144266d4af2e12d6443ef7398207a8f4e6f220c61db9ce49de3dc09d88417288a6a61d9b05e1def6b279 - stage-packages: - - libicu55 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu4 - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/arm64/dotnet-sdk-5.0/snap/snapcraft.yaml b/src/installer/snaps/arm64/dotnet-sdk-5.0/snap/snapcraft.yaml deleted file mode 100644 index 94a9af147619c..0000000000000 --- a/src/installer/snaps/arm64/dotnet-sdk-5.0/snap/snapcraft.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: dotnet-sdk -version: 5.0.100-preview.2.20176.6 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -architectures: - - build-on: [amd64] - run-on: [arm64] - -base: core18 - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/f87574ee-c128-4e91-b436-68c99d801daf/b296bea9d987a4edaa71df47cd2e7aca/dotnet-sdk-5.0.100-preview.2.20176.6-linux-arm64.tar.gz - source-checksum: sha512/53cbf213e2e97b909b256d931f061178f26e5647424f144266d4af2e12d6443ef7398207a8f4e6f220c61db9ce49de3dc09d88417288a6a61d9b05e1def6b279 - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu6 - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/dotnet-runtime-2.1/snap/snapcraft.yaml b/src/installer/snaps/dotnet-runtime-2.1/snap/snapcraft.yaml deleted file mode 100644 index afba782699333..0000000000000 --- a/src/installer/snaps/dotnet-runtime-2.1/snap/snapcraft.yaml +++ /dev/null @@ -1,45 +0,0 @@ -name: dotnet-runtime-21 -base: core18 -version: 2.1.26 -summary: Cross-Platform .NET Core Runtime. -description: | - .NET Core runtimes and libraries which are optimized for running .NET Core apps in production. See https://dot.net/core. - .NET Core is a general purpose development platform maintained by Microsoft. - -grade: stable -confinement: strict - -apps: - dotnet: - command: dotnet - plugs: - - network - - network-bind - - removable-media - - home - -slots: - dotnet-runtime: - content: dotnet-runtime-21 - interface: content - read: [/] - -parts: - dotnet-runtime: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/5c00bafe-4ead-4e9a-82b6-884a9727e751/fdb7f6701e556d89b6884037cc9983cb/dotnet-runtime-2.1.26-linux-x64.tar.gz - source-checksum: sha512/41cc13f14dd7721a079bdd0ab489de40e9d4f32787239a26e7d10fcb0020a8e78d446c3b430b4bf80a557a925b3ca87d7981bfda4bbf9495cc44b1d42d877c40 - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - liblttng-ust0 - - libstdc++6 - - zlib1g - - libgcc1 - - lldb - - libunwind8 - - libtinfo5 - - libdb5.3 - - libc6 \ No newline at end of file diff --git a/src/installer/snaps/dotnet-runtime-3.1/snap/snapcraft.yaml b/src/installer/snaps/dotnet-runtime-3.1/snap/snapcraft.yaml deleted file mode 100644 index 5c50df7aab507..0000000000000 --- a/src/installer/snaps/dotnet-runtime-3.1/snap/snapcraft.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: dotnet-runtime-31 -base: core18 -version: 3.1.13 -summary: Cross-Platform .NET Core Runtime. -description: | - .NET Core runtimes and libraries which are optimized for running .NET Core apps in production. See https://dot.net/core. - .NET Core is a general purpose development platform maintained by Microsoft. - -grade: stable -confinement: strict - -apps: - dotnet: - command: dotnet - plugs: - - network - - network-bind - - removable-media - - home - -slots: - dotnet-runtime: - content: dotnet-runtime-31 - interface: content - read: [/] - -parts: - dotnet-runtime: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/6880db3b-a4fe-4801-8e80-bbbec045f7c0/283b70d5e263c0341f011adf5a2ea5b1/dotnet-runtime-3.1.13-linux-x64.tar.gz - source-checksum: sha512/2559026dd9f6939ae882a925cd44901524673885b94b93d029db414c6be5981302a4dbd6d0e1234957dfab610b79b9a82867defa59dff09b5a99412318a7bd27 - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - liblttng-ust0 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - libdb5.3 - - libc6 \ No newline at end of file diff --git a/src/installer/snaps/dotnet-runtime-5.0/snap/snapcraft.yaml b/src/installer/snaps/dotnet-runtime-5.0/snap/snapcraft.yaml deleted file mode 100644 index e7c6bd7bad47f..0000000000000 --- a/src/installer/snaps/dotnet-runtime-5.0/snap/snapcraft.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: dotnet-runtime-50 -base: core18 -version: 5.0.4 -summary: Cross-Platform .NET Core Runtime. -description: | - .NET Core runtimes and libraries which are optimized for running .NET Core apps in production. See https://dot.net/core. - .NET Core is a general purpose development platform maintained by Microsoft. - -grade: stable -confinement: strict - -apps: - dotnet: - command: dotnet - plugs: - - network - - network-bind - - removable-media - - home - -slots: - dotnet-runtime: - content: dotnet-runtime-50 - interface: content - read: [/] - -parts: - dotnet-runtime: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/66db1966-cbe4-4c6c-9e73-80305c555aba/faabab630f9f56e28e9dc30691bda72c/dotnet-runtime-5.0.4-linux-x64.tar.gz - source-checksum: sha512/1f224778d81ca94d4246e9390d4d15f400eac527ebe50d1e92bd337b0a80bf111d15b0f187f28aa1c9218b2244aca00d3bb4090f73b6ac9ba484241111c74534 - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - liblttng-ust0 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - libdb5.3 - - libc6 \ No newline at end of file diff --git a/src/installer/snaps/dotnet-sdk-2.1/snap/snapcraft.yaml b/src/installer/snaps/dotnet-sdk-2.1/snap/snapcraft.yaml deleted file mode 100644 index d73c54acca4b5..0000000000000 --- a/src/installer/snaps/dotnet-sdk-2.1/snap/snapcraft.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: dotnet-sdk -version: 2.1.814 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -base: core18 - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/b44d40e6-fa23-4f2d-a0a9-4199731f0b1e/5e62077a9e8014d8d4c74aee5406e0c7/dotnet-sdk-2.1.814-linux-x64.tar.gz - source-checksum: sha512/79408996f53650d0c3ac39348fa102537d14190ba5dcc4b9152cdb8fc72566608ad7430f196731eeb62dcfacdb0f2fa37577b5d51e165afd9dd9ae15f9d2aabc - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu6 - - lldb - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/dotnet-sdk-3.1/snap/snapcraft.yaml b/src/installer/snaps/dotnet-sdk-3.1/snap/snapcraft.yaml deleted file mode 100644 index 01f810e2bc81d..0000000000000 --- a/src/installer/snaps/dotnet-sdk-3.1/snap/snapcraft.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: dotnet-sdk -version: 3.1.407 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -base: core18 - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/ab82011d-2549-4e23-a8a9-a2b522a31f27/6e615d6177e49c3e874d05ee3566e8bf/dotnet-sdk-3.1.407-linux-x64.tar.gz - source-checksum: sha512/b9c61061464a38df0a3eb5894a4a1229cd27d2ccba4168e434f4609b763630c01fbe1b2564826194d6d9b5ad86047e586312c0f35eafc3755dfe0ff9ba075c0c - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu6 - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/dotnet-sdk-5.0/snap/snapcraft.yaml b/src/installer/snaps/dotnet-sdk-5.0/snap/snapcraft.yaml deleted file mode 100644 index 93a84fff40277..0000000000000 --- a/src/installer/snaps/dotnet-sdk-5.0/snap/snapcraft.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: dotnet-sdk -version: 5.0.201 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -base: core18 - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/73a9cb2a-1acd-4d20-b864-d12797ca3d40/075dbe1dc3bba4aa85ca420167b861b6/dotnet-sdk-5.0.201-linux-x64.tar.gz - source-checksum: sha512/099084cc7935482e363bd7802d2fdd909b3d72d2e9706e9ba4df95e3d142a28b780d2b85e5fb4662dcaad18e91c7e06519184fae981a521425eed605770c3c5a - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu6 - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/dotnet-sdk-6.0/snap/snapcraft.yaml b/src/installer/snaps/dotnet-sdk-6.0/snap/snapcraft.yaml deleted file mode 100644 index bfbe2c9a407f8..0000000000000 --- a/src/installer/snaps/dotnet-sdk-6.0/snap/snapcraft.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: dotnet-sdk -version: 6.0.100-preview.2.21155.3 -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: stable -confinement: classic - -apps: - dotnet: - command: dotnet - -base: core18 - -parts: - dotnet-sdk: - plugin: dump - source: https://download.visualstudio.microsoft.com/download/pr/25c7e38e-0a6a-4d66-ac4e-b550a44b8a98/49128be84b903799259e7bebe8e9d969/dotnet-sdk-6.0.100-preview.2.21155.3-linux-x64.tar.gz - source-checksum: sha512/90d9b6070f7732dcf75f5a09a4f10f9b23c835a3bb144e0c3f1fa451cadd3d49c9781973b180f70a4d2798358a7c00f3c0b9b3bf35326fe4c94e470e84ac8c35 - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - libtinfo5 - - liblttng-ust0 - - liburcu6 - - runtime-wrapper: - plugin: dump - source: . - diff --git a/src/installer/snaps/dotnet-sdk/dotnet-runtime b/src/installer/snaps/dotnet-sdk/dotnet-runtime deleted file mode 100644 index 31ac85be70543..0000000000000 --- a/src/installer/snaps/dotnet-sdk/dotnet-runtime +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -dotnet_runtime_snaps=$(ls "$SNAP/../.." | grep dotnet-runtime) - -for snap in "$dotnet_runtime_snaps" -do - runtime_dir=$(realpath "$SNAP/../../$snap/current/shared/Microsoft.NETCore.App") - if [ -d "$runtime_dir" ]; then - runtime_version=$(ls "$runtime_dir") - runtime_path="$SNAP_DATA/Microsoft.NETCore.App/$runtime_version" - if [ ! -d "$runtime_path" ]; then - sudo ln -s "$runtime_dir/$runtime_version" "$runtime_path" - fi - fi -done - -exec $SNAP/dotnet "$@" diff --git a/src/installer/snaps/dotnet-sdk/snap/snapcraft.yaml b/src/installer/snaps/dotnet-sdk/snap/snapcraft.yaml deleted file mode 100644 index 8f9c7cc3fe823..0000000000000 --- a/src/installer/snaps/dotnet-sdk/snap/snapcraft.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: dotnet-sdk -version: $(DOTNET_SDK_VERSION) -summary: Cross-Platform .NET Core SDK -description: | - .NET Core SDK. https://dot.net/core. - -grade: devel -confinement: classic - -apps: - dotnet: - command: dotnet - -base: core18 - -parts: - dotnet-sdk: - plugin: dump - source: $(SOURCE_TARGZ) - source-checksum: sha512/$(SOURCE_TARGZ_SHA) - stage-packages: - - libicu60 - - libssl1.0.0 - - libcurl3 - - libgssapi-krb5-2 - - libstdc++6 - - zlib1g - - libgcc1 - - lldb - - libunwind8 - - libtinfo5 - - liblttng-ust0 - - liburcu6 - runtime-wrapper: - plugin: dump - source: . diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs index 1741c9584b326..33c895548b219 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs @@ -31,8 +31,8 @@ private void Bundle_Extraction_To_Specific_Path_Succeeds() var hostName = BundleHelper.GetHostName(fixture); // Publish the bundle - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, options: BundleOptions.BundleNativeBinaries); + BundleOptions options = BundleOptions.BundleNativeBinaries; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); // Verify expected files in the bundle directory var bundleDir = BundleHelper.GetBundleDir(fixture); @@ -80,8 +80,7 @@ private void Bundle_Extraction_To_Relative_Path_Succeeds(string relativePath, Bu return; var fixture = sharedTestState.TestFixture.Copy(); - UseSingleFileSelfContainedHost(fixture); - var bundler = BundleHelper.BundleApp(fixture, out var singleFile, bundleOptions); + var bundler = BundleSelfContainedApp(fixture, out var singleFile, bundleOptions); // Run the bundled app (extract files to ) var cmd = Command.Create(singleFile); @@ -110,8 +109,8 @@ private void Bundle_extraction_is_reused() var fixture = sharedTestState.TestFixture.Copy(); // Publish the bundle - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); + BundleOptions options = BundleOptions.BundleNativeBinaries; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); // Create a directory for extraction. var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); @@ -160,13 +159,12 @@ private void Bundle_extraction_can_recover_missing_files() var appName = Path.GetFileNameWithoutExtension(hostName); // Publish the bundle - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); + BundleOptions options = BundleOptions.BundleNativeBinaries; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); // Create a directory for extraction. var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); - // Run the bunded app for the first time, and extract files to // $DOTNET_BUNDLE_EXTRACT_BASE_DIR//bundle-id Command.Create(singleFile) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs index c71aaa35e5bdb..18aba06f6d978 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs @@ -44,10 +44,28 @@ public static string UseFrameworkDependentHost(TestProjectFixture testFixture) public static string BundleSelfContainedApp( TestProjectFixture testFixture, BundleOptions options = BundleOptions.None, - Version targetFrameworkVersion = null) + Version targetFrameworkVersion = null, + bool disableCompression = false) + { + string singleFile; + BundleSelfContainedApp(testFixture, out singleFile, options, targetFrameworkVersion); + return singleFile; + } + + public static Bundler BundleSelfContainedApp( + TestProjectFixture testFixture, + out string singleFile, + BundleOptions options = BundleOptions.None, + Version targetFrameworkVersion = null, + bool disableCompression = false) { UseSingleFileSelfContainedHost(testFixture); - return BundleHelper.BundleApp(testFixture, options, targetFrameworkVersion); + if (targetFrameworkVersion == null || targetFrameworkVersion >= new Version(6, 0)) + { + options |= BundleOptions.EnableCompression; + } + + return BundleHelper.BundleApp(testFixture, out singleFile, options, targetFrameworkVersion); } public abstract class SharedTestStateBase diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index 2a0dc9ad537be..9ede20c516e39 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -59,7 +59,54 @@ public void Bundled_Framework_dependent_App_Run_Succeeds(BundleOptions options) public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) { var fixture = sharedTestState.TestSelfContainedFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture, options); + var singleFile = BundleSelfContainedApp(fixture, options); + + // Run the bundled app (extract files) + RunTheApp(singleFile, fixture); + + // Run the bundled app again (reuse extracted files) + RunTheApp(singleFile, fixture); + } + + [InlineData(BundleOptions.None)] + [InlineData(BundleOptions.BundleNativeBinaries)] + [InlineData(BundleOptions.BundleAllContent)] + [Theory] + public void Bundled_Self_Contained_NoCompression_App_Run_Succeeds(BundleOptions options) + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + var singleFile = BundleSelfContainedApp(fixture, options, disableCompression: true); + + // Run the bundled app (extract files) + RunTheApp(singleFile, fixture); + + // Run the bundled app again (reuse extracted files) + RunTheApp(singleFile, fixture); + } + + [InlineData(BundleOptions.None)] + [InlineData(BundleOptions.BundleNativeBinaries)] + [InlineData(BundleOptions.BundleAllContent)] + [Theory] + public void Bundled_Self_Contained_Targeting50_App_Run_Succeeds(BundleOptions options) + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + var singleFile = BundleSelfContainedApp(fixture, options, new Version(5, 0)); + + // Run the bundled app (extract files) + RunTheApp(singleFile, fixture); + + // Run the bundled app again (reuse extracted files) + RunTheApp(singleFile, fixture); + } + + [InlineData(BundleOptions.BundleAllContent)] + [Theory] + public void Bundled_Framework_dependent_Targeting50_App_Run_Succeeds(BundleOptions options) + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + UseFrameworkDependentHost(fixture); + var singleFile = BundleHelper.BundleApp(fixture, options, new Version(5, 0)); // Run the bundled app (extract files) RunTheApp(singleFile, fixture); @@ -68,6 +115,17 @@ public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) RunTheApp(singleFile, fixture); } + [Fact] + public void Bundled_Self_Contained_Targeting50_WithCompression_Throws() + { + var fixture = sharedTestState.TestSelfContainedFixture.Copy(); + UseSingleFileSelfContainedHost(fixture); + // compression must be off when targeting 5.0 + var options = BundleOptions.EnableCompression; + + Assert.Throws(()=>BundleHelper.BundleApp(fixture, options, new Version(5, 0))); + } + [InlineData(BundleOptions.None)] [InlineData(BundleOptions.BundleNativeBinaries)] [InlineData(BundleOptions.BundleAllContent)] @@ -75,7 +133,7 @@ public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) public void Bundled_With_Empty_File_Succeeds(BundleOptions options) { var fixture = sharedTestState.TestAppWithEmptyFileFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture, options); + var singleFile = BundleSelfContainedApp(fixture, options); // Run the app RunTheApp(singleFile, fixture); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs index 95e1061f20c27..cb8bdf9d44a8e 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs @@ -26,8 +26,8 @@ public NetCoreApp3CompatModeTests(SingleFileSharedState fixture) public void Bundle_Is_Extracted() { var fixture = sharedTestState.TestFixture.Copy(); - UseSingleFileSelfContainedHost(fixture); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleAllContent); + BundleOptions options = BundleOptions.BundleAllContent; + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, options); var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); Command.Create(singleFile, "executing_assembly_location trusted_platform_assemblies assembly_location System.Console") diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index 60425b9c7192f..0efa9a7819189 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -91,7 +91,7 @@ public void GetCommandLineArgs_0_Non_Bundled_App() public void AppContext_Native_Search_Dirs_Contains_Bundle_Dir() { var fixture = sharedTestState.TestFixture.Copy(); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile); + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile); string extractionDir = BundleHelper.GetExtractionDir(fixture, bundler).Name; string bundleDir = BundleHelper.GetBundleDir(fixture).FullName; @@ -110,7 +110,7 @@ public void AppContext_Native_Search_Dirs_Contains_Bundle_Dir() public void AppContext_Native_Search_Dirs_Contains_Bundle_And_Extraction_Dirs() { var fixture = sharedTestState.TestFixture.Copy(); - Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); + Bundler bundler = BundleSelfContainedApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); string extractionDir = BundleHelper.GetExtractionDir(fixture, bundler).Name; string bundleDir = BundleHelper.GetBundleDir(fixture).FullName; diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs index 6a102ddade96a..ecd00f75c6fd1 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs @@ -60,7 +60,7 @@ public static string[] GetBundledFiles(TestProjectFixture fixture) public static string[] GetExtractedFiles(TestProjectFixture fixture, BundleOptions bundleOptions) { - switch (bundleOptions) + switch (bundleOptions & ~BundleOptions.EnableCompression) { case BundleOptions.None: case BundleOptions.BundleOtherFiles: diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs index d5022b168c1a9..d8aa3eb1edfb8 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Bundle.Tests/BundlerConsistencyTests.cs @@ -70,6 +70,34 @@ public void TestWithoutSpecifyingHostFails() Assert.Throws(() => bundler.GenerateBundle(fileSpecs)); } + [Fact] + public void TestWithExactDuplicateEntriesPasses() + { + var fixture = sharedTestState.TestFixture.Copy(); + + var hostName = BundleHelper.GetHostName(fixture); + var bundleDir = BundleHelper.GetBundleDir(fixture); + var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); + var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); + + // Generate a file specification with duplicate entries + var fileSpecs = new List(); + fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); + string appPath = BundleHelper.GetAppPath(fixture); + fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); + fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); + string systemLibPath = Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"); + fileSpecs.Add(new FileSpec(systemLibPath, "rel/system.repeat.dll")); + fileSpecs.Add(new FileSpec(systemLibPath, "rel/system.repeat.dll")); + + Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + bundler.GenerateBundle(fileSpecs); + + // Exact duplicates are not duplicated in the bundle + bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals("rel/app.repeat.dll")).Single().Type.Should().Be(FileType.Assembly); + bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals("rel/system.repeat.dll")).Single().Type.Should().Be(FileType.Assembly); + } + [Fact] public void TestWithDuplicateEntriesFails() { @@ -93,6 +121,58 @@ public void TestWithDuplicateEntriesFails() .And.Contain(BundleHelper.GetAppPath(fixture)); } + [Fact] + public void TestWithCaseSensitiveDuplicateEntriesPasses() + { + var fixture = sharedTestState.TestFixture.Copy(); + + var hostName = BundleHelper.GetHostName(fixture); + var bundleDir = BundleHelper.GetBundleDir(fixture); + var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); + var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); + + // Generate a file specification with duplicate entries + var fileSpecs = new List(); + fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); + fileSpecs.Add(new FileSpec(BundleHelper.GetAppPath(fixture), "rel/app.repeat.dll")); + fileSpecs.Add(new FileSpec(Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"), "rel/app.Repeat.dll")); + + Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + bundler.GenerateBundle(fileSpecs); + + bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals("rel/app.repeat.dll")).Single().Type.Should().Be(FileType.Assembly); + bundler.BundleManifest.Files.Where(entry => entry.RelativePath.Equals("rel/app.Repeat.dll")).Single().Type.Should().Be(FileType.Assembly); + } + + [Fact] + public void TestWithMultipleDuplicateEntriesFails() + { + var fixture = sharedTestState.TestFixture.Copy(); + + var hostName = BundleHelper.GetHostName(fixture); + var bundleDir = BundleHelper.GetBundleDir(fixture); + var targetOS = BundleHelper.GetTargetOS(fixture.CurrentRid); + var targetArch = BundleHelper.GetTargetArch(fixture.CurrentRid); + + // Generate a file specification with duplicate entries + var fileSpecs = new List(); + fileSpecs.Add(new FileSpec(BundleHelper.GetHostPath(fixture), BundleHelper.GetHostName(fixture))); + string appPath = BundleHelper.GetAppPath(fixture); + fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); + fileSpecs.Add(new FileSpec(appPath, "rel/app.repeat.dll")); + string systemLibPath = Path.Join(BundleHelper.GetPublishPath(fixture), "System.dll"); + fileSpecs.Add(new FileSpec(appPath, "rel/system.repeat.dll")); + fileSpecs.Add(new FileSpec(systemLibPath, "rel/system.repeat.dll")); + + Bundler bundler = new Bundler(hostName, bundleDir.FullName, targetOS: targetOS, targetArch: targetArch); + Assert.Throws(() => bundler.GenerateBundle(fileSpecs)) + .Message + .Should().Contain("rel/system.repeat.dll") + .And.NotContain("rel/app.repeat.dll") + .And.Contain(appPath) + .And.Contain(systemLibPath); + } + [Fact] public void TestBaseNameComputation() { diff --git a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs index a127b4c22806d..0a7f1f908f08c 100644 --- a/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs +++ b/src/installer/tests/TestUtils/Assertions/CommandResultAssertions.cs @@ -146,7 +146,7 @@ public string GetDiagnosticsInfo() $"Arguments: {Result.StartInfo.Arguments}{Environment.NewLine}" + $"Exit Code: {Result.ExitCode}{Environment.NewLine}" + $"StdOut:{Environment.NewLine}{Result.StdOut}{Environment.NewLine}" + - $"StdErr:{Environment.NewLine}{Result.StdErr}{Environment.NewLine}"; ; + $"StdErr:{Environment.NewLine}{Result.StdErr}{Environment.NewLine}"; } public AndConstraint HaveSkippedProjectCompilation(string skippedProject, string frameworkFullName) diff --git a/src/libraries/Common/src/Extensions/EmptyDisposable.cs b/src/libraries/Common/src/Extensions/EmptyDisposable.cs index 322df0fea85fd..1dbaef4237e29 100644 --- a/src/libraries/Common/src/Extensions/EmptyDisposable.cs +++ b/src/libraries/Common/src/Extensions/EmptyDisposable.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.FileProviders { - internal class EmptyDisposable : IDisposable + internal sealed class EmptyDisposable : IDisposable { public static EmptyDisposable Instance { get; } = new EmptyDisposable(); diff --git a/src/libraries/Common/src/Extensions/Logging/NullExternalScopeProvider.cs b/src/libraries/Common/src/Extensions/Logging/NullExternalScopeProvider.cs index 791ecc87152cd..5c50de4dd6207 100644 --- a/src/libraries/Common/src/Extensions/Logging/NullExternalScopeProvider.cs +++ b/src/libraries/Common/src/Extensions/Logging/NullExternalScopeProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Logging /// /// Scope provider that does nothing. /// - internal class NullExternalScopeProvider : IExternalScopeProvider + internal sealed class NullExternalScopeProvider : IExternalScopeProvider { private NullExternalScopeProvider() { diff --git a/src/libraries/Common/src/Extensions/Logging/NullScope.cs b/src/libraries/Common/src/Extensions/Logging/NullScope.cs index 14f92fbe72aee..7748734de07be 100644 --- a/src/libraries/Common/src/Extensions/Logging/NullScope.cs +++ b/src/libraries/Common/src/Extensions/Logging/NullScope.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging /// /// An empty scope without any logic /// - internal class NullScope : IDisposable + internal sealed class NullScope : IDisposable { public static NullScope Instance { get; } = new NullScope(); diff --git a/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs b/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs index 548398ae413d3..f69b52220f355 100644 --- a/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs +++ b/src/libraries/Common/src/Extensions/ParameterDefaultValue/ParameterDefaultValue.cs @@ -15,7 +15,7 @@ namespace Microsoft.Extensions.Internal { - internal class ParameterDefaultValue + internal static class ParameterDefaultValue { public static bool TryGetDefaultValue(ParameterInfo parameter, out object? defaultValue) { diff --git a/src/libraries/Common/src/Extensions/ProviderAliasUtilities/ProviderAliasUtilities.cs b/src/libraries/Common/src/Extensions/ProviderAliasUtilities/ProviderAliasUtilities.cs index 40e80be644765..69e7687e62665 100644 --- a/src/libraries/Common/src/Extensions/ProviderAliasUtilities/ProviderAliasUtilities.cs +++ b/src/libraries/Common/src/Extensions/ProviderAliasUtilities/ProviderAliasUtilities.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Diagnostics; using System.Reflection; @@ -13,16 +14,19 @@ internal static class ProviderAliasUtilities internal static string GetAlias(Type providerType) { - foreach (CustomAttributeData attributeData in CustomAttributeData.GetCustomAttributes(providerType)) + IList attributes = CustomAttributeData.GetCustomAttributes(providerType); + + for (int i = 0; i < attributes.Count; i++) { - if (attributeData.AttributeType.FullName == AliasAttibuteTypeFullName) + CustomAttributeData attributeData = attributes[i]; + if (attributeData.AttributeType.FullName == AliasAttibuteTypeFullName && + attributeData.ConstructorArguments.Count > 0) { - foreach (CustomAttributeTypedArgument arg in attributeData.ConstructorArguments) - { - Debug.Assert(arg.ArgumentType == typeof(string)); + CustomAttributeTypedArgument arg = attributeData.ConstructorArguments[0]; + + Debug.Assert(arg.ArgumentType == typeof(string)); - return arg.Value?.ToString(); - } + return arg.Value?.ToString(); } } diff --git a/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Hash.cs b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Hash.cs index 7a7ca492ee3e7..f080df4d1d352 100644 --- a/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Hash.cs +++ b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Hash.cs @@ -13,6 +13,8 @@ namespace Internal.Cryptography // internal static partial class AsymmetricAlgorithmHelpers { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is used when the user asks for it.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")] public static byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) { // The classes that call us are sealed and their base class has checked this already. @@ -21,10 +23,22 @@ public static byte[] HashData(byte[] data, int offset, int count, HashAlgorithmN Debug.Assert(offset >= 0 && offset <= data.Length - count); Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name)); +#if NET5_0_OR_GREATER + ReadOnlySpan source = data.AsSpan(offset, count); + + return + hashAlgorithm == HashAlgorithmName.SHA256 ? SHA256.HashData(source) : + hashAlgorithm == HashAlgorithmName.SHA1 ? SHA1.HashData(source) : + hashAlgorithm == HashAlgorithmName.SHA512 ? SHA512.HashData(source) : + hashAlgorithm == HashAlgorithmName.SHA384 ? SHA384.HashData(source) : + hashAlgorithm == HashAlgorithmName.MD5 ? MD5.HashData(source) : + throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name); +#else using (HashAlgorithm hasher = GetHashAlgorithm(hashAlgorithm)) { return hasher.ComputeHash(data, offset, count); } +#endif } public static byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) @@ -39,25 +53,37 @@ public static byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is used when the user asks for it.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")] public static bool TryHashData(ReadOnlySpan source, Span destination, HashAlgorithmName hashAlgorithm, out int bytesWritten) { // The classes that call us are sealed and their base class has checked this already. Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name)); +#if NET5_0_OR_GREATER + return + hashAlgorithm == HashAlgorithmName.SHA256 ? SHA256.TryHashData(source, destination, out bytesWritten) : + hashAlgorithm == HashAlgorithmName.SHA1 ? SHA1.TryHashData(source, destination, out bytesWritten) : + hashAlgorithm == HashAlgorithmName.SHA512 ? SHA512.TryHashData(source, destination, out bytesWritten) : + hashAlgorithm == HashAlgorithmName.SHA384 ? SHA384.TryHashData(source, destination, out bytesWritten) : + hashAlgorithm == HashAlgorithmName.MD5 ? MD5.TryHashData(source, destination, out bytesWritten) : + throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name); +#else using (HashAlgorithm hasher = GetHashAlgorithm(hashAlgorithm)) { return hasher.TryComputeHash(source, destination, out bytesWritten); } +#endif } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is used when the user asks for it.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")] private static HashAlgorithm GetHashAlgorithm(HashAlgorithmName hashAlgorithmName) => - hashAlgorithmName == HashAlgorithmName.MD5 ? MD5.Create() : - hashAlgorithmName == HashAlgorithmName.SHA1 ? SHA1.Create() : hashAlgorithmName == HashAlgorithmName.SHA256 ? SHA256.Create() : + hashAlgorithmName == HashAlgorithmName.SHA1 ? SHA1.Create() : + hashAlgorithmName == HashAlgorithmName.SHA512 ? SHA512.Create() : hashAlgorithmName == HashAlgorithmName.SHA384 ? SHA384.Create() : - hashAlgorithmName == HashAlgorithmName.SHA512 ? (HashAlgorithm)SHA512.Create() : + hashAlgorithmName == HashAlgorithmName.MD5 ? MD5.Create() : throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmName.Name); } } diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Bignum.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Bignum.cs index 0ceb04a871572..5936cee56d6d3 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Bignum.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Bignum.cs @@ -10,33 +10,12 @@ internal static partial class Interop // TODO: [AndroidCrypto] Rename class to AndroidCrypto once all consumers are split in Android vs. Unix internal static partial class Crypto { - [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_BigNumFromBinary")] - private static extern unsafe SafeBignumHandle BigNumFromBinary(byte* s, int len); - [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_BigNumToBinary")] private static extern unsafe int BigNumToBinary(SafeBignumHandle a, byte* to); [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_GetBigNumBytes")] private static extern int GetBigNumBytes(SafeBignumHandle a); - internal static SafeBignumHandle CreateBignum(ReadOnlySpan bigEndianValue) - { - unsafe - { - fixed (byte* pBigEndianValue = bigEndianValue) - { - SafeBignumHandle ret = BigNumFromBinary(pBigEndianValue, bigEndianValue.Length); - if (ret.IsInvalid) - { - ret.Dispose(); - throw new CryptographicException(); - } - - return ret; - } - } - } - internal static unsafe byte[]? ExtractBignum(SafeBignumHandle? bignum, int targetSize) { if (bignum == null || bignum.IsInvalid) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs index 38d7dcf755938..b7e3ab4baa6a8 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Rsa.cs @@ -124,29 +124,39 @@ internal static RSAParameters ExportRsaParameters(SafeRsaHandle key, bool includ throw new CryptographicException(); } - int modulusSize = RsaSize(key); - - // RSACryptoServiceProvider expects P, DP, Q, DQ, and InverseQ to all - // be padded up to half the modulus size. - int halfModulus = modulusSize / 2; - - RSAParameters rsaParameters = new RSAParameters + using (n) + using (e) + using (d) + using (p) + using (dmp1) + using (q) + using (dmq1) + using (iqmp) { - Modulus = Crypto.ExtractBignum(n, modulusSize)!, - Exponent = Crypto.ExtractBignum(e, 0)!, - }; - - if (includePrivateParameters) - { - rsaParameters.D = Crypto.ExtractBignum(d, modulusSize); - rsaParameters.P = Crypto.ExtractBignum(p, halfModulus); - rsaParameters.DP = Crypto.ExtractBignum(dmp1, halfModulus); - rsaParameters.Q = Crypto.ExtractBignum(q, halfModulus); - rsaParameters.DQ = Crypto.ExtractBignum(dmq1, halfModulus); - rsaParameters.InverseQ = Crypto.ExtractBignum(iqmp, halfModulus); + int modulusSize = RsaSize(key); + + // RSACryptoServiceProvider expects P, DP, Q, DQ, and InverseQ to all + // be padded up to half the modulus size. + int halfModulus = modulusSize / 2; + + RSAParameters rsaParameters = new RSAParameters + { + Modulus = Crypto.ExtractBignum(n, modulusSize)!, + Exponent = Crypto.ExtractBignum(e, 0)!, + }; + + if (includePrivateParameters) + { + rsaParameters.D = Crypto.ExtractBignum(d, modulusSize); + rsaParameters.P = Crypto.ExtractBignum(p, halfModulus); + rsaParameters.DP = Crypto.ExtractBignum(dmp1, halfModulus); + rsaParameters.Q = Crypto.ExtractBignum(q, halfModulus); + rsaParameters.DQ = Crypto.ExtractBignum(dmq1, halfModulus); + rsaParameters.InverseQ = Crypto.ExtractBignum(iqmp, halfModulus); + } + + return rsaParameters; } - - return rsaParameters; } [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_GetRsaParameters")] diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs index ae4840f7e457e..133dc3ec445d1 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs @@ -12,7 +12,7 @@ internal static partial class AndroidCrypto [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRelease")] internal static extern void SSLStreamRelease(IntPtr ptr); - internal class SslException : Exception + internal sealed class SslException : Exception { internal SslException() { diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509.cs index 2ec104f11690d..b40581f15b8d2 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509.cs @@ -89,6 +89,7 @@ internal enum PAL_KeyAlgorithm DSA, EC, RSA, + UnknownAlgorithm = -1, } [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509PublicKey")] diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs new file mode 100644 index 0000000000000..2590f256925e2 --- /dev/null +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Chain.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +internal static partial class Interop +{ + internal static partial class AndroidCrypto + { + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainCreateContext")] + internal static extern SafeX509ChainContextHandle X509ChainCreateContext( + SafeX509Handle cert, + IntPtr[] extraStore, + int extraStoreLen); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainDestroyContext")] + internal static extern void X509ChainDestroyContext(IntPtr ctx); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainBuild")] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool X509ChainBuild( + SafeX509ChainContextHandle ctx, + long timeInMsFromUnixEpoch); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainGetCertificateCount")] + private static extern int X509ChainGetCertificateCount(SafeX509ChainContextHandle ctx); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainGetCertificates")] + private static extern int X509ChainGetCertificates( + SafeX509ChainContextHandle ctx, + IntPtr[] certs, + int certsLen); + + internal static X509Certificate2[] X509ChainGetCertificates(SafeX509ChainContextHandle ctx) + { + int count = Interop.AndroidCrypto.X509ChainGetCertificateCount(ctx); + var certPtrs = new IntPtr[count]; + + int res = Interop.AndroidCrypto.X509ChainGetCertificates(ctx, certPtrs, certPtrs.Length); + if (res == 0) + throw new CryptographicException(); + + Debug.Assert(res <= certPtrs.Length); + + var certs = new X509Certificate2[certPtrs.Length]; + for (int i = 0; i < res; i++) + { + certs[i] = new X509Certificate2(certPtrs[i]); + } + + if (res == certPtrs.Length) + { + return certs; + } + + return certs[0..res]; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ValidationError + { + public IntPtr Message; // UTF-16 string + public int Index; + public int Status; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainGetErrorCount")] + private static extern int X509ChainGetErrorCount(SafeX509ChainContextHandle ctx); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainGetErrors")] + private static unsafe extern int X509ChainGetErrors( + SafeX509ChainContextHandle ctx, + [Out] ValidationError[] errors, + int errorsLen); + + internal static ValidationError[] X509ChainGetErrors(SafeX509ChainContextHandle ctx) + { + int count = Interop.AndroidCrypto.X509ChainGetErrorCount(ctx); + if (count == 0) + return Array.Empty(); + + var errors = new ValidationError[count]; + int res = Interop.AndroidCrypto.X509ChainGetErrors(ctx, errors, errors.Length); + if (res != SUCCESS) + throw new CryptographicException(); + + return errors; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainSetCustomTrustStore")] + internal static extern int X509ChainSetCustomTrustStore( + SafeX509ChainContextHandle ctx, + IntPtr[] customTrustStore, + int customTrustStoreLen); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainSupportsRevocationOptions")] + [return:MarshalAs(UnmanagedType.U1)] + internal static extern bool X509ChainSupportsRevocationOptions(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509ChainValidate")] + internal static extern int X509ChainValidate( + SafeX509ChainContextHandle ctx, + X509RevocationMode revocationMode, + X509RevocationFlag revocationFlag, + out byte checkedRevocation); + } +} + +namespace System.Security.Cryptography.X509Certificates +{ + internal sealed class SafeX509ChainContextHandle : SafeHandle + { + public SafeX509ChainContextHandle() + : base(IntPtr.Zero, ownsHandle: true) + { + } + + protected override bool ReleaseHandle() + { + Interop.AndroidCrypto.X509ChainDestroyContext(handle); + SetHandle(IntPtr.Zero); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } +} diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Store.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Store.cs new file mode 100644 index 0000000000000..52e12eb9fcd52 --- /dev/null +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.X509Store.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class AndroidCrypto + { + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreAddCertificate")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern unsafe bool X509StoreAddCertificate( + SafeX509StoreHandle store, + SafeX509Handle cert, + string hashString); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreAddCertificateWithPrivateKey")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern unsafe bool X509StoreAddCertificateWithPrivateKey( + SafeX509StoreHandle store, + SafeX509Handle cert, + SafeKeyHandle key, + PAL_KeyAlgorithm algorithm, + string hashString); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreContainsCertificate")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern unsafe bool X509StoreContainsCertificate( + SafeX509StoreHandle store, + SafeX509Handle cert, + string hashString); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreEnumerateCertificates")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern unsafe bool X509StoreEnumerateCertificates( + SafeX509StoreHandle storeHandle, + delegate* unmanaged callback, + void *callbackContext); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreEnumerateTrustedCertificates")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern unsafe bool X509StoreEnumerateTrustedCertificates( + byte systemOnly, + delegate* unmanaged callback, + void *callbackContext); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreOpenDefault")] + internal static extern unsafe SafeX509StoreHandle X509StoreOpenDefault(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_X509StoreRemoveCertificate")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern unsafe bool X509StoreRemoveCertificate( + SafeX509StoreHandle store, + SafeX509Handle cert, + string hashString); + } +} + +namespace System.Security.Cryptography.X509Certificates +{ + internal sealed class SafeX509StoreHandle : Interop.JObjectLifetime.SafeJObjectHandle + { + public SafeX509StoreHandle() + { + } + } +} diff --git a/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs b/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs new file mode 100644 index 0000000000000..8eb0ee177ab8d --- /dev/null +++ b/src/libraries/Common/src/Interop/Interop.ICU.iOS.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Globalization + { + [DllImport(Libraries.GlobalizationNative, EntryPoint = "GlobalizationNative_LoadICUData")] + internal static extern int LoadICUData(string path); + } +} diff --git a/src/libraries/Common/src/Interop/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Interop.Ldap.cs index c40aa55fcdc67..4ff3fd17eed9b 100644 --- a/src/libraries/Common/src/Interop/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Interop.Ldap.cs @@ -15,7 +15,7 @@ internal static partial class Interop namespace System.DirectoryServices.Protocols { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal class Luid + internal sealed class Luid { private readonly int _lowPart; private readonly int _highPart; diff --git a/src/libraries/Common/src/Interop/Interop.TimeZoneDisplayNameType.cs b/src/libraries/Common/src/Interop/Interop.TimeZoneDisplayNameType.cs index f46072196eb91..570eb0eb4b1ec 100644 --- a/src/libraries/Common/src/Interop/Interop.TimeZoneDisplayNameType.cs +++ b/src/libraries/Common/src/Interop/Interop.TimeZoneDisplayNameType.cs @@ -11,6 +11,8 @@ internal enum TimeZoneDisplayNameType Generic = 0, Standard = 1, DaylightSavings = 2, + GenericLocation = 3, + ExemplarCity = 4, } } } diff --git a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs index 74a5ee3d14c15..392add014ed6a 100644 --- a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs @@ -27,7 +27,7 @@ internal struct SaslDefaultCredentials /// where we will have to resolve the result. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - internal class SaslInteractiveChallenge + internal sealed class SaslInteractiveChallenge { public ulong saslChallengeType; public string challenge; diff --git a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs index 1b1e3e8f168ce..c4d4a21fa455a 100644 --- a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs +++ b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFProxy.cs @@ -45,7 +45,7 @@ internal struct CFStreamClientContext public IntPtr CopyDescription; } - internal class CFProxy + internal sealed class CFProxy { private SafeCFDictionaryHandle _dictionary; diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs index 424453447e9ac..523f197a69ce0 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs @@ -7,7 +7,7 @@ internal static partial class Interop { internal static partial class AppleCrypto { - internal class SslException : Exception + internal sealed class SslException : Exception { internal SslException() { diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs index 3a7aaecc1f6fd..0e5e3b4fb6fd6 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetCpuUtilization.cs @@ -5,7 +5,7 @@ internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [StructLayout(LayoutKind.Sequential)] internal struct ProcessCpuInformation diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs index 543487754ed7c..15295e5053b62 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetRandomBytes.cs @@ -6,7 +6,7 @@ internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetNonCryptographicallySecureRandomBytes")] internal static extern unsafe void GetNonCryptographicallySecureRandomBytes(byte* buffer, int length); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs index 359d3775f02b1..850ff0af9e567 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs @@ -5,7 +5,7 @@ internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetSystemTimeAsTicks")] [SuppressGCTransition] diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs index 6bf296ffdbeae..c5f6d1baa2adb 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MemAlloc.cs @@ -6,7 +6,7 @@ internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemAlloc")] internal static extern IntPtr MemAlloc(nuint sizeInBytes); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs index ea23fbe49d18f..cb06c67651c0c 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForCtrlC.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { internal enum CtrlCode { diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs index d42576d5c6798..3082c661cbaec 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RegisterForSigChld.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_RegisterForSigChld")] internal static extern unsafe void RegisterForSigChld(delegate* unmanaged handler); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs index 609a9997388bb..927c2d2706833 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.RestoreAndHandleCtrl.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_RestoreAndHandleCtrl")] internal static extern void RestoreAndHandleCtrl(CtrlCode ctrlCode); diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs index d6d87033d1e6b..a12194e173755 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.SetTerminalInvalidationHandler.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Sys + internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_SetTerminalInvalidationHandler")] [SuppressGCTransition] diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs new file mode 100644 index 0000000000000..53ef644d84b97 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.DigestAlgs.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +internal static partial class Interop +{ + internal static partial class Crypto + { + private static volatile IntPtr s_evpMd5; + private static volatile IntPtr s_evpSha1; + private static volatile IntPtr s_evpSha256; + private static volatile IntPtr s_evpSha384; + private static volatile IntPtr s_evpSha512; + + [DllImport(Libraries.CryptoNative)] + private static extern IntPtr CryptoNative_EvpMd5(); + + internal static IntPtr EvpMd5() => + s_evpMd5 != IntPtr.Zero ? s_evpMd5 : (s_evpMd5 = CryptoNative_EvpMd5()); + + [DllImport(Libraries.CryptoNative)] + internal static extern IntPtr CryptoNative_EvpSha1(); + + internal static IntPtr EvpSha1() => + s_evpSha1 != IntPtr.Zero ? s_evpSha1 : (s_evpSha1 = CryptoNative_EvpSha1()); + + [DllImport(Libraries.CryptoNative)] + internal static extern IntPtr CryptoNative_EvpSha256(); + + internal static IntPtr EvpSha256() => + s_evpSha256 != IntPtr.Zero ? s_evpSha256 : (s_evpSha256 = CryptoNative_EvpSha256()); + + [DllImport(Libraries.CryptoNative)] + internal static extern IntPtr CryptoNative_EvpSha384(); + + internal static IntPtr EvpSha384() => + s_evpSha384 != IntPtr.Zero ? s_evpSha384 : (s_evpSha384 = CryptoNative_EvpSha384()); + + [DllImport(Libraries.CryptoNative)] + internal static extern IntPtr CryptoNative_EvpSha512(); + + internal static IntPtr EvpSha512() => + s_evpSha512 != IntPtr.Zero ? s_evpSha512 : (s_evpSha512 = CryptoNative_EvpSha512()); + + internal static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch + { + nameof(HashAlgorithmName.SHA1) => EvpSha1(), + nameof(HashAlgorithmName.SHA256) => EvpSha256(), + nameof(HashAlgorithmName.SHA384) => EvpSha384(), + nameof(HashAlgorithmName.SHA512) => EvpSha512(), + nameof(HashAlgorithmName.MD5) => EvpMd5(), + _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId)) + }; + } +} diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs index 45e3cce6715d9..bd6cf8bfb5b61 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.InteropServices; +using System.Security.Cryptography; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -36,21 +37,6 @@ internal static int EvpDigestUpdate(SafeEvpMdCtxHandle ctx, ReadOnlySpan d [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpMdSize")] internal static extern int EvpMdSize(IntPtr md); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpMd5")] - internal static extern IntPtr EvpMd5(); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha1")] - internal static extern IntPtr EvpSha1(); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha256")] - internal static extern IntPtr EvpSha256(); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha384")] - internal static extern IntPtr EvpSha384(); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpSha512")] - internal static extern IntPtr EvpSha512(); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetMaxMdSize")] private static extern int GetMaxMdSize(); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs index 1509e5196286b..f89135a31d12e 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.Rsa.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Security.Cryptography; using Microsoft.Win32.SafeHandles; @@ -9,6 +11,92 @@ internal static partial class Interop { internal static partial class Crypto { + [DllImport(Libraries.CryptoNative)] + private static extern SafeEvpPKeyHandle CryptoNative_RsaGenerateKey(int keySize); + + internal static SafeEvpPKeyHandle RsaGenerateKey(int keySize) + { + SafeEvpPKeyHandle pkey = CryptoNative_RsaGenerateKey(keySize); + + if (pkey.IsInvalid) + { + pkey.Dispose(); + throw CreateOpenSslCryptographicException(); + } + + return pkey; + } + + [DllImport(Libraries.CryptoNative)] + private static extern int CryptoNative_RsaDecrypt( + SafeEvpPKeyHandle pkey, + ref byte source, + int sourceLength, + RSAEncryptionPaddingMode paddingMode, + IntPtr digestAlgorithm, + ref byte destination, + int destinationLength); + + internal static int RsaDecrypt( + SafeEvpPKeyHandle pkey, + ReadOnlySpan source, + RSAEncryptionPaddingMode paddingMode, + IntPtr digestAlgorithm, + Span destination) + { + int written = CryptoNative_RsaDecrypt( + pkey, + ref MemoryMarshal.GetReference(source), + source.Length, + paddingMode, + digestAlgorithm, + ref MemoryMarshal.GetReference(destination), + destination.Length); + + if (written < 0) + { + Debug.Assert(written == -1); + throw CreateOpenSslCryptographicException(); + } + + return written; + } + + [DllImport(Libraries.CryptoNative)] + private static extern int CryptoNative_RsaSignHash( + SafeEvpPKeyHandle pkey, + RSASignaturePaddingMode paddingMode, + IntPtr digestAlgorithm, + ref byte hash, + int hashLength, + ref byte destination, + int destinationLength); + + internal static int RsaSignHash( + SafeEvpPKeyHandle pkey, + RSASignaturePaddingMode paddingMode, + IntPtr digestAlgorithm, + ReadOnlySpan hash, + Span destination) + { + int written = CryptoNative_RsaSignHash( + pkey, + paddingMode, + digestAlgorithm, + ref MemoryMarshal.GetReference(hash), + hash.Length, + ref MemoryMarshal.GetReference(destination), + destination.Length); + + if (written < 0) + { + Debug.Assert(written == -1); + throw CreateOpenSslCryptographicException(); + } + + return written; + } + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyGetRsa")] internal static extern SafeRsaHandle EvpPkeyGetRsa(SafeEvpPKeyHandle pkey); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs index fc948ea59560f..8a3fc840b1610 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EvpPkey.cs @@ -15,6 +15,9 @@ internal static partial class Crypto [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPkeyDestroy")] internal static extern void EvpPkeyDestroy(IntPtr pkey); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpPKeySize")] + internal static extern int EvpPKeySize(SafeEvpPKeyHandle pkey); + [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_UpRefEvpPkey")] internal static extern int UpRefEvpPkey(SafeEvpPKeyHandle handle); } diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs index ef439c56d845d..f902743fd020d 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs @@ -44,7 +44,7 @@ internal static X509VerifyStatusCode X509ChainGetCachedOcspStatus(SafeX509StoreC { X509VerifyStatusCode response = CryptoNative_X509ChainGetCachedOcspStatus(ctx, cachePath, chainDepth); - if (response < 0) + if (response.Code < 0) { Debug.Fail($"Unexpected response from X509ChainGetCachedOcspSuccess: {response}"); throw new CryptographicException(); @@ -70,7 +70,7 @@ internal static X509VerifyStatusCode X509ChainVerifyOcsp( { X509VerifyStatusCode response = CryptoNative_X509ChainVerifyOcsp(ctx, req, resp, cachePath, chainDepth); - if (response < 0) + if (response.Code < 0) { Debug.Fail($"Unexpected response from X509ChainGetCachedOcspSuccess: {response}"); throw new CryptographicException(); diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs index 48bd75cdc2996..2d6023cce3666 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Rsa.cs @@ -43,35 +43,6 @@ private static extern int RsaPublicEncrypt( SafeRsaHandle rsa, RsaPadding padding); - internal static int RsaPrivateDecrypt( - int flen, - ReadOnlySpan from, - Span to, - SafeRsaHandle rsa, - RsaPadding padding) => - RsaPrivateDecrypt(flen, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa, padding); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaPrivateDecrypt")] - private static extern int RsaPrivateDecrypt( - int flen, - ref byte from, - ref byte to, - SafeRsaHandle rsa, - RsaPadding padding); - - internal static int RsaSignPrimitive( - ReadOnlySpan from, - Span to, - SafeRsaHandle rsa) => - RsaSignPrimitive(from.Length, ref MemoryMarshal.GetReference(from), ref MemoryMarshal.GetReference(to), rsa); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSignPrimitive")] - private static extern int RsaSignPrimitive( - int flen, - ref byte from, - ref byte to, - SafeRsaHandle rsa); - internal static int RsaVerificationPrimitive( ReadOnlySpan from, Span to, @@ -88,16 +59,6 @@ private static extern int RsaVerificationPrimitive( [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSize")] internal static extern int RsaSize(SafeRsaHandle rsa); - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaGenerateKeyEx")] - internal static extern int RsaGenerateKeyEx(SafeRsaHandle rsa, int bits, SafeBignumHandle e); - - internal static bool RsaSign(int type, ReadOnlySpan m, int m_len, Span sigret, out int siglen, SafeRsaHandle rsa) => - RsaSign(type, ref MemoryMarshal.GetReference(m), m_len, ref MemoryMarshal.GetReference(sigret), out siglen, rsa); - - [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_RsaSign")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool RsaSign(int type, ref byte m, int m_len, ref byte sigret, out int siglen, SafeRsaHandle rsa); - internal static bool RsaVerify(int type, ReadOnlySpan m, ReadOnlySpan sigbuf, SafeRsaHandle rsa) { bool ret = RsaVerify( diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs index 696c132250eac..885fe0e312a0f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509.cs @@ -230,13 +230,13 @@ internal static bool X509StoreCtxRebuildChain(SafeX509StoreCtxHandle ctx) [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509StoreCtxSetVerifyCallback")] internal static extern void X509StoreCtxSetVerifyCallback(SafeX509StoreCtxHandle ctx, X509StoreVerifyCallback callback); - internal static string GetX509VerifyCertErrorString(X509VerifyStatusCode n) + internal static string GetX509VerifyCertErrorString(int n) { return Marshal.PtrToStringAnsi(X509VerifyCertErrorString(n))!; } [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509VerifyCertErrorString")] - private static extern IntPtr X509VerifyCertErrorString(X509VerifyStatusCode n); + private static extern IntPtr X509VerifyCertErrorString(int n); [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_X509CrlDestroy")] internal static extern void X509CrlDestroy(IntPtr a); @@ -253,11 +253,13 @@ internal static string GetX509VerifyCertErrorString(X509VerifyStatusCode n) [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EncodeX509SubjectPublicKeyInfo")] internal static extern int EncodeX509SubjectPublicKeyInfo(SafeX509Handle x509, byte[] buf); - internal enum X509VerifyStatusCode : int + internal enum X509VerifyStatusCodeUniversal { X509_V_OK = 0, + X509_V_ERR_UNSPECIFIED = 1, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT = 2, X509_V_ERR_UNABLE_TO_GET_CRL = 3, + X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE = 4, X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE = 5, X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY = 6, X509_V_ERR_CERT_SIGNATURE_FAILURE = 7, @@ -277,18 +279,25 @@ internal enum X509VerifyStatusCode : int X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21, X509_V_ERR_CERT_CHAIN_TOO_LONG = 22, X509_V_ERR_CERT_REVOKED = 23, - X509_V_ERR_INVALID_CA = 24, + + // Code 24 varies. + X509_V_ERR_PATH_LENGTH_EXCEEDED = 25, X509_V_ERR_INVALID_PURPOSE = 26, X509_V_ERR_CERT_UNTRUSTED = 27, X509_V_ERR_CERT_REJECTED = 28, + X509_V_ERR_SUBJECT_ISSUER_MISMATCH = 29, + X509_V_ERR_AKID_SKID_MISMATCH = 30, + X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH = 31, X509_V_ERR_KEYUSAGE_NO_CERTSIGN = 32, X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER = 33, X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION = 34, X509_V_ERR_KEYUSAGE_NO_CRL_SIGN = 35, X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION = 36, X509_V_ERR_INVALID_NON_CA = 37, + X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED = 38, X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE = 39, + X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED = 40, X509_V_ERR_INVALID_EXTENSION = 41, X509_V_ERR_INVALID_POLICY_EXTENSION = 42, X509_V_ERR_NO_EXPLICIT_POLICY = 43, @@ -303,7 +312,6 @@ internal enum X509VerifyStatusCode : int X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX = 52, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX = 53, X509_V_ERR_CRL_PATH_VALIDATION_ERROR = 54, - X509_V_ERR_PATH_LOOP = 55, X509_V_ERR_SUITE_B_INVALID_VERSION = 56, X509_V_ERR_SUITE_B_INVALID_ALGORITHM = 57, X509_V_ERR_SUITE_B_INVALID_CURVE = 58, @@ -313,6 +321,41 @@ internal enum X509VerifyStatusCode : int X509_V_ERR_HOSTNAME_MISMATCH = 62, X509_V_ERR_EMAIL_MISMATCH = 63, X509_V_ERR_IP_ADDRESS_MISMATCH = 64, + } + internal enum X509VerifyStatusCode102 + { + X509_V_ERR_INVALID_CA = 24, + + X509_V_ERR_INVALID_CALL = 65, + X509_V_ERR_STORE_LOOKUP = 66, + X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION = 67, + } + + internal enum X509VerifyStatusCode111 + { + X509_V_ERR_INVALID_CA = 24, + + X509_V_ERR_DANE_NO_MATCH = 65, + X509_V_ERR_EE_KEY_TOO_SMALL = 66, + X509_V_ERR_CA_KEY_TOO_SMALL = 67, + X509_V_ERR_CA_MD_TOO_WEAK = 68, + X509_V_ERR_INVALID_CALL = 69, + X509_V_ERR_STORE_LOOKUP = 70, + X509_V_ERR_NO_VALID_SCTS = 71, + X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION = 72, + X509_V_ERR_OCSP_VERIFY_NEEDED = 73, + X509_V_ERR_OCSP_VERIFY_FAILED = 74, + X509_V_ERR_OCSP_CERT_UNKNOWN = 75, + X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH = 76, + X509_V_ERR_NO_ISSUER_PUBLIC_KEY = 77, + X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM = 78, + X509_V_ERR_EC_KEY_EXPLICIT_PARAMS = 79, + } + + internal enum X509VerifyStatusCode30 + { + X509_V_ERR_NO_ISSUER_PUBLIC_KEY = 24, + X509_V_ERR_DANE_NO_MATCH = 65, X509_V_ERR_EE_KEY_TOO_SMALL = 66, X509_V_ERR_CA_KEY_TOO_SMALL = 67, @@ -324,6 +367,62 @@ internal enum X509VerifyStatusCode : int X509_V_ERR_OCSP_VERIFY_NEEDED = 73, X509_V_ERR_OCSP_VERIFY_FAILED = 74, X509_V_ERR_OCSP_CERT_UNKNOWN = 75, + X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM = 76, + X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH = 77, + X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY = 78, + X509_V_ERR_INVALID_CA = 79, + X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA = 80, + X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN = 81, + X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA = 82, + X509_V_ERR_ISSUER_NAME_EMPTY = 83, + X509_V_ERR_SUBJECT_NAME_EMPTY = 84, + X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER = 85, + X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER = 86, + X509_V_ERR_EMPTY_SUBJECT_ALT_NAME = 87, + X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL = 88, + X509_V_ERR_CA_BCONS_NOT_CRITICAL = 89, + X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL = 90, + X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL = 91, + X509_V_ERR_CA_CERT_MISSING_KEY_USAGE = 92, + X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3 = 93, + X509_V_ERR_EC_KEY_EXPLICIT_PARAMS = 94, + } + + internal readonly struct X509VerifyStatusCode : IEquatable + { + internal static readonly X509VerifyStatusCode X509_V_OK = X509VerifyStatusCodeUniversal.X509_V_OK; + + public int Code { get; } + + internal X509VerifyStatusCode(int code) + { + Code = code; + } + + public X509VerifyStatusCodeUniversal UniversalCode => (X509VerifyStatusCodeUniversal)Code; + public X509VerifyStatusCode102 Code102 => (X509VerifyStatusCode102)Code; + public X509VerifyStatusCode111 Code111 => (X509VerifyStatusCode111)Code; + public X509VerifyStatusCode30 Code30 => (X509VerifyStatusCode30)Code; + + public bool Equals(X509VerifyStatusCode other) => Code == other.Code; + + public override bool Equals(object? obj) => obj is X509VerifyStatusCode other && Equals(other); + + public override int GetHashCode() => Code.GetHashCode(); + + public static bool operator ==(X509VerifyStatusCode left, X509VerifyStatusCode right) => left.Equals(right); + + public static bool operator !=(X509VerifyStatusCode left, X509VerifyStatusCode right) => !left.Equals(right); + + public static explicit operator X509VerifyStatusCode(int code) + { + return new X509VerifyStatusCode(code); + } + + public static implicit operator X509VerifyStatusCode(X509VerifyStatusCodeUniversal code) + { + return new X509VerifyStatusCode((int)code); + } } } } diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AdjustTokenPrivileges.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AdjustTokenPrivileges.cs index 609e3cd331478..b93d0660e61ab 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AdjustTokenPrivileges.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AdjustTokenPrivileges.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] internal static extern unsafe bool AdjustTokenPrivileges( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AllocateLocallyUniqueId.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AllocateLocallyUniqueId.cs index 035db9fcbccfa..689f8328d5207 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AllocateLocallyUniqueId.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.AllocateLocallyUniqueId.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32)] internal static extern bool AllocateLocallyUniqueId(out LUID Luid); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs index d52882ea32750..753a310ea2084 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ChangeServiceConfig2.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool ChangeServiceConfig2(SafeServiceHandle serviceHandle, uint infoLevel, ref SERVICE_DESCRIPTION serviceDesc); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClaimSecurityAttributes.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClaimSecurityAttributes.cs index 37692663b0de6..bb0fb076e3f54 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClaimSecurityAttributes.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClaimSecurityAttributes.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { [StructLayout(LayoutKind.Explicit)] internal struct CLAIM_SECURITY_ATTRIBUTE_INFORMATION_V1 diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClearEventLog.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClearEventLog.cs index 530ed7b664396..a432d1d8dba05 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClearEventLog.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ClearEventLog.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool ClearEventLog(SafeEventLogReadHandle hEventLog, string lpBackupFileName); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseEventLog.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseEventLog.cs index 6ee970066f502..fcede9daf4f92 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseEventLog.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseEventLog.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] internal static extern bool CloseEventLog(IntPtr hEventLog); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseServiceHandle.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseServiceHandle.cs index 19373640faf41..d1103c58f8526 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseServiceHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CloseServiceHandle.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool CloseServiceHandle(IntPtr handle); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ControlService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ControlService.cs index addda32e5d341..4b74a032ad124 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ControlService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ControlService.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe bool ControlService(SafeServiceHandle serviceHandle, int control, SERVICE_STATUS* pStatus); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ConvertStringSecurityDescriptorToSecurityDescriptor.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ConvertStringSecurityDescriptorToSecurityDescriptor.cs index 838c1124947da..1cda9c2b58304 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ConvertStringSecurityDescriptorToSecurityDescriptor.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ConvertStringSecurityDescriptorToSecurityDescriptor.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Interop.Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)] internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateProcessWithLogon.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateProcessWithLogon.cs index 276f6c933acf3..60f2745df3d6c 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateProcessWithLogon.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateProcessWithLogon.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true, BestFitMapping = false, EntryPoint = "CreateProcessWithLogonW")] internal static extern unsafe bool CreateProcessWithLogonW( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs index c9a4249741a0a..593b0bd2a62c8 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CreateService.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr CreateService(SafeServiceHandle databaseHandle, string serviceName, string displayName, int access, int serviceType, diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptAcquireContext.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptAcquireContext.cs index d0c9c9d0509ed..903733feee10d 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptAcquireContext.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptAcquireContext.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [Flags] internal enum CryptAcquireContextFlags : uint diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptCreateHash.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptCreateHash.cs index a49519557eeab..679b82aee2a5e 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptCreateHash.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptCreateHash.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [Flags] internal enum CryptCreateHashFlags : int diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDecrypt.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDecrypt.cs index ae1b5c3c379ee..0761afdf79d77 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDecrypt.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDecrypt.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { internal enum CryptDecryptFlags : int { diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDeriveKey.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDeriveKey.cs index b2c6ca2e980b2..5afc729706e37 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDeriveKey.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDeriveKey.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool CryptDeriveKey( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyHash.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyHash.cs index 6dbe44b672045..f0e94194bef77 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyHash.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyHash.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CryptDestroyHash(IntPtr hHash); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyKey.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyKey.cs index 6a4a841e47dd4..948165d336783 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyKey.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptDestroyKey.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CryptDestroyKey(IntPtr hKey); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptEncrypt.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptEncrypt.cs index 933f5a75931db..15f4683bf379b 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptEncrypt.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptEncrypt.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CryptEncrypt( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptExportKey.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptExportKey.cs index f29b7b1585eee..1e878fc9ae53d 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptExportKey.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptExportKey.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CryptExportKey( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGenKey.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGenKey.cs index 26a615ff116ff..6518252da2847 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGenKey.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGenKey.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool CryptGenKey(SafeProvHandle hProv, int Algid, int dwFlags, out SafeKeyHandle phKey); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetDefaultProvider.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetDefaultProvider.cs index 3a264330a3275..99a82e00cc90a 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetDefaultProvider.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetDefaultProvider.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { internal enum GetDefaultProviderFlags : int { diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetHashParam.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetHashParam.cs index e503a7575ef48..3b45b4f885f40 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetHashParam.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetHashParam.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { internal enum CryptHashProperty : int { diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetKeyParam.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetKeyParam.cs index b7e64bca26c40..880e2aa3ac8db 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetKeyParam.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetKeyParam.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { internal enum CryptGetKeyParamFlags : int { diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetUserKey.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetUserKey.cs index b35ee72d6e135..d1078c88ff705 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetUserKey.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptGetUserKey.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] internal static extern bool CryptGetUserKey(SafeProvHandle hProv, int dwKeySpec, out SafeKeyHandle phUserKey); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptHashData.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptHashData.cs index 1670380fb6f2e..c655276a9b2cd 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptHashData.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptHashData.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool CryptHashData(SafeHashHandle hHash, byte[] pbData, int dwDataLen, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs index 242e97141ca5c..f1266457cbe01 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptImportKey.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe bool CryptImportKey( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptReleaseContext.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptReleaseContext.cs index 0a021497f3038..dd5922d21f64f 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptReleaseContext.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptReleaseContext.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] public static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSetKeyParam.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSetKeyParam.cs index f1ee9ed2bda8f..568d6bce51416 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSetKeyParam.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSetKeyParam.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] public static extern bool CryptSetKeyParam(SafeKeyHandle hKey, int dwParam, byte[] pbData, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSignHash.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSignHash.cs index 70e366f6fefc9..ce8c93da4a574 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSignHash.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.CryptSignHash.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { internal enum KeySpec : int { diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs index a6c4cfa507e67..b770ff4febbc8 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeleteService.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool DeleteService(SafeServiceHandle serviceHandle); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeregisterEventSource.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeregisterEventSource.cs index e377137c8c8e8..b4f712313ed69 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeregisterEventSource.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.DeregisterEventSource.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] internal static extern bool DeregisterEventSource(IntPtr hEventLog); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS.cs index 00d0c20eca893..958335fd991f7 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS.cs @@ -3,12 +3,12 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal class ENUM_SERVICE_STATUS + internal sealed class ENUM_SERVICE_STATUS { internal string? serviceName; internal string? displayName; diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS_PROCESS.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS_PROCESS.cs index 8fcd89df9a1cb..09afde186a0e8 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS_PROCESS.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ENUM_SERVICE_STATUS_PROCESS.cs @@ -3,12 +3,12 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal class ENUM_SERVICE_STATUS_PROCESS + internal sealed class ENUM_SERVICE_STATUS_PROCESS { internal string? serviceName; internal string? displayName; diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EncryptDecrypt.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EncryptDecrypt.cs index 705af9477cdf3..14cf346be531f 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EncryptDecrypt.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EncryptDecrypt.cs @@ -4,9 +4,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { /// /// WARNING: This method does not implicitly handle long paths. Use EncryptFile. diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumDependentServices.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumDependentServices.cs index bb54095e2437c..e5112e3d52570 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumDependentServices.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumDependentServices.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "EnumDependentServicesW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool EnumDependentServices( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumServicesStatusEx.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumServicesStatusEx.cs index 32d23d96d4855..d8ad4ae03014e 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumServicesStatusEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.EnumServicesStatusEx.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "EnumServicesStatusExW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool EnumServicesStatusEx( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetNumberOfEventLogRecords.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetNumberOfEventLogRecords.cs index cd1abb0f117df..67807ae41f42c 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetNumberOfEventLogRecords.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetNumberOfEventLogRecords.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool GetNumberOfEventLogRecords(SafeEventLogReadHandle hEventLog, out int NumberOfRecords); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetOldestEventLogRecord.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetOldestEventLogRecord.cs index b71998067fb26..a3cf76cb34d78 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetOldestEventLogRecord.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetOldestEventLogRecord.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceDisplayName.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceDisplayName.cs index 4800e11530f33..c69084f20f83b 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceDisplayName.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceDisplayName.cs @@ -8,9 +8,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "GetServiceDisplayNameW", CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true)] internal static extern unsafe bool GetServiceDisplayName(SafeServiceHandle? SCMHandle, string serviceName, char* displayName, ref int displayNameLength); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceKeyName.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceKeyName.cs index 85251b354ae07..90169a489766a 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceKeyName.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.GetServiceKeyName.cs @@ -8,9 +8,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "GetServiceKeyNameW", CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true)] internal static extern unsafe bool GetServiceKeyName(SafeServiceHandle? SCMHandle, string displayName, char* KeyName, ref int KeyNameLength); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LSA_STRING.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LSA_STRING.cs index 3af18d2e61585..e5e3172ae7ce0 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LSA_STRING.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LSA_STRING.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential)] internal struct LSA_STRING diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LUID.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LUID.cs index cb1228ab2e617..dd4418904b8e7 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LUID.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LUID.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential)] internal struct LUID diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupAccountSid.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupAccountSid.cs index 4af5332708040..742ba2d25df2c 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupAccountSid.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupAccountSid.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Interop.Libraries.Advapi32, CharSet = CharSet.Unicode, EntryPoint = "LookupAccountSidW", SetLastError = true)] public static extern unsafe int LookupAccountSid( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupPrivilegeValue.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupPrivilegeValue.cs index 93c99a01a1063..af16874c9bd7b 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupPrivilegeValue.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.LookupPrivilegeValue.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "LookupPrivilegeValueW")] internal static extern bool LookupPrivilegeValue([MarshalAs(UnmanagedType.LPTStr)] string? lpSystemName, [MarshalAs(UnmanagedType.LPTStr)] string lpName, out LUID lpLuid); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.NotifyChangeEventLog.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.NotifyChangeEventLog.cs index 542f614b7d184..7a7db2f748f26 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.NotifyChangeEventLog.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.NotifyChangeEventLog.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenEventLog.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenEventLog.cs index 72b5138b904e1..31483c83304ad 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenEventLog.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenEventLog.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeEventLogReadHandle OpenEventLog(string lpUNCServerName, string lpSourceName); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken.cs index 7e8e225ebbb98..3e992403871ce 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, out SafeTokenHandle TokenHandle); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken_IntPtr.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken_IntPtr.cs index 4283336a9ad5a..e709f3263a9d3 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken_IntPtr.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenProcessToken_IntPtr.cs @@ -6,9 +6,9 @@ using System; using System.Security.Principal; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool OpenProcessToken( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenSCManager.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenSCManager.cs index a4b64cee88735..4d661e0870ec6 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenSCManager.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenSCManager.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "OpenSCManagerW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr OpenSCManager(string? machineName, string? databaseName, int access); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenService.cs index 591355a9b9c63..23a432f12cc0a 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.OpenService.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "OpenServiceW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr OpenService(SafeServiceHandle? databaseHandle, string serviceName, int access); 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 2d65e77e2a215..97d6de64b831f 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 @@ -5,9 +5,9 @@ using System.Globalization; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential)] internal struct PERF_COUNTER_BLOCK diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ProcessOptions.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ProcessOptions.cs index 8fd9e6071cb44..c651e1738e173 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ProcessOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ProcessOptions.cs @@ -1,22 +1,22 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { - internal partial class SEPrivileges + internal static partial class SEPrivileges { internal const uint SE_PRIVILEGE_DISABLED = 0; internal const int SE_PRIVILEGE_ENABLED = 2; } - internal partial class PerfCounterOptions + internal static partial class PerfCounterOptions { internal const int NtPerfCounterSizeLarge = 0x00000100; } - internal partial class ProcessOptions + internal static partial class ProcessOptions { internal const int PROCESS_TERMINATE = 0x0001; internal const int PROCESS_VM_READ = 0x0010; @@ -31,13 +31,13 @@ internal partial class ProcessOptions internal const int SYNCHRONIZE = 0x00100000; } - internal partial class RPCStatus + internal static partial class RPCStatus { internal const int RPC_S_SERVER_UNAVAILABLE = 1722; internal const int RPC_S_CALL_FAILED = 1726; } - internal partial class StartupInfoOptions + internal static partial class StartupInfoOptions { internal const int STARTF_USESTDHANDLES = 0x00000100; internal const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QUERY_SERVICE_CONFIG.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QUERY_SERVICE_CONFIG.cs index 1cc8c26d6513b..15c2b8d524025 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QUERY_SERVICE_CONFIG.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QUERY_SERVICE_CONFIG.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential)] internal unsafe class QUERY_SERVICE_CONFIG diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceConfig.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceConfig.cs index 6aeaf40d23882..f3e99c1a69c74 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceConfig.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceConfig.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "QueryServiceConfigW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool QueryServiceConfig(SafeServiceHandle serviceHandle, IntPtr queryServiceConfigPtr, int bufferSize, out int bytesNeeded); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceStatus.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceStatus.cs index c71d58b220373..19003e0e3982c 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceStatus.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.QueryServiceStatus.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe bool QueryServiceStatus(SafeServiceHandle serviceHandle, SERVICE_STATUS* pStatus); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReadEventLog.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReadEventLog.cs index 10da56b3dd95c..5cbc26db4d0ca 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReadEventLog.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReadEventLog.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { internal const int SEEK_READ = 0x2; internal const int FORWARDS_READ = 0x4; diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegConnectRegistry.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegConnectRegistry.cs index 6e31334405ed2..fe0dc958c36d4 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegConnectRegistry.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegConnectRegistry.cs @@ -10,9 +10,9 @@ using Internal.Win32.SafeHandles; #endif -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, BestFitMapping = false, EntryPoint = "RegConnectRegistryW")] internal static extern int RegConnectRegistry(string machineName, SafeRegistryHandle key, out SafeRegistryHandle result); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterEventSource.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterEventSource.cs index 95763ba4f5e50..eadabdceaca79 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterEventSource.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterEventSource.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeEventLogWriteHandle RegisterEventSource(string lpUNCServerName, string lpSourceName); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterServiceCtrlHandlerEx.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterServiceCtrlHandlerEx.cs index 716e9f534dfc3..ea198bd4d6e74 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterServiceCtrlHandlerEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.RegisterServiceCtrlHandlerEx.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { public delegate int ServiceControlCallbackEx(int control, int eventType, IntPtr eventData, IntPtr eventContext); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReportEvent.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReportEvent.cs index 6da0f1117b6e1..ee3e1943beed0 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReportEvent.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ReportEvent.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool ReportEvent( diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DELAYED_AUTOSTART_INFO.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DELAYED_AUTOSTART_INFO.cs index 6b7ec4935f5b4..6319225c17d13 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DELAYED_AUTOSTART_INFO.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DELAYED_AUTOSTART_INFO.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SERVICE_DELAYED_AUTOSTART_INFO diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DESCRIPTION.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DESCRIPTION.cs index 71581abc32ff2..8c65c2987deae 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DESCRIPTION.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_DESCRIPTION.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SERVICE_DESCRIPTION diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_STATUS.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_STATUS.cs index 2038ee7b4289d..94596bff36a63 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_STATUS.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_STATUS.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential)] internal struct SERVICE_STATUS diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_TABLE_ENTRY.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_TABLE_ENTRY.cs index 69d34ea23bce9..6986612e8d871 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_TABLE_ENTRY.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SERVICE_TABLE_ENTRY.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { public delegate void ServiceMainCallback(int argCount, IntPtr argPointer); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ServiceProcessOptions.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ServiceProcessOptions.cs index 53c9b1b2543ca..d7d9d5eae2c4a 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ServiceProcessOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.ServiceProcessOptions.cs @@ -4,11 +4,11 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { - internal partial class AcceptOptions + internal static partial class AcceptOptions { internal const int ACCEPT_POWEREVENT = 0x00000040; internal const int ACCEPT_PAUSE_CONTINUE = 0x00000002; @@ -17,7 +17,7 @@ internal partial class AcceptOptions internal const int ACCEPT_STOP = 0x00000001; } - internal partial class ControlOptions + internal static partial class ControlOptions { internal const int CONTROL_CONTINUE = 0x00000003; internal const int CONTROL_INTERROGATE = 0x00000004; @@ -28,14 +28,14 @@ internal partial class ControlOptions internal const int CONTROL_STOP = 0x00000001; } - internal partial class ServiceConfigOptions + internal static partial class ServiceConfigOptions { internal const int SERVICE_CONFIG_DESCRIPTION = 0x00000001; internal const int SERVICE_CONFIG_FAILURE_ACTIONS = 0x00000002; internal const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 0x00000003; } - internal partial class ServiceOptions + internal static partial class ServiceOptions { internal const int SERVICE_QUERY_CONFIG = 0x0001; internal const int SERVICE_CHANGE_CONFIG = 0x0002; @@ -63,7 +63,7 @@ internal partial class ServiceOptions internal const int STANDARD_RIGHTS_REQUIRED = 0x000F0000; } - internal partial class ServiceTypeOptions + internal static partial class ServiceTypeOptions { internal const int SERVICE_TYPE_ADAPTER = 0x00000004; internal const int SERVICE_TYPE_FILE_SYSTEM_DRIVER = 0x00000002; @@ -86,7 +86,7 @@ internal partial class ServiceTypeOptions SERVICE_TYPE_INTERACTIVE_PROCESS; } - internal partial class ServiceAccessOptions + internal static partial class ServiceAccessOptions { internal const int ACCESS_TYPE_CHANGE_CONFIG = 0x0002; internal const int ACCESS_TYPE_ENUMERATE_DEPENDENTS = 0x0008; @@ -110,7 +110,7 @@ internal partial class ServiceAccessOptions ACCESS_TYPE_USER_DEFINED_CONTROL; } - internal partial class ServiceStartModes + internal static partial class ServiceStartModes { internal const int START_TYPE_BOOT = 0x00000000; internal const int START_TYPE_SYSTEM = 0x00000001; @@ -119,21 +119,21 @@ internal partial class ServiceStartModes internal const int START_TYPE_DISABLED = 0x00000004; } - internal partial class ServiceState + internal static partial class ServiceState { internal const int SERVICE_ACTIVE = 1; internal const int SERVICE_INACTIVE = 2; internal const int SERVICE_STATE_ALL = SERVICE_ACTIVE | SERVICE_INACTIVE; } - internal partial class StatusOptions + internal static partial class StatusOptions { internal const int STATUS_ACTIVE = 0x00000001; internal const int STATUS_INACTIVE = 0x00000002; internal const int STATUS_ALL = STATUS_ACTIVE | STATUS_INACTIVE; } - internal partial class ServiceControlStatus + internal static partial class ServiceControlStatus { internal const int STATE_CONTINUE_PENDING = 0x00000005; internal const int STATE_PAUSED = 0x00000007; @@ -145,7 +145,7 @@ internal partial class ServiceControlStatus internal const int ERROR_EXCEPTION_IN_SERVICE = 0x00000428; } - internal partial class ServiceStartErrorModes + internal static partial class ServiceStartErrorModes { internal const int ERROR_CONTROL_CRITICAL = 0x00000003; internal const int ERROR_CONTROL_IGNORE = 0x00000000; @@ -153,7 +153,7 @@ internal partial class ServiceStartErrorModes internal const int ERROR_CONTROL_SEVERE = 0x00000002; } - internal partial class ServiceControllerOptions + internal static partial class ServiceControllerOptions { internal const int SC_ENUM_PROCESS_INFO = 0; internal const int SC_MANAGER_CONNECT = 0x0001; @@ -172,7 +172,7 @@ internal partial class ServiceControllerOptions SC_MANAGER_MODIFY_BOOT_CONFIG; } - internal partial class PowerBroadcastStatus + internal static partial class PowerBroadcastStatus { internal const int PBT_APMBATTERYLOW = 0x0009; internal const int PBT_APMOEMEVENT = 0x000B; @@ -185,7 +185,7 @@ internal partial class PowerBroadcastStatus internal const int PBT_APMSUSPEND = 0x0004; } - internal partial class SessionStateChange + internal static partial class SessionStateChange { internal const int WTS_CONSOLE_CONNECT = 0x1; internal const int WTS_CONSOLE_DISCONNECT = 0x2; diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetServiceStatus.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetServiceStatus.cs index b64c87c9f2df6..78d53da1697ab 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetServiceStatus.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetServiceStatus.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern unsafe bool SetServiceStatus(IntPtr serviceStatusHandle, SERVICE_STATUS* status); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetThreadToken.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetThreadToken.cs index a9bb4a95d9294..f3c64ca98d189 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetThreadToken.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.SetThreadToken.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, SetLastError = true)] internal static extern bool SetThreadToken(IntPtr ThreadHandle, SafeTokenHandle? hToken); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartService.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartService.cs index a1af7cdeb4653..d56a472d92537 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartService.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartService.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, EntryPoint = "StartServiceW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool StartService(SafeServiceHandle serviceHandle, int argNum, IntPtr argPtrs); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartServiceCtrlDispatcher.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartServiceCtrlDispatcher.cs index 32935b86b1d38..6a9f9f66e0c35 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartServiceCtrlDispatcher.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.StartServiceCtrlDispatcher.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern bool StartServiceCtrlDispatcher(IntPtr entry); diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.WTSSESSION_NOTIFICATION.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.WTSSESSION_NOTIFICATION.cs index a788481eaa37f..2979292ab97c2 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.WTSSESSION_NOTIFICATION.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.WTSSESSION_NOTIFICATION.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Advapi32 + internal static partial class Advapi32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public class WTSSESSION_NOTIFICATION diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/BCryptAlgorithmCache.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/BCryptAlgorithmCache.cs index e495659bd0f96..822090b10b18c 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/BCryptAlgorithmCache.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/BCryptAlgorithmCache.cs @@ -8,9 +8,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static class BCryptAlgorithmCache { diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.AsymmetricEncryption.Types.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.AsymmetricEncryption.Types.cs index 83b338bb56dff..0312ded5f364a 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.AsymmetricEncryption.Types.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.AsymmetricEncryption.Types.cs @@ -5,12 +5,12 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { /// /// BCrypt types related to asymmetric encryption algorithms /// - internal partial class BCrypt + internal static partial class BCrypt { [StructLayout(LayoutKind.Sequential)] internal struct BCRYPT_OAEP_PADDING_INFO diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptAlgPseudoHandle.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptAlgPseudoHandle.cs index 8ebae44095261..9886f2b8f4921 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptAlgPseudoHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptAlgPseudoHandle.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { // Pseudo-handles, as defined in bcrypt.h // TODO: This really should be backed by 'nuint' (see https://github.com/dotnet/roslyn/issues/44110) @@ -19,5 +19,7 @@ public enum BCryptAlgPseudoHandle : uint BCRYPT_SHA512_ALG_HANDLE = 0x00000061, BCRYPT_PBKDF2_ALG_HANDLE = 0x00000331, } + + internal static bool PseudoHandlesSupported { get; } = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 0); } } diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptChainingModes.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptChainingModes.cs index 6c4b9f1c4f548..4a5b7df46c67d 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptChainingModes.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptChainingModes.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal const string BCRYPT_CHAIN_MODE_CBC = "ChainingModeCBC"; internal const string BCRYPT_CHAIN_MODE_ECB = "ChainingModeECB"; diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCloseAlgorithmProvider.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCloseAlgorithmProvider.cs index 0276b1887c3d9..1a08654c8b2b2 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCloseAlgorithmProvider.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCloseAlgorithmProvider.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern NTSTATUS BCryptCloseAlgorithmProvider(IntPtr hAlgorithm, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCreateHash.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCreateHash.cs index b4f6f02b8a26a..bd3578a6a7b0d 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCreateHash.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptCreateHash.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static NTSTATUS BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm, out SafeBCryptHashHandle phHash, IntPtr pbHashObject, int cbHashObject, ReadOnlySpan secret, int cbSecret, BCryptCreateHashFlags dwFlags) { diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDeriveKeyPBKDF2.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDeriveKeyPBKDF2.cs index d514f2b8e5471..88ff68b7ae1c2 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDeriveKeyPBKDF2.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDeriveKeyPBKDF2.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern unsafe NTSTATUS BCryptDeriveKeyPBKDF2( diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyHash.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyHash.cs index b7753d110c442..5466f24e6eec3 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyHash.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyHash.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern NTSTATUS BCryptDestroyHash(IntPtr hHash); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyKey.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyKey.cs index 2ee52a0ac42f7..329d22cd14903 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyKey.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDestroyKey.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern NTSTATUS BCryptDestroyKey(IntPtr hKey); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDuplicateHash.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDuplicateHash.cs index e3a6c604a1bef..5635873ad17e5 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDuplicateHash.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptDuplicateHash.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static SafeBCryptHashHandle BCryptDuplicateHash(SafeBCryptHashHandle hHash) { diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptExportKey.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptExportKey.cs index 4c595ec0f7a33..8f5936af4ce06 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptExportKey.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptExportKey.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern NTSTATUS BCryptExportKey(SafeBCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, [Out] byte[]? pbOutput, int cbOutput, out int pcbResult, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs index 4351bdf1cc0d7..bf1b5a6571290 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptFinishHash.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static NTSTATUS BCryptFinishHash(SafeBCryptHashHandle hHash, Span pbOutput, int cbOutput, int dwFlags) => BCryptFinishHash(hHash, ref MemoryMarshal.GetReference(pbOutput), cbOutput, dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs index 0f829c5d33ea9..2d3d2364b87ad 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static unsafe extern NTSTATUS BCryptGenerateSymmetricKey( diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGetProperty.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGetProperty.cs index dafa5d27cc42f..b052cfead2db4 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGetProperty.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGetProperty.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern unsafe NTSTATUS BCryptGetProperty(SafeBCryptHandle hObject, string pszProperty, void* pbOutput, int cbOutput, out int pcbResult, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHash.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHash.cs index 530e7b2c97daf..e93128c465027 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHash.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHash.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static unsafe extern NTSTATUS BCryptHash(nuint hAlgorithm, byte* pbSecret, int cbSecret, byte* pbInput, int cbInput, byte* pbOutput, int cbOutput); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs index 73dc23d639da9..445a67b8f0f12 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptHashData.cs @@ -6,9 +6,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static NTSTATUS BCryptHashData(SafeBCryptHashHandle hHash, ReadOnlySpan pbInput, int cbInput, int dwFlags) => BCryptHashData(hHash, ref MemoryMarshal.GetReference(pbInput), cbInput, dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptImportKey.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptImportKey.cs index 848ef7f57c2fb..5ee600d5283a9 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptImportKey.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptImportKey.cs @@ -7,9 +7,9 @@ using Internal.NativeCrypto; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static unsafe SafeKeyHandle BCryptImportKey(SafeAlgorithmHandle hAlg, ReadOnlySpan key) { diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDataBlob.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDataBlob.cs index 68aaee607d1c6..10b6b905ee62a 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDataBlob.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDataBlob.cs @@ -5,9 +5,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal const int BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4d42444b; // 'KDBM' internal const int BCRYPT_KEY_DATA_BLOB_VERSION1 = 1; diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDerivation.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDerivation.cs index dfe2c7d3f32f7..e9c93a367908d 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDerivation.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptKeyDerivation.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static unsafe extern NTSTATUS BCryptKeyDerivation( diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptOpenAlgorithmProvider.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptOpenAlgorithmProvider.cs index 8532b0adb65ed..65b457f046ed0 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptOpenAlgorithmProvider.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptOpenAlgorithmProvider.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { [DllImport(Libraries.BCrypt, CharSet = CharSet.Unicode)] internal static extern NTSTATUS BCryptOpenAlgorithmProvider(out SafeBCryptAlgorithmHandle phAlgorithm, string pszAlgId, string? pszImplementation, BCryptOpenAlgorithmProviderFlags dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptPropertyStrings.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptPropertyStrings.cs index 6b29607172cdf..3636abdea7efd 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptPropertyStrings.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptPropertyStrings.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static class BCryptPropertyStrings { diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.Blobs.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.Blobs.cs index 374a80eb7c67f..302e2aff8641c 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.Blobs.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.Blobs.cs @@ -5,12 +5,12 @@ using System.Diagnostics; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { // // These structures define the layout of CNG key blobs passed to NCryptImportKey // - internal partial class BCrypt + internal static partial class BCrypt { /// /// Append "value" to the data already in blob. diff --git a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.CreateCryptographicException.cs b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.CreateCryptographicException.cs index 0d94e695ffc6c..0755e9614105c 100644 --- a/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.CreateCryptographicException.cs +++ b/src/libraries/Common/src/Interop/Windows/BCrypt/Interop.CreateCryptographicException.cs @@ -4,9 +4,9 @@ using System; using Internal.Cryptography; -internal partial class Interop +internal static partial class Interop { - internal partial class BCrypt + internal static partial class BCrypt { internal static Exception CreateCryptographicException(NTSTATUS ntStatus) { diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CertEnumCertificatesInStore.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CertEnumCertificatesInStore.cs index 1004313f9cb04..b6d12f6ca92ac 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CertEnumCertificatesInStore.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CertEnumCertificatesInStore.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Crypt32 + internal static partial class Crypt32 { [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern unsafe CERT_CONTEXT* CertEnumCertificatesInStore(IntPtr hCertStore, CERT_CONTEXT* pPrevCertContext); diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptFormatObject.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptFormatObject.cs index 4e5f747211b04..b9c3418823084 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptFormatObject.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptFormatObject.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Crypt32 + internal static partial class Crypt32 { internal const int CRYPT_FORMAT_STR_NONE = 0; internal const int CRYPT_FORMAT_STR_MULTI_LINE = 0x00000001; diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectData.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectData.cs index d5fb606cfe48b..bf6cc60b98503 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectData.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectData.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Security; -internal partial class Interop +internal static partial class Interop { - internal partial class Crypt32 + internal static partial class Crypt32 { [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectDataFlags.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectDataFlags.cs index 906cb654be3df..a424bd6cc58ba 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectDataFlags.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptProtectDataFlags.cs @@ -3,9 +3,9 @@ using System; -internal partial class Interop +internal static partial class Interop { - internal partial class Crypt32 + internal static partial class Crypt32 { [Flags] internal enum CryptProtectDataFlags : int diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptUnprotectData.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptUnprotectData.cs index 87a7919dc8a30..ab32e33d59f02 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptUnprotectData.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.CryptUnprotectData.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Security; -internal partial class Interop +internal static partial class Interop { - internal partial class Crypt32 + internal static partial class Crypt32 { [DllImport(Libraries.Crypt32, CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.DATA_BLOB.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.DATA_BLOB.cs index bb28fd4beda44..eb1dadf2a782b 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.DATA_BLOB.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.DATA_BLOB.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Crypt32 + internal static partial class Crypt32 { [StructLayout(LayoutKind.Sequential)] internal struct DATA_BLOB diff --git a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.HashIdAlg.cs b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.HashIdAlg.cs index 27f6e8b70049d..d9aac15e5d311 100644 --- a/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.HashIdAlg.cs +++ b/src/libraries/Common/src/Interop/Windows/Crypt32/Interop.HashIdAlg.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; -internal partial class Interop +internal static partial class Interop { internal static partial class Crypt32 { diff --git a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs index 6c9b8748919f3..976a6396118e1 100644 --- a/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs +++ b/src/libraries/Common/src/Interop/Windows/CryptUI/Interop.CryptUIDlgCertificate.cs @@ -10,7 +10,7 @@ internal static partial class Interop internal static partial class CryptUI { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal class CRYPTUI_VIEWCERTIFICATE_STRUCTW + internal sealed class CRYPTUI_VIEWCERTIFICATE_STRUCTW { internal uint dwSize; internal IntPtr hwndParent; @@ -33,7 +33,7 @@ internal class CRYPTUI_VIEWCERTIFICATE_STRUCTW } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal class CRYPTUI_SELECTCERTIFICATE_STRUCTW + internal sealed class CRYPTUI_SELECTCERTIFICATE_STRUCTW { internal uint dwSize; internal IntPtr hwndParent; diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs index 19b89ff505e95..2632da9a2b09d 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { internal static partial class Gdi32 { diff --git a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs index 6fd2456d296c4..987a0cff614ee 100644 --- a/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs +++ b/src/libraries/Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { internal static partial class Gdi32 { diff --git a/src/libraries/Common/src/Interop/Windows/Interop.HRESULT_FROM_WIN32.cs b/src/libraries/Common/src/Interop/Windows/Interop.HRESULT_FROM_WIN32.cs index dd476004399c1..d76722e97856b 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.HRESULT_FROM_WIN32.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.HRESULT_FROM_WIN32.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; -internal partial class Interop +internal static partial class Interop { // Implementation of HRESULT_FROM_WIN32 macro [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/Common/src/Interop/Windows/Interop.LongFileTime.cs b/src/libraries/Common/src/Interop/Windows/Interop.LongFileTime.cs index e1be498ffe697..dbb840c9721ac 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.LongFileTime.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.LongFileTime.cs @@ -3,7 +3,7 @@ using System; -internal partial class Interop +internal static partial class Interop { /// /// 100-nanosecond intervals (ticks) since January 1, 1601 (UTC). diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Beep.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Beep.cs index ef22a059ba490..7c7c79e76899b 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Beep.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Beep.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool Beep(int frequency, int duration); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMPROP.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMPROP.cs index 4d2ba229763a0..8084c8a390a99 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMPROP.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMPROP.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { // Declaration for C# representation of Win32 COMMPROP // structure associated with a file handle to a serial communications resource. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMTIMEOUTS.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMTIMEOUTS.cs index 4c0faae9fb5c3..cae41fccf650e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMTIMEOUTS.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMMTIMEOUTS.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { // Declaration for C# representation of Win32 COMMTIMEOUTS // structure associated with a file handle to a serial communications resource. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMSTAT.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMSTAT.cs index 44b76ebe571fa..5a2befdc846c8 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMSTAT.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.COMSTAT.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { // Declaration for C# representation of Win32 COMSTAT structure associated with // a file handle to a serial communications resource. SerialStream's diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs index 9564f264b66b8..0ec97a3625738 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs @@ -3,9 +3,9 @@ using System; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal unsafe struct CREATEFILE2_EXTENDED_PARAMETERS { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommBreak.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommBreak.cs index c21f399b0f5f7..19602498de13e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommBreak.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommBreak.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool ClearCommBreak( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommError.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommError.cs index 65a35f2c298e9..8d4c5d1e6c789 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommError.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ClearCommError.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool ClearCommError( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompareStringOrdinal.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompareStringOrdinal.cs index 4b3d8c75b90df..b8d5c34f8cb99 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompareStringOrdinal.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompareStringOrdinal.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317762.aspx [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompletionPort.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompletionPort.cs index cc60031f8606d..dc2c6c4193f7d 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompletionPort.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CompletionPort.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern IntPtr CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, UIntPtr CompletionKey, int NumberOfConcurrentThreads); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConnectNamedPipe.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConnectNamedPipe.cs index 0c72318aac21c..2805d3d1b7684 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConnectNamedPipe.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConnectNamedPipe.cs @@ -6,9 +6,9 @@ using System.Runtime.InteropServices; using System.Threading; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleCursorInfo.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleCursorInfo.cs index c5ab22ca8193d..5b34e2210b965 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleCursorInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleCursorInfo.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [StructLayoutAttribute(LayoutKind.Sequential)] internal struct CONSOLE_CURSOR_INFO diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleScreenBufferInfo.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleScreenBufferInfo.cs index 71cfa523d44b8..625e88a0761c6 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleScreenBufferInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ConsoleScreenBufferInfo.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [StructLayoutAttribute(LayoutKind.Sequential)] internal struct CONSOLE_SCREEN_BUFFER_INFO diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFile.cs index 73ceb6b8c349e..b8f59fd8e315f 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFile.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal static int CopyFile(string src, string dst, bool failIfExists) { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFileEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFileEx.cs index 440278ab11eb8..f0766d2477b26 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFileEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CopyFileEx.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// WARNING: This method does not implicitly handle long paths. Use CopyFileEx. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateDirectory.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateDirectory.cs index ad57bf0e4caa0..5c4e754f86019 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateDirectory.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateDirectory.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// WARNING: This method does not implicitly handle long paths. Use CreateDirectory. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateFileMapping.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateFileMapping.cs index 4b60571ba561c..cc85cc91756ba 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateFileMapping.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateFileMapping.cs @@ -6,9 +6,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "CreateFileMappingW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeMemoryMappedFileHandle CreateFileMapping( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe.cs index b40e76f71aebe..3d88bf3c5ba94 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipe.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "CreateNamedPipeW")] internal static extern SafePipeHandle CreateNamedPipe( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipeClient.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipeClient.cs index 219a70165991c..4c8b9cbc06bc2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipeClient.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateNamedPipeClient.cs @@ -6,9 +6,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)] internal static extern SafePipeHandle CreateNamedPipeClient( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafeFileHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafeFileHandle.cs index fdf4aad05e1ff..ae4e43a73a386 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafeFileHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafeFileHandle.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, int nSize); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafePipeHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafePipeHandle.cs index 740256b40b661..92a3257d33d37 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafePipeHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreatePipe_SafePipeHandle.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool CreatePipe(out SafePipeHandle hReadPipe, out SafePipeHandle hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, int nSize); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateProcess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateProcess.cs index 5893f6152ce36..ede776e82438f 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateProcess.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.CreateProcess.cs @@ -6,9 +6,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "CreateProcessW")] internal static extern unsafe bool CreateProcess( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DCB.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DCB.cs index d1b1ee1ff0a98..5a13ce4540efd 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DCB.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DCB.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal static class DCBFlags { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteFile.cs index da77fea95df68..112538259e925 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteFile.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// WARNING: This method does not implicitly handle long paths. Use DeleteFile. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteVolumeMountPoint.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteVolumeMountPoint.cs index 1a6aaecf1cb00..66b39032eb90e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteVolumeMountPoint.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DeleteVolumeMountPoint.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// WARNING: This method does not implicitly handle long paths. Use DeleteVolumeMountPoint. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DisconnectNamedPipe.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DisconnectNamedPipe.cs index 5ea6bc0069c5b..ccef10e08a77a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DisconnectNamedPipe.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DisconnectNamedPipe.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs index 15df45563e8c6..5a5ae4ddca197 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcessModules.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "K32EnumProcessModules")] internal static extern bool EnumProcessModules(SafeProcessHandle handle, IntPtr[]? modules, int size, out int needed); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcesses.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcesses.cs index ed5fdac363a3a..f5298dc2cc879 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcesses.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EnumProcesses.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "K32EnumProcesses")] internal static extern bool EnumProcesses(int[] processIds, int size, out int needed); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EscapeCommFunction.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EscapeCommFunction.cs index 459a68b3748c2..ffd536c369186 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EscapeCommFunction.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.EscapeCommFunction.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal static class CommFunctions { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileAttributes.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileAttributes.cs index f9cdf4e3aa08b..c2350c35ec604 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileAttributes.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileAttributes.cs @@ -5,7 +5,7 @@ internal static partial class Interop { internal static partial class Kernel32 { - internal partial class FileAttributes + internal static partial class FileAttributes { internal const int FILE_ATTRIBUTE_NORMAL = 0x00000080; internal const int FILE_ATTRIBUTE_READONLY = 0x00000001; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs index 8a2724dc2eddf..f2a3872299d2c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileOperations.cs @@ -1,17 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class IOReparseOptions + internal static partial class IOReparseOptions { internal const uint IO_REPARSE_TAG_FILE_PLACEHOLDER = 0x80000015; internal const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; } - internal partial class FileOperations + internal static partial class FileOperations { internal const int OPEN_EXISTING = 3; internal const int COPY_FILE_FAIL_IF_EXISTS = 0x00000001; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTypes.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTypes.cs index 6649e1644e72c..6b2f00a98348a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTypes.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTypes.cs @@ -5,7 +5,7 @@ internal static partial class Interop { internal static partial class Kernel32 { - internal partial class FileTypes + internal static partial class FileTypes { internal const int FILE_TYPE_UNKNOWN = 0x0000; internal const int FILE_TYPE_DISK = 0x0001; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputAttribute.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputAttribute.cs index 023ae150381e6..4a1657f02119a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputAttribute.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputAttribute.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode)] internal static extern bool FillConsoleOutputAttribute(IntPtr hConsoleOutput, short wColorAttribute, int numCells, COORD startCoord, out int pNumBytesWritten); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputCharacter.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputCharacter.cs index 89254f98d3f4b..5b5f6ac477246 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputCharacter.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FillConsoleOutputCharacter.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "FillConsoleOutputCharacterW")] internal static extern bool FillConsoleOutputCharacter(IntPtr hConsoleOutput, char character, int nLength, COORD dwWriteCoord, out int pNumCharsWritten); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FindNextFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FindNextFile.cs index bd01c1d2895ef..b947d5d229c09 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FindNextFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FindNextFile.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "FindNextFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] internal static extern bool FindNextFile(SafeFindHandle hndFindFile, ref WIN32_FIND_DATA lpFindFileData); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FlushViewOfFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FlushViewOfFile.cs index 7842cf6d984be..ce82cfa23c33d 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FlushViewOfFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FlushViewOfFile.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool FlushViewOfFile(IntPtr lpBaseAddress, UIntPtr dwNumberOfBytesToFlush); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage.cs index 17cfbeb441ea6..53b0ad25573dd 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage.cs @@ -71,7 +71,7 @@ internal static unsafe string GetMessage(int errorCode, IntPtr moduleHandle) } // Couldn't get a message, so manufacture one. - return string.Format("Unknown error (0x{0:x})", errorCode); + return $"Unknown error (0x{errorCode:x})"; } private static string GetAndTrimString(Span buffer) diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage_SafeLibraryHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage_SafeLibraryHandle.cs index 6626deb755ec5..288730496d649 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage_SafeLibraryHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FormatMessage_SafeLibraryHandle.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { public const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; public const int FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GenericOperations.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GenericOperations.cs index 933a85f8d9c77..f346fa89f8910 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GenericOperations.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GenericOperations.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class GenericOperations + internal static partial class GenericOperations { internal const int GENERIC_READ = unchecked((int)0x80000000); internal const int GENERIC_WRITE = 0x40000000; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCPInfoEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCPInfoEx.cs index b44a63e5c185d..b0c64b5c80b4d 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCPInfoEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCPInfoEx.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetCPInfoExW")] private static extern unsafe Interop.BOOL GetCPInfoExW(uint CodePage, uint dwFlags, CPINFOEXW* lpCPInfoEx); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommModemStatus.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommModemStatus.cs index cc293b346c12b..b0fdff93dc07a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommModemStatus.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommModemStatus.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal static class CommModemState { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommProperties.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommProperties.cs index b8b590c75f570..c06d2a28b2c7e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommProperties.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommProperties.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool GetCommProperties( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommState.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommState.cs index 2e94d4b7c42be..09b81af4229d2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommState.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCommState.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool GetCommState( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleCP.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleCP.cs index 900c649067c70..e76c06990b4b9 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleCP.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleCP.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] internal static extern uint GetConsoleCP(); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleMode.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleMode.cs index f71ac5b154ae0..99e725c885bfb 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleMode.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleMode.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool GetConsoleMode(IntPtr handle, out int mode); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleOutputCP.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleOutputCP.cs index 3e4eaf7a324c3..fd59f4dddf1ca 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleOutputCP.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleOutputCP.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] internal static extern uint GetConsoleOutputCP(); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleScreenBufferInfo.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleScreenBufferInfo.cs index bf79276ff5b05..b782fb94d391b 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleScreenBufferInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleScreenBufferInfo.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleTitle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleTitle.cs index 1424039131ec5..39f7ef1e4678e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleTitle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetConsoleTitle.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] internal static extern unsafe uint GetConsoleTitleW(char* title, uint nSize); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDiskFreeSpaceEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDiskFreeSpaceEx.cs index bf86ee4ec9c9c..d7dfb5d5c1d09 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDiskFreeSpaceEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDiskFreeSpaceEx.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { // NOTE: The out parameters are PULARGE_INTEGERs and may require // some byte munging magic. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDriveType.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDriveType.cs index 9c97329450afc..4c45c528f9aa1 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDriveType.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetDriveType.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "GetDriveTypeW", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)] internal static extern int GetDriveType(string drive); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetExitCodeProcess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetExitCodeProcess.cs index c4f5cbf8f8dac..6184ad87a5bc5 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetExitCodeProcess.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetExitCodeProcess.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool GetExitCodeProcess(SafeProcessHandle processHandle, out int exitCode); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFileType_IntPtr.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFileType_IntPtr.cs index 4420e492689ef..313ca31e97dd5 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFileType_IntPtr.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetFileType_IntPtr.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern uint GetFileType(IntPtr hFile); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLargestConsoleWindowSize.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLargestConsoleWindowSize.cs index 84d3e81deedcf..d26a2d924f5c1 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLargestConsoleWindowSize.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLargestConsoleWindowSize.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern Interop.Kernel32.COORD GetLargestConsoleWindowSize(IntPtr hConsoleOutput); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLastError.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLastError.cs new file mode 100644 index 0000000000000..6dec98d0d7518 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetLastError.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + [SuppressGCTransition] + internal static extern int GetLastError(); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleBaseName.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleBaseName.cs index 746b6a2d5b634..9cfb175b0a16c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleBaseName.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleBaseName.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "K32GetModuleBaseNameW")] internal static extern int GetModuleBaseName(SafeProcessHandle processHandle, IntPtr moduleHandle, [Out] char[] baseName, int size); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileNameEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileNameEx.cs index eab0ce8b33fd1..fc99fca9a0942 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileNameEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleFileNameEx.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "K32GetModuleFileNameExW")] internal static extern int GetModuleFileNameEx(SafeProcessHandle processHandle, IntPtr moduleHandle, [Out] char[] baseName, int size); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleHandle.cs index 5da4adde9f25c..99f35d7b3c547 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleHandle.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "GetModuleHandleW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr GetModuleHandle(string? moduleName); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleInformation.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleInformation.cs index ed336f9165257..6ec386711ca4c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetModuleInformation.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "K32GetModuleInformation")] private static extern bool GetModuleInformation(SafeProcessHandle processHandle, IntPtr moduleHandle, out NtModuleInfo ntModuleInfo, int size); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeHandleState.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeHandleState.cs index 3a26645cc372c..f98c2c1c31b5c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeHandleState.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeHandleState.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] internal static extern unsafe bool GetNamedPipeHandleStateW( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeInfo.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeInfo.cs index 0e7fef19a9ce1..586f661857c2e 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNamedPipeInfo.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern unsafe bool GetNamedPipeInfo( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNativeSystemInfo.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNativeSystemInfo.cs index dfc4c30911058..60bbcca66c548 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNativeSystemInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetNativeSystemInfo.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] internal static extern void GetNativeSystemInfo(out SYSTEM_INFO lpSystemInfo); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetOverlappedResult.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetOverlappedResult.cs index ad44065a5b6a2..6d52bfff0adf5 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetOverlappedResult.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetOverlappedResult.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Threading; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern unsafe bool GetOverlappedResult( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetPriorityClass.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetPriorityClass.cs index fae6838a00ba1..385b534acb3f8 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetPriorityClass.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetPriorityClass.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int GetPriorityClass(SafeProcessHandle handle); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcAddress.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcAddress.cs index 797ef5549b736..214050545ed15 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcAddress.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcAddress.cs @@ -6,9 +6,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Ansi, BestFitMapping = false)] public static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, string lpProcName); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessAffinityMask.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessAffinityMask.cs index 4529b864d7b4a..0cd7484e25289 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessAffinityMask.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessAffinityMask.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetProcessAffinityMask(SafeProcessHandle handle, out IntPtr processMask, out IntPtr systemMask); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessId.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessId.cs index 69317d64557df..9d9167df4a13c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessId.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessId.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] public static extern int GetProcessId(SafeProcessHandle nativeHandle); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessPriorityBoost.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessPriorityBoost.cs index decb2f8f893da..93619bebe7f97 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessPriorityBoost.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessPriorityBoost.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetProcessPriorityBoost(SafeProcessHandle handle, out bool disabled); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs index 4c392697c8c76..1c585d6e74a80 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessTimes.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetProcessTimes(SafeProcessHandle handle, out long creation, out long exit, out long kernel, out long user); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessWorkingSetSizeEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessWorkingSetSizeEx.cs index b398ad5e34985..64284d546f7fa 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessWorkingSetSizeEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetProcessWorkingSetSizeEx.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetProcessWorkingSetSizeEx(SafeProcessHandle handle, out IntPtr min, out IntPtr max, out int flags); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriority.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriority.cs index 51cbfec2e0335..b55390030f24b 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriority.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriority.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int GetThreadPriority(SafeThreadHandle handle); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriorityBoost.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriorityBoost.cs index 4e051331bebf2..e9a9bc9924174 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriorityBoost.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadPriorityBoost.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetThreadPriorityBoost(SafeThreadHandle handle, out bool disabled); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadTimes.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadTimes.cs index 78d286c1fb196..54e2e43458ccb 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadTimes.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetThreadTimes.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetThreadTimes(SafeThreadHandle handle, out long creation, out long exit, out long kernel, out long user); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetVolumeInformation.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetVolumeInformation.cs index c047379bc88b5..48775f06c5a9d 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetVolumeInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetVolumeInformation.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "GetVolumeInformationW", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)] internal static extern unsafe bool GetVolumeInformation(string drive, char* volumeName, int volumeNameBufLen, int* volSerialNumber, int* maxFileNameLen, out int fileSystemFlags, char* fileSystemName, int fileSystemNameBufLen); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs index e2f189d09dcf4..8971a6326bf84 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)] public static extern IntPtr GlobalLock(IntPtr hMem); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs index a000b95a9a31b..a5029f5ccb6e8 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GlobalMemoryStatusEx.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] internal static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleOptions.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleOptions.cs index 2f24130f62f28..4a2b32f7c3c30 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleOptions.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class HandleOptions + internal static partial class HandleOptions { internal const int DUPLICATE_SAME_ACCESS = 2; internal const int STILL_ACTIVE = 0x00000103; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleTypes.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleTypes.cs index 0878edb7bba55..27971438b7431 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleTypes.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.HandleTypes.cs @@ -5,7 +5,7 @@ internal static partial class Interop { internal static partial class Kernel32 { - internal partial class HandleTypes + internal static partial class HandleTypes { internal const int STD_INPUT_HANDLE = -10; internal const int STD_OUTPUT_HANDLE = -11; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Heap.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Heap.cs index f91a3e77d2710..a5198b12a3688 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Heap.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Heap.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode)] internal static extern IntPtr GetProcessHeap(); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs index 7ab38e7415876..be477375c81a3 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IoControlCodeAccess.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// RequiredAccess. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs index 6f786db47b7de..eb365d7e18a54 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.IsWow64Process_SafeProcessHandle.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool IsWow64Process(SafeProcessHandle hProcess, out bool Wow64Process); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibrary.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibrary.cs index efd2242d0550a..a65062ad682b1 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibrary.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibrary.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] public static extern IntPtr LoadLibrary(string libFilename); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs index 7e912185e58a9..95ed8cb18a1cb 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.LoadLibraryEx.cs @@ -6,9 +6,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { public const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002; public const int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFO.cs index 84785156db9d2..58e7a6b113cc1 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFO.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MEMORY_BASIC_INFO.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [StructLayout(LayoutKind.Sequential)] internal unsafe struct MEMORY_BASIC_INFORMATION diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MapViewOfFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MapViewOfFile.cs index 4fa3ff3ceae03..73f714d64164c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MapViewOfFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MapViewOfFile.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "MapViewOfFile", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeMemoryMappedViewHandle MapViewOfFile( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MaxLengths.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MaxLengths.cs index 56844bdab7a8b..93306f8150049 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MaxLengths.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MaxLengths.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal const int CREDUI_MAX_USERNAME_LENGTH = 513; } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MemOptions.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MemOptions.cs index 2cab5c6598833..55b1896d0c186 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MemOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MemOptions.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class MemOptions + internal static partial class MemOptions { internal const int MEM_COMMIT = 0x1000; internal const int MEM_RESERVE = 0x2000; @@ -15,7 +15,7 @@ internal partial class MemOptions internal const int INVALID_FILE_SIZE = -1; - internal partial class PageOptions + internal static partial class PageOptions { internal const int PAGE_READWRITE = 0x04; internal const int PAGE_READONLY = 0x02; @@ -24,7 +24,7 @@ internal partial class PageOptions internal const int PAGE_EXECUTE_READWRITE = 0x40; } - internal partial class FileMapOptions + internal static partial class FileMapOptions { internal const int FILE_MAP_COPY = 0x0001; internal const int FILE_MAP_WRITE = 0x0002; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MoveFileEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MoveFileEx.cs index 46b9b721989e0..507d325517443 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MoveFileEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.MoveFileEx.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { private const uint MOVEFILE_REPLACE_EXISTING = 0x01; private const uint MOVEFILE_COPY_ALLOWED = 0x02; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenFileMapping.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenFileMapping.cs index 7dba1f9ff2189..70e3075e816c4 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenFileMapping.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenFileMapping.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "OpenFileMappingW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeMemoryMappedFileHandle OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenProcess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenProcess.cs index 22011c59a09e7..970193fe756ef 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenProcess.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenProcess.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeProcessHandle OpenProcess(int access, bool inherit, int processId); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenThread.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenThread.cs index 817a5a62a3965..0eebf8b61d96d 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenThread.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.OpenThread.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern SafeThreadHandle OpenThread(int access, bool inherit, int threadId); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PeekConsoleInput.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PeekConsoleInput.cs index 7a0764831e837..98ef5c59c7898 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PeekConsoleInput.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PeekConsoleInput.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PeekConsoleInputW")] internal static extern bool PeekConsoleInput(IntPtr hConsoleInput, out InputRecord buffer, int numInputRecords_UseOne, out int numEventsRead); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PerformanceCounterOptions.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PerformanceCounterOptions.cs index 2d9e1dea66ce8..2c46f8df376ab 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PerformanceCounterOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PerformanceCounterOptions.cs @@ -3,11 +3,11 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class PerformanceCounterOptions + internal static partial class PerformanceCounterOptions { internal const int SDDL_REVISION_1 = 1; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PipeOptions.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PipeOptions.cs index 67e5fa95e2ca4..b722d7f3d8a73 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PipeOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PipeOptions.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class PipeOptions + internal static partial class PipeOptions { internal const uint PIPE_ACCESS_INBOUND = 1; internal const uint PIPE_ACCESS_OUTBOUND = 2; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ProcessWaitHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ProcessWaitHandle.cs index ec8621667a5d3..3090de5ad6feb 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ProcessWaitHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ProcessWaitHandle.cs @@ -6,9 +6,9 @@ using System.Runtime.InteropServices; using System.Threading; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal sealed class ProcessWaitHandle : WaitHandle { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PurgeComm.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PurgeComm.cs index 350f89f102524..9a71eececb637 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PurgeComm.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.PurgeComm.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal static class PurgeFlags { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsole.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsole.cs index 947e4f9c02b5e..1ef39b4465df2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsole.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsole.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "ReadConsoleW")] internal static extern unsafe bool ReadConsole( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleInput.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleInput.cs index 44c2d297f7c02..413ad3d88042f 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleInput.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleInput.cs @@ -4,7 +4,7 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { internal const short KEY_EVENT = 1; @@ -30,7 +30,7 @@ internal struct InputRecord } - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "ReadConsoleInputW")] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleOutput.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleOutput.cs index d7be05a6b96d0..5a40457dc5f26 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleOutput.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadConsoleOutput.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [StructLayout(LayoutKind.Sequential)] internal struct CHAR_INFO diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadDirectoryChangesW.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadDirectoryChangesW.cs index 62507d5a9d8b6..ccbffb6d14a82 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadDirectoryChangesW.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadDirectoryChangesW.cs @@ -6,9 +6,9 @@ using System.Runtime.InteropServices; using System.Threading; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "ReadDirectoryChangesW", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe bool ReadDirectoryChangesW( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_IntPtr.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_IntPtr.cs index e5f3c2190d755..2ccefd764674a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_IntPtr.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_IntPtr.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern unsafe int ReadFile( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs index 8419932249299..c6fb2c4311cf3 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReadFile_SafeHandle_NativeOverlapped.cs @@ -16,5 +16,13 @@ internal static extern unsafe int ReadFile( int numBytesToRead, IntPtr numBytesRead_mustBeZero, NativeOverlapped* overlapped); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe int ReadFile( + SafeHandle handle, + byte* bytes, + int numBytesToRead, + out int numBytesRead, + NativeOverlapped* overlapped); } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RemoveDirectory.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RemoveDirectory.cs index d29a6267673fc..2d9d01ea06445 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RemoveDirectory.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.RemoveDirectory.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// WARNING: This method does not implicitly handle long paths. Use RemoveDirectory. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs index 6ae3c6a3a527b..e3a6eb5c2d805 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "ReplaceFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)] private static extern bool ReplaceFilePrivate( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommBreak.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommBreak.cs index 40db4741e169f..609b8ac46074f 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommBreak.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommBreak.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool SetCommBreak( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommMask.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommMask.cs index 8570f44b565be..dcf7475ed73df 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommMask.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommMask.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal static class CommEvents { diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommState.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommState.cs index d5cffb8cb2ddc..135627e0ecac5 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommState.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommState.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool SetCommState( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommTimeouts.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommTimeouts.cs index fdb5c578fb9ec..2ca02d02d4e42 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommTimeouts.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetCommTimeouts.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal const int MAXDWORD = -1; // This is 0xfffffff, or UInt32.MaxValue, here used as an int diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCP.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCP.cs index 7cb6628c6e4a4..e7c2140d16beb 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCP.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCP.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool SetConsoleCP(int codePage); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs index 7594b62f8c5e0..c6ee59a77a2b9 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCtrlHandler.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { internal const int CTRL_C_EVENT = 0; internal const int CTRL_BREAK_EVENT = 1; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCursorPosition.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCursorPosition.cs index c0ac0f0f6a7c2..39a951b4399bb 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCursorPosition.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleCursorPosition.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, COORD cursorPosition); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleOutputCP.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleOutputCP.cs index 855022dcfc568..81441a4a66012 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleOutputCP.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleOutputCP.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool SetConsoleOutputCP(int codePage); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleScreenBufferSize.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleScreenBufferSize.cs index a6e2252e82983..7c55bf6b3c614 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleScreenBufferSize.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleScreenBufferSize.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool SetConsoleScreenBufferSize(IntPtr hConsoleOutput, Interop.Kernel32.COORD size); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTextAttribute.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTextAttribute.cs index bc45dafaabfd6..a4cfcd65b49a9 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTextAttribute.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTextAttribute.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern int SetConsoleTextAttribute(IntPtr hConsoleOutput, short wAttributes); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTitle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTitle.cs index 13e4e11115a4f..34fb5bf40e436 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTitle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleTitle.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "SetConsoleTitleW")] internal static extern bool SetConsoleTitle(string title); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleWindowInfo.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleWindowInfo.cs index ac9ee2fea7654..ae87d60ffe9b2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleWindowInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetConsoleWindowInfo.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern unsafe bool SetConsoleWindowInfo(IntPtr hConsoleOutput, bool absolute, SMALL_RECT* consoleWindow); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileAttributes.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileAttributes.cs index bfb36e0955a6a..33ecdabc76f1a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileAttributes.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileAttributes.cs @@ -4,9 +4,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { /// /// WARNING: This method does not implicitly handle long paths. Use SetFileAttributes. diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileInformationByHandle.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileInformationByHandle.cs index 1bc903dc9ec38..193fa9b988710 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileInformationByHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetFileInformationByHandle.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true)] internal static extern unsafe bool SetFileInformationByHandle(SafeFileHandle hFile, int FileInformationClass, void* lpFileInformation, uint dwBufferSize); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetNamedPipeHandleState.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetNamedPipeHandleState.cs index 9448c6739d834..70f0ab13483e7 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetNamedPipeHandleState.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetNamedPipeHandleState.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetPriorityClass.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetPriorityClass.cs index ebafa0d844014..eaa743fd26c36 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetPriorityClass.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetPriorityClass.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool SetPriorityClass(SafeProcessHandle handle, int priorityClass); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessAffinityMask.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessAffinityMask.cs index 9818faca98ec0..f4bf52550ae8b 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessAffinityMask.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessAffinityMask.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool SetProcessAffinityMask(SafeProcessHandle handle, IntPtr mask); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessPriorityBoost.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessPriorityBoost.cs index 868e4aee2f207..73263e99458fc 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessPriorityBoost.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessPriorityBoost.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool SetProcessPriorityBoost(SafeProcessHandle handle, bool disabled); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessWorkingSetSizeEx.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessWorkingSetSizeEx.cs index 2f9cd37b0d3a7..06b06a704675a 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessWorkingSetSizeEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetProcessWorkingSetSizeEx.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool SetProcessWorkingSetSizeEx(SafeProcessHandle handle, IntPtr min, IntPtr max, int flags); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadAffinityMask.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadAffinityMask.cs index 4fb3243e84fb1..54120f20d8bcd 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadAffinityMask.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadAffinityMask.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr SetThreadAffinityMask(SafeThreadHandle handle, IntPtr mask); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadIdealProcessor.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadIdealProcessor.cs index ab1e50f8ac291..3d4f3e9d0b22f 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadIdealProcessor.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadIdealProcessor.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int SetThreadIdealProcessor(SafeThreadHandle handle, int processor); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriority.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriority.cs index e9ca6f043bf5c..4024214bc64dc 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriority.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriority.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool SetThreadPriority(SafeThreadHandle handle, int priority); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriorityBoost.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriorityBoost.cs index d16f9e12b4dca..b285164670226 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriorityBoost.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetThreadPriorityBoost.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool SetThreadPriorityBoost(SafeThreadHandle handle, bool disabled); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetVolumeLabel.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetVolumeLabel.cs index 95119d4f772be..4b6c9344e262c 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetVolumeLabel.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetVolumeLabel.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, EntryPoint = "SetVolumeLabelW", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)] internal static extern bool SetVolumeLabel(string driveLetter, string? volumeName); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetupComm.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetupComm.cs index 0890801379671..b36db5959bc5b 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetupComm.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetupComm.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool SetupComm( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.TerminateProcess.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.TerminateProcess.cs index 88420d0afe63a..37dfec5d196f3 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.TerminateProcess.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.TerminateProcess.cs @@ -5,9 +5,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool TerminateProcess(SafeProcessHandle processHandle, int exitCode); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadOptions.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadOptions.cs index 31adedb43b00a..ee3dbf0a337d2 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadOptions.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ThreadOptions.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { - internal partial class ThreadOptions + internal static partial class ThreadOptions { internal const int THREAD_SET_INFORMATION = 0x0020; internal const int THREAD_QUERY_INFORMATION = 0x0040; diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs new file mode 100644 index 0000000000000..d4919d725a52c --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Threading.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + internal const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); + + [DllImport(Libraries.Kernel32)] + internal static extern uint WaitForMultipleObjectsEx(uint nCount, IntPtr lpHandles, BOOL bWaitAll, uint dwMilliseconds, BOOL bAlertable); + + [DllImport(Libraries.Kernel32)] + internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); + + [DllImport(Libraries.Kernel32)] + internal static extern uint SignalObjectAndWait(IntPtr hObjectToSignal, IntPtr hObjectToWaitOn, uint dwMilliseconds, BOOL bAlertable); + + [DllImport(Libraries.Kernel32)] + internal static extern void Sleep(uint milliseconds); + + internal const uint CREATE_SUSPENDED = 0x00000004; + internal const uint STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000; + + [DllImport(Libraries.Kernel32)] + internal static extern unsafe SafeWaitHandle CreateThread( + IntPtr lpThreadAttributes, + IntPtr dwStackSize, + delegate* unmanaged lpStartAddress, + IntPtr lpParameter, + uint dwCreationFlags, + out uint lpThreadId); + + [DllImport(Libraries.Kernel32)] + internal static extern uint ResumeThread(SafeWaitHandle hThread); + + [DllImport(Libraries.Kernel32)] + internal static extern IntPtr GetCurrentThread(); + + internal const int DUPLICATE_SAME_ACCESS = 2; + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern bool DuplicateHandle( + IntPtr hSourceProcessHandle, + IntPtr hSourceHandle, + IntPtr hTargetProcessHandle, + out SafeWaitHandle lpTargetHandle, + uint dwDesiredAccess, + bool bInheritHandle, + uint dwOptions); + + internal enum ThreadPriority : int + { + Idle = -15, + Lowest = -2, + BelowNormal = -1, + Normal = 0, + AboveNormal = 1, + Highest = 2, + TimeCritical = 15, + + ErrorReturn = 0x7FFFFFFF + } + + [DllImport(Libraries.Kernel32)] + internal static extern ThreadPriority GetThreadPriority(SafeWaitHandle hThread); + + [DllImport(Libraries.Kernel32)] + internal static extern bool SetThreadPriority(SafeWaitHandle hThread, int nPriority); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.UnmapViewOfFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.UnmapViewOfFile.cs index 853d15f50e692..55d3b8eeb49b5 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.UnmapViewOfFile.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.UnmapViewOfFile.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VerLanguageName.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VerLanguageName.cs index 2b1272342dd2b..6c665a47e4955 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VerLanguageName.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VerLanguageName.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, EntryPoint = "VerLanguageNameW")] internal static extern unsafe int VerLanguageName(uint wLang, char* szLang, uint cchLang); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VirtualQuery.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VirtualQuery.cs index 0bf18455edfb3..25e3fd62b0dad 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VirtualQuery.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.VirtualQuery.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true)] internal static extern UIntPtr VirtualQuery(SafeHandle lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, UIntPtr dwLength); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitCommEvent.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitCommEvent.cs index 6013e7aad8d55..e9328f34beb97 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitCommEvent.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitCommEvent.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Threading; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Auto)] internal static extern unsafe bool WaitCommEvent( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitForSingleObject.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitForSingleObject.cs index bc0c9f72b9170..2a81da9a5a1c8 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitForSingleObject.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitForSingleObject.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)] internal static extern int WaitForSingleObject(SafeWaitHandle handle, int timeout); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitNamedPipe.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitNamedPipe.cs index 11cbbe9027dc8..cb95a8e120cd5 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitNamedPipe.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WaitNamedPipe.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false, EntryPoint = "WaitNamedPipeW")] [return: MarshalAs(UnmanagedType.Bool)] diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsole.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsole.cs index 238f623cdb3ac..ed5cfee214666 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsole.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsole.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "WriteConsoleW")] internal static extern unsafe bool WriteConsole( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsoleOutput.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsoleOutput.cs index 741813811a837..47e03d8057633 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsoleOutput.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteConsoleOutput.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "WriteConsoleOutputW")] internal static extern unsafe bool WriteConsoleOutput(IntPtr hConsoleOutput, CHAR_INFO* buffer, COORD bufferSize, COORD bufferCoord, ref SMALL_RECT writeRegion); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_IntPtr.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_IntPtr.cs index 750f89d5bd44f..7ebff795dcfb4 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_IntPtr.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_IntPtr.cs @@ -3,9 +3,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Kernel32 + internal static partial class Kernel32 { [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern unsafe int WriteFile( diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs index cca93e0b3ac89..3eaff9ca07626 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.WriteFile_SafeHandle_NativeOverlapped.cs @@ -17,5 +17,8 @@ internal static partial class Kernel32 // and pass in an address for the numBytesRead parameter. [DllImport(Libraries.Kernel32, SetLastError = true)] internal static extern unsafe int WriteFile(SafeHandle handle, byte* bytes, int numBytesToWrite, IntPtr numBytesWritten_mustBeZero, NativeOverlapped* lpOverlapped); + + [DllImport(Libraries.Kernel32, SetLastError = true)] + internal static extern unsafe int WriteFile(SafeHandle handle, byte* bytes, int numBytesToWrite, out int numBytesWritten, NativeOverlapped* lpOverlapped); } } diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs index ea1365e844e8c..2049c100ce7eb 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_DIR_INFORMATION.cs @@ -5,9 +5,9 @@ using System.IO; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { /// /// FILE_FULL_DIR_INFORMATION structure. diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs index 539d9fd67dc98..24f807538bf59 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_FULL_EA_INFORMATION.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { /// /// FILE_FULL_EA_INFORMATION structure. diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs index f6b3b48dde92e..7232b5ff6b406 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.FILE_INFORMATION_CLASS.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { // https://msdn.microsoft.com/en-us/library/windows/hardware/ff728840.aspx public enum FILE_INFORMATION_CLASS : uint diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs index 2895f2c00193c..80e187913c53a 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.IO_STATUS_BLOCK.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { // https://msdn.microsoft.com/en-us/library/windows/hardware/ff550671.aspx [StructLayout(LayoutKind.Sequential)] diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs index 0bb009b065058..e2f518e409dca 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtCreateFile.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { // https://msdn.microsoft.com/en-us/library/bb432380.aspx // https://msdn.microsoft.com/en-us/library/windows/hardware/ff566424.aspx diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs index 3a1f2ab2603f9..517fd4195f360 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryDirectoryFile.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { // https://msdn.microsoft.com/en-us/library/windows/hardware/ff556633.aspx // https://msdn.microsoft.com/en-us/library/windows/hardware/ff567047.aspx diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryInformationProcess.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryInformationProcess.cs index 6f4c394de7a46..24db7e74756d0 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryInformationProcess.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtQueryInformationProcess.cs @@ -4,9 +4,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { [DllImport(Libraries.NtDll, ExactSpelling = true)] internal static extern unsafe uint NtQueryInformationProcess(SafeProcessHandle ProcessHandle, int ProcessInformationClass, void* ProcessInformation, uint ProcessInformationLength, out uint ReturnLength); diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs index 5540270597c71..d79653a64105e 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.NtStatus.cs @@ -3,7 +3,7 @@ internal static partial class Interop { - internal class StatusOptions + internal static class StatusOptions { // Error codes from ntstatus.h internal const uint STATUS_SUCCESS = 0x00000000; diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs index 5592714f6735a..ee82a86be12d1 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlGetVersion.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { [DllImport(Libraries.NtDll, ExactSpelling = true)] private static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation); diff --git a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs index 666a533a21900..d41e2eb7f3327 100644 --- a/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs +++ b/src/libraries/Common/src/Interop/Windows/NtDll/Interop.RtlNtStatusToDosError.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class NtDll + internal static partial class NtDll { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680600(v=vs.85).aspx [DllImport(Libraries.NtDll, ExactSpelling = true)] diff --git a/src/libraries/Common/src/Interop/Windows/Pdh/Interop.PdhFormatFromRawValue.cs b/src/libraries/Common/src/Interop/Windows/Pdh/Interop.PdhFormatFromRawValue.cs index 130dac40cafa5..11a1556a5230d 100644 --- a/src/libraries/Common/src/Interop/Windows/Pdh/Interop.PdhFormatFromRawValue.cs +++ b/src/libraries/Common/src/Interop/Windows/Pdh/Interop.PdhFormatFromRawValue.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Pdh + internal static partial class Pdh { [DllImport(Libraries.Pdh, CharSet = CharSet.Unicode)] public static extern int PdhFormatFromRawValue( diff --git a/src/libraries/Common/src/Interop/Windows/PerfCounter/Interop.PerformanceData.cs b/src/libraries/Common/src/Interop/Windows/PerfCounter/Interop.PerformanceData.cs index 2fa286c648e75..5b95bd08795a6 100644 --- a/src/libraries/Common/src/Interop/Windows/PerfCounter/Interop.PerformanceData.cs +++ b/src/libraries/Common/src/Interop/Windows/PerfCounter/Interop.PerformanceData.cs @@ -7,7 +7,7 @@ internal static partial class Interop { - internal partial class PerfCounter + internal static partial class PerfCounter { [DllImport(Libraries.Advapi32, ExactSpelling = true)] internal static extern uint PerfStopProvider( diff --git a/src/libraries/Common/src/Interop/Windows/Shell32/Interop.ShellExecuteExW.cs b/src/libraries/Common/src/Interop/Windows/Shell32/Interop.ShellExecuteExW.cs index 3acd2cb7dbb5e..2d2213ae70b0b 100644 --- a/src/libraries/Common/src/Interop/Windows/Shell32/Interop.ShellExecuteExW.cs +++ b/src/libraries/Common/src/Interop/Windows/Shell32/Interop.ShellExecuteExW.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Shell32 + internal static partial class Shell32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal unsafe struct SHELLEXECUTEINFO diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.AuthenticationPackageNames.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.AuthenticationPackageNames.cs index db30fa63fca9b..fde81db96401e 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.AuthenticationPackageNames.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.AuthenticationPackageNames.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { internal static class AuthenticationPackageNames { diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbLogonSubmitType.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbLogonSubmitType.cs index ba0090f523f06..52dc702eba5da 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbLogonSubmitType.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbLogonSubmitType.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { internal enum KERB_LOGON_SUBMIT_TYPE : int { diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbS4uLogin.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbS4uLogin.cs index 02f94e1e12dc1..419d0d91f083f 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbS4uLogin.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.KerbS4uLogin.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [StructLayout(LayoutKind.Sequential)] internal struct KERB_S4U_LOGON diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaConnectUntrusted.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaConnectUntrusted.cs index d2c486ed1129a..73b9e1aa80b52 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaConnectUntrusted.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaConnectUntrusted.cs @@ -6,9 +6,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [DllImport(Interop.Libraries.SspiCli)] internal static extern int LsaConnectUntrusted(out SafeLsaHandle LsaHandle); diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaDeregisterLogonProcess.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaDeregisterLogonProcess.cs index aef62f926e247..352d7763ea518 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaDeregisterLogonProcess.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaDeregisterLogonProcess.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [DllImport(Interop.Libraries.SspiCli)] internal static extern int LsaDeregisterLogonProcess(IntPtr LsaHandle); diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLogonUser.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLogonUser.cs index 0fe7d43002848..82aaec9af71fe 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLogonUser.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLogonUser.cs @@ -6,9 +6,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [DllImport(Libraries.SspiCli)] internal static extern int LsaLogonUser( diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLookupAuthenticationPackage.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLookupAuthenticationPackage.cs index cc394dde97f86..059ededf0c412 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLookupAuthenticationPackage.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.LsaLookupAuthenticationPackage.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [DllImport(Libraries.SspiCli)] internal static extern int LsaLookupAuthenticationPackage( diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.QuotaLimits.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.QuotaLimits.cs index 6e0b89654d479..66ba69ef015b9 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.QuotaLimits.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.QuotaLimits.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [StructLayout(LayoutKind.Sequential)] internal struct QUOTA_LIMITS diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SecurityLogonType.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SecurityLogonType.cs index f07e493ea11ca..9be0b512c06fe 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SecurityLogonType.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SecurityLogonType.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { internal enum SECURITY_LOGON_TYPE : int { diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.TokenSource.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.TokenSource.cs index 330362a7ce06a..abee3daa3b3b1 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.TokenSource.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.TokenSource.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class SspiCli + internal static partial class SspiCli { [StructLayout(LayoutKind.Sequential)] internal struct TOKEN_SOURCE diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.Winnt.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.Winnt.cs index 325b749980fdb..fd34f7e8b0680 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.Winnt.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.Winnt.cs @@ -3,13 +3,13 @@ internal static partial class Interop { - internal class LuidOptions + internal static class LuidOptions { // Access Control library. internal const uint ANONYMOUS_LOGON_LUID = 0x3e6; } - internal class SecurityIdentifier + internal static class SecurityIdentifier { internal const int SECURITY_ANONYMOUS_LOGON_RID = 0x00000007; internal const int SECURITY_AUTHENTICATED_USER_RID = 0x0000000B; @@ -17,7 +17,7 @@ internal class SecurityIdentifier internal const int SECURITY_BUILTIN_DOMAIN_RID = 0x00000020; } - internal class SecurityGroups + internal static class SecurityGroups { internal const uint SE_GROUP_MANDATORY = 0x00000001; internal const uint SE_GROUP_ENABLED_BY_DEFAULT = 0x00000002; @@ -28,7 +28,7 @@ internal class SecurityGroups internal const uint SE_GROUP_RESOURCE = 0x20000000; } - internal class DuplicateHandleOptions + internal static class DuplicateHandleOptions { internal const uint DUPLICATE_CLOSE_SOURCE = 0x00000001; internal const uint DUPLICATE_SAME_ACCESS = 0x00000002; diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs index dc18d92c3463d..ea65be9600eba 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecurityPackageInfoClass.cs @@ -7,7 +7,7 @@ namespace System.Net { // _SecPkgInfoW in sspi.h. - internal class SecurityPackageInfoClass + internal sealed class SecurityPackageInfoClass { internal int Capabilities; internal short Version; diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.Constants.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.Constants.cs index b67a64920510f..7a1574155a7d9 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.Constants.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.Constants.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { public const int COLOR_WINDOW = 5; diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.CreateWindowEx.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.CreateWindowEx.cs index f5d8d62616db8..e68dfd52f5df3 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.CreateWindowEx.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.CreateWindowEx.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] public static extern IntPtr CreateWindowExW( diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.DefWindowProc.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.DefWindowProc.cs index 11e28f760ac81..6b654003533c5 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.DefWindowProc.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.DefWindowProc.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern IntPtr DefWindowProcW(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.DestroyWindow.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.DestroyWindow.cs index 5b5e0f5c464f9..c1f6b0f1fbbb5 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.DestroyWindow.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.DestroyWindow.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern bool DestroyWindow(IntPtr hWnd); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.DispatchMessage.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.DispatchMessage.cs index 8ccb7d6b48a34..0ebea0ef10598 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.DispatchMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.DispatchMessage.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern int DispatchMessageW([In] ref MSG msg); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.EnumWindows.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.EnumWindows.cs index 249788c3f3fa5..853738f9a61f3 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.EnumWindows.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.EnumWindows.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32)] public static extern unsafe bool EnumWindows(delegate* unmanaged callback, IntPtr extraData); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.FindWindow.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.FindWindow.cs index 915231ecb942d..550fc1fdc0afa 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.FindWindow.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.FindWindow.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet=CharSet.Auto, ExactSpelling = true)] public static extern IntPtr FindWindowW(string lpClassName, string lpWindowName); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetClassInfo.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetClassInfo.cs index b39b83f4bb4d4..17804dc70f9f2 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetClassInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetClassInfo.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern bool GetClassInfoW(IntPtr hInst, string lpszClass, ref WNDCLASS wc); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetKeyState.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetKeyState.cs index 0067f201ba145..bff237b379ef6 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetKeyState.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetKeyState.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32)] internal static extern short GetKeyState(int virtualKeyCode); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetProcessWindowStation.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetProcessWindowStation.cs index 3da6347bc8493..d624e2850a5da 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetProcessWindowStation.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetProcessWindowStation.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] internal static extern IntPtr GetProcessWindowStation(); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetSysColor.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetSysColor.cs index 0ef34d7901e2a..b066e25ec6eb2 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetSysColor.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetSysColor.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { // The returned value is a COLORREF. The docs don't say that explicitly, but // they do document the same macros (GetRValue, etc.). [0x00BBGGRR] diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetUserObjectInformation.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetUserObjectInformation.cs index 54243aa792ae1..14d09b968e709 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetUserObjectInformation.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetUserObjectInformation.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern unsafe bool GetUserObjectInformationW(IntPtr hObj, int nIndex, void* pvBuffer, uint nLength, ref uint lpnLengthNeeded); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindow.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindow.cs index afbc6b5dca41e..1ece26d7fdd6a 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindow.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindow.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32)] public static extern IntPtr GetWindow(IntPtr hWnd, int uCmd); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowLong.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowLong.cs index acc8aa8e2c576..1a0310987f990 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowLong.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowLong.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, EntryPoint = "GetWindowLongW")] public static extern int GetWindowLong(IntPtr hWnd, int uCmd); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextLengthW.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextLengthW.cs index 1077b4877e2bf..3acbfa271c7b7 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextLengthW.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextLengthW.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] public static extern int GetWindowTextLengthW(IntPtr hWnd); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextW.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextW.cs index 3a56b7c5ff64b..2590f25f465ec 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextW.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowTextW.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)] public static extern unsafe int GetWindowTextW(IntPtr hWnd, char* lpString, int nMaxCount); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs index 76dca36f42e13..33863ed425912 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.GetWindowThreadProcessId.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindow.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindow.cs index 4f545b9cbb35e..12e380cc7dcdd 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindow.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindow.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern bool IsWindow(IntPtr hWnd); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs index 8e17970f708e9..5e819d26db630 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.IsWindowVisible.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32)] public static extern bool IsWindowVisible(IntPtr hWnd); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.KillTimer.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.KillTimer.cs index 603e2b919ea95..2de52e0f34738 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.KillTimer.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.KillTimer.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern bool KillTimer(IntPtr hwnd, IntPtr idEvent); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.MSG.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.MSG.cs index eb87fa953efa7..66ae0b7947f7d 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.MSG.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.MSG.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [StructLayout(LayoutKind.Sequential)] public struct MSG diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.MessageBeep.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.MessageBeep.cs index ab8402daac3d6..ad7bbbedcc17e 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.MessageBeep.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.MessageBeep.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { internal const int MB_OK = 0; internal const int MB_ICONHAND = 0x10; diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.MsgWaitForMultipleObjectsEx.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.MsgWaitForMultipleObjectsEx.cs index ccea1d673be20..0b47d2be7659f 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.MsgWaitForMultipleObjectsEx.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.MsgWaitForMultipleObjectsEx.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.PeekMessage.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.PeekMessage.cs index 3ae4a801d7924..45710968dba9d 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.PeekMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.PeekMessage.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern bool PeekMessageW([In, Out] ref MSG msg, IntPtr hwnd, int msgMin, int msgMax, int remove); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.PostMessage.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.PostMessage.cs index f06edc40bfa44..eeec1ba0b518f 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.PostMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.PostMessage.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern int PostMessageW(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterClass.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterClass.cs index 8dece96082725..270b0de400061 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterClass.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterClass.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] public static extern short RegisterClassW(ref WNDCLASS wc); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterWindowMessage.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterWindowMessage.cs index 4349e5f21ac9b..df46561daab79 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterWindowMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.RegisterWindowMessage.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern int RegisterWindowMessageW(string msg); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessage.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessage.cs index c7500295ec8c4..aafb7c7e6f0a4 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessage.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern IntPtr SendMessageW(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessageTimeout.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessageTimeout.cs index 49f95350ece59..7472803ade678 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessageTimeout.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SendMessageTimeout.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, EntryPoint = "SendMessageTimeoutW")] public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, int flags, int timeout, out IntPtr pdwResult); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLong.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLong.cs index 903f3679efc71..1278d8bdb95f8 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLong.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLong.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern IntPtr SetClassLongW(IntPtr hwnd, int nIndex, IntPtr dwNewLong); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLongPtr.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLongPtr.cs index 44f2f75ccb764..2277ae1f230a4 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLongPtr.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetClassLongPtr.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern IntPtr SetClassLongPtrW(IntPtr hwnd, int nIndex, IntPtr dwNewLong); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetTimer.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetTimer.cs index b19b64d4ada61..0049286c2cacf 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetTimer.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetTimer.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern IntPtr SetTimer(IntPtr hWnd, IntPtr nIDEvent, int uElapse, IntPtr lpTimerProc); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLong.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLong.cs index c5ea5235bd763..669ff2310a14e 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLong.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLong.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern IntPtr SetWindowLongW(IntPtr hWnd, int nIndex, IntPtr dwNewLong); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLongPtr.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLongPtr.cs index ee08388d8ab36..e9f915ee89ff5 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLongPtr.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SetWindowLongPtr.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, ExactSpelling = true)] public static extern IntPtr SetWindowLongPtrW(IntPtr hWnd, int nIndex, IntPtr dwNewLong); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs index 81f2c9f953e3d..566ac5b212dc9 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { public enum SystemParametersAction : uint { diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.TranslateMessage.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.TranslateMessage.cs index c8a2bfdb83c7c..fe866df167886 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.TranslateMessage.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.TranslateMessage.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, ExactSpelling = true)] public static extern bool TranslateMessage([In, Out] ref MSG msg); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.USEROBJECTFLAGS.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.USEROBJECTFLAGS.cs index e743c36c28fb9..7c00e7c332fff 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.USEROBJECTFLAGS.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.USEROBJECTFLAGS.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { internal struct USEROBJECTFLAGS { diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.UnregisterClass.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.UnregisterClass.cs index eb0e108dc77f7..bf321d2a9028f 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.UnregisterClass.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.UnregisterClass.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] public static extern short UnregisterClassW(string lpClassName, IntPtr hInstance); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.WNDCLASS.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.WNDCLASS.cs index 4bbf3045889da..d60d354666938 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.WNDCLASS.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.WNDCLASS.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal unsafe struct WNDCLASS diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.WaitForInputIdle.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.WaitForInputIdle.cs index aa4136349ebc8..f6d3667775628 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.WaitForInputIdle.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.WaitForInputIdle.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { [DllImport(Libraries.User32)] public static extern int WaitForInputIdle(SafeProcessHandle handle, int milliseconds); diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.Win32SystemColors.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.Win32SystemColors.cs index a8187192bbf1c..2a5305f161821 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.Win32SystemColors.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.Win32SystemColors.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { internal enum Win32SystemColors : byte { diff --git a/src/libraries/Common/src/Interop/Windows/User32/Interop.WndProc.cs b/src/libraries/Common/src/Interop/Windows/User32/Interop.WndProc.cs index 105cc2764cdbc..a28a5603f958b 100644 --- a/src/libraries/Common/src/Interop/Windows/User32/Interop.WndProc.cs +++ b/src/libraries/Common/src/Interop/Windows/User32/Interop.WndProc.cs @@ -3,9 +3,9 @@ using System; -internal partial class Interop +internal static partial class Interop { - internal partial class User32 + internal static partial class User32 { public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); } diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfo.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfo.cs index f37615a203241..2b4d0b89ec9fe 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfo.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Version + internal static partial class Version { - internal partial class FileVersionInfo + internal static partial class FileVersionInfo { internal const int VS_FF_DEBUG = 0x1; internal const int VS_FF_PRERELEASE = 0x2; diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfoType.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfoType.cs index 7463c5277a6fa..13b7671d2de26 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfoType.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.FileVersionInfoType.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Version + internal static partial class Version { - internal partial class FileVersionInfoType + internal static partial class FileVersionInfoType { internal const int FILE_VER_GET_LOCALISED = 0x1; internal const int FILE_VER_GET_NEUTRAL = 0x2; diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs index dde98554145c9..5392b85d67360 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Version + internal static partial class Version { [DllImport(Libraries.Version, CharSet = CharSet.Unicode, EntryPoint = "GetFileVersionInfoExW")] internal static extern bool GetFileVersionInfoEx( diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoSizeEx.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoSizeEx.cs index 1c36bdd1dffe1..e6a703695db18 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoSizeEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoSizeEx.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Version + internal static partial class Version { [DllImport(Libraries.Version, CharSet = CharSet.Unicode, EntryPoint = "GetFileVersionInfoSizeExW")] internal static extern uint GetFileVersionInfoSizeEx(uint dwFlags, string lpwstrFilename, out uint lpdwHandle); diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.VSFixedFileInfo.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.VSFixedFileInfo.cs index f7afa20085485..4f52b8135ae8b 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.VSFixedFileInfo.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.VSFixedFileInfo.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Version + internal static partial class Version { [StructLayout(LayoutKind.Sequential)] internal struct VS_FIXEDFILEINFO diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs index ee71fa8d64c91..60c122d07b42e 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs @@ -4,9 +4,9 @@ using System; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Version + internal static partial class Version { [DllImport(Libraries.Version, CharSet = CharSet.Unicode, EntryPoint = "VerQueryValueW")] internal static extern bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out uint puLen); diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.SafeWinHttpHandle.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.SafeWinHttpHandle.cs index 70945ccd01c22..77c581a239914 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.SafeWinHttpHandle.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.SafeWinHttpHandle.cs @@ -7,9 +7,9 @@ using Microsoft.Win32.SafeHandles; -internal partial class Interop +internal static partial class Interop { - internal partial class WinHttp + internal static partial class WinHttp { internal class SafeWinHttpHandle : SafeHandleZeroOrMinusOneIsInvalid { diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs index c79c05f5a640d..7ac85b3155bbc 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class WinHttp + internal static partial class WinHttp { [DllImport(Interop.Libraries.WinHttp, CharSet = CharSet.Unicode, SetLastError = true)] public static extern SafeWinHttpHandle WinHttpOpen( diff --git a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs index c8f49628123c5..ed6186d34a8d1 100644 --- a/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs +++ b/src/libraries/Common/src/Interop/Windows/WinHttp/Interop.winhttp_types.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Text; -internal partial class Interop +internal static partial class Interop { - internal partial class WinHttp + internal static partial class WinHttp { public const uint ERROR_SUCCESS = 0; public const uint ERROR_FILE_NOT_FOUND = 2; @@ -129,10 +129,10 @@ internal partial class WinHttp public const uint WINHTTP_AUTH_TARGET_PROXY = 0x00000001; public const uint WINHTTP_OPTION_USERNAME = 0x1000; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="It is property descriptor, not secret value.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. It is property descriptor, not secret value.")] public const uint WINHTTP_OPTION_PASSWORD = 0x1001; public const uint WINHTTP_OPTION_PROXY_USERNAME = 0x1002; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="It is property descriptor, not secret value.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. It is property descriptor, not secret value.")] public const uint WINHTTP_OPTION_PROXY_PASSWORD = 0x1003; public const uint WINHTTP_OPTION_SERVER_SPN_USED = 106; diff --git a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.MMCKINFO.cs b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.MMCKINFO.cs index f97f85224fda7..1bcbc116c8a29 100644 --- a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.MMCKINFO.cs +++ b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.MMCKINFO.cs @@ -8,7 +8,7 @@ internal static partial class Interop internal static partial class WinMM { [StructLayout(LayoutKind.Sequential)] - internal class MMCKINFO + internal sealed class MMCKINFO { internal int ckID; internal int cksize; diff --git a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.mmioRead.cs b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.mmioRead.cs index 38b257ae3a1d6..3fb30c873de64 100644 --- a/src/libraries/Common/src/Interop/Windows/WinMm/Interop.mmioRead.cs +++ b/src/libraries/Common/src/Interop/Windows/WinMm/Interop.mmioRead.cs @@ -9,7 +9,7 @@ internal static partial class Interop internal static partial class WinMM { [StructLayout(LayoutKind.Sequential)] - internal class WAVEFORMATEX + internal sealed class WAVEFORMATEX { internal short wFormatTag; internal short nChannels; diff --git a/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.Constants.cs b/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.Constants.cs index 520cd343e10bd..ac5dd35b2346b 100644 --- a/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.Constants.cs +++ b/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.Constants.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -internal partial class Interop +internal static partial class Interop { - internal partial class Wtsapi32 + internal static partial class Wtsapi32 { public const int NOTIFY_FOR_THIS_SESSION = 0x0; diff --git a/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSRegisterSessionNotification.cs b/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSRegisterSessionNotification.cs index b2c97359facb6..23c0a2886290b 100644 --- a/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSRegisterSessionNotification.cs +++ b/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSRegisterSessionNotification.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Wtsapi32 + internal static partial class Wtsapi32 { [DllImport(Libraries.Wtsapi32, ExactSpelling = true)] public static extern bool WTSRegisterSessionNotification(HandleRef hWnd, int dwFlags); diff --git a/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSUnRegisterSessionNotification.cs b/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSUnRegisterSessionNotification.cs index ecdac58d47606..a45ebf3049156 100644 --- a/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSUnRegisterSessionNotification.cs +++ b/src/libraries/Common/src/Interop/Windows/WtsApi32/Interop.WTSUnRegisterSessionNotification.cs @@ -3,9 +3,9 @@ using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { - internal partial class Wtsapi32 + internal static partial class Wtsapi32 { [DllImport(Libraries.Wtsapi32, ExactSpelling = true)] public static extern bool WTSUnRegisterSessionNotification(HandleRef hWnd); diff --git a/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs b/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs index 5144111b7652d..cbe065b5a5a4b 100644 --- a/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs +++ b/src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs @@ -25,7 +25,7 @@ internal enum CodeTypeReferenceOptions #if !FEATURE_SERIALIZATION public class CodeTypeReference : CodeObject #else - internal class CodeTypeReference : CodeObject + internal sealed class CodeTypeReference : CodeObject #endif { private string? _baseType; diff --git a/src/libraries/Common/src/System/CodeDom/CodeTypeReferenceCollection.cs b/src/libraries/Common/src/System/CodeDom/CodeTypeReferenceCollection.cs index 7705d998afc88..92df60156f9be 100644 --- a/src/libraries/Common/src/System/CodeDom/CodeTypeReferenceCollection.cs +++ b/src/libraries/Common/src/System/CodeDom/CodeTypeReferenceCollection.cs @@ -12,7 +12,7 @@ namespace System.Runtime.Serialization #if !FEATURE_SERIALIZATION public class CodeTypeReferenceCollection : CollectionBase #else - internal class CodeTypeReferenceCollection : CollectionBase + internal sealed class CodeTypeReferenceCollection : CollectionBase #endif { public CodeTypeReferenceCollection() { } diff --git a/src/libraries/Common/src/System/Collections/Generic/EnumerableHelpers.cs b/src/libraries/Common/src/System/Collections/Generic/EnumerableHelpers.cs index 256fad2a1d5ed..b5bebedb3af16 100644 --- a/src/libraries/Common/src/System/Collections/Generic/EnumerableHelpers.cs +++ b/src/libraries/Common/src/System/Collections/Generic/EnumerableHelpers.cs @@ -8,24 +8,6 @@ namespace System.Collections.Generic /// internal static partial class EnumerableHelpers { - /// Attempt to determine the count of the source enumerable without forcing an enumeration. - /// The source enumerable. - /// The count determined by the type test. - /// - /// True if the source enumerable could be determined without enumerating, false otherwise. - /// - internal static bool TryGetCount(IEnumerable source, out int count) - { - if (source is ICollection ict) - { - count = ict.Count; - return true; - } - - count = 0; - return false; - } - /// Converts an enumerable to an array using the same logic as List{T}. /// The enumerable to convert. /// The number of items stored in the resulting array, 0-indexed. diff --git a/src/libraries/Common/src/System/Data/Common/DbConnectionPoolKey.cs b/src/libraries/Common/src/System/Data/Common/DbConnectionPoolKey.cs index 6c5a567cf7ad7..6e3601ae81398 100644 --- a/src/libraries/Common/src/System/Data/Common/DbConnectionPoolKey.cs +++ b/src/libraries/Common/src/System/Data/Common/DbConnectionPoolKey.cs @@ -5,7 +5,7 @@ namespace System.Data.Common { // DbConnectionPoolKey: Base class implementation of a key to connection pool groups // Only connection string is used as a key - internal class DbConnectionPoolKey : ICloneable + internal sealed class DbConnectionPoolKey : ICloneable { private string? _connectionString; @@ -14,17 +14,17 @@ internal DbConnectionPoolKey(string? connectionString) _connectionString = connectionString; } - protected DbConnectionPoolKey(DbConnectionPoolKey key) + private DbConnectionPoolKey(DbConnectionPoolKey key) { _connectionString = key.ConnectionString; } - public virtual object Clone() + public object Clone() { return new DbConnectionPoolKey(this); } - internal virtual string? ConnectionString + internal string? ConnectionString { get { diff --git a/src/libraries/Common/src/System/Data/Common/MultipartIdentifier.cs b/src/libraries/Common/src/System/Data/Common/MultipartIdentifier.cs index 0aaca592d0711..bca263e6eb0da 100644 --- a/src/libraries/Common/src/System/Data/Common/MultipartIdentifier.cs +++ b/src/libraries/Common/src/System/Data/Common/MultipartIdentifier.cs @@ -7,7 +7,7 @@ namespace System.Data.Common { - internal class MultipartIdentifier + internal static class MultipartIdentifier { private const int MaxParts = 4; internal const int ServerIndex = 0; diff --git a/src/libraries/Common/src/System/Data/ProviderBase/TimeoutTimer.cs b/src/libraries/Common/src/System/Data/ProviderBase/TimeoutTimer.cs index 6f41bf428942f..dc242f39e4afe 100644 --- a/src/libraries/Common/src/System/Data/ProviderBase/TimeoutTimer.cs +++ b/src/libraries/Common/src/System/Data/ProviderBase/TimeoutTimer.cs @@ -18,7 +18,7 @@ namespace System.Data.ProviderBase // Get remaining time in appropriate format to pass to subsystem timeouts // Check for timeout via IsExpired for checks in managed code. // Simply abandon to GC when done. - internal class TimeoutTimer + internal sealed class TimeoutTimer { //------------------- // Fields diff --git a/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs b/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs index dfa0395e91122..b097ff36758fd 100644 --- a/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs +++ b/src/libraries/Common/src/System/Globalization/FormatProvider.Number.cs @@ -281,10 +281,8 @@ internal partial class FormatProvider // // This class contains only static members and does not need to be serializable - private partial class Number + private static partial class Number { - private Number() { } - // Constants used by number parsing private const int NumberMaxDigits = 32; diff --git a/src/libraries/Common/src/System/HResults.cs b/src/libraries/Common/src/System/HResults.cs index 22127a48ef37f..330c3df5fc73e 100644 --- a/src/libraries/Common/src/System/HResults.cs +++ b/src/libraries/Common/src/System/HResults.cs @@ -122,5 +122,33 @@ internal static partial class HResults internal const int RO_E_CLOSED = unchecked((int)0x80000013); internal const int RPC_E_CHANGED_MODE = unchecked((int)0x80010106); internal const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); + internal const int STG_E_PATHNOTFOUND = unchecked((int)0x80030003); + internal const int CTL_E_PATHNOTFOUND = unchecked((int)0x800A004C); + internal const int CTL_E_FILENOTFOUND = unchecked((int)0x800A0035); + internal const int FUSION_E_INVALID_PRIVATE_ASM_LOCATION = unchecked((int)0x80131041); + internal const int FUSION_E_SIGNATURE_CHECK_FAILED = unchecked((int)0x80131045); + internal const int FUSION_E_LOADFROM_BLOCKED = unchecked((int)0x80131051); + internal const int FUSION_E_CACHEFILE_FAILED = unchecked((int)0x80131052); + internal const int FUSION_E_ASM_MODULE_MISSING = unchecked((int)0x80131042); + internal const int FUSION_E_INVALID_NAME = unchecked((int)0x80131047); + internal const int FUSION_E_PRIVATE_ASM_DISALLOWED = unchecked((int)0x80131044); + internal const int FUSION_E_HOST_GAC_ASM_MISMATCH = unchecked((int)0x80131050); + internal const int COR_E_MODULE_HASH_CHECK_FAILED = unchecked((int)0x80131039); + internal const int FUSION_E_REF_DEF_MISMATCH = unchecked((int)0x80131040); + internal const int SECURITY_E_INCOMPATIBLE_SHARE = unchecked((int)0x80131401); + internal const int SECURITY_E_INCOMPATIBLE_EVIDENCE = unchecked((int)0x80131403); + internal const int SECURITY_E_UNVERIFIABLE = unchecked((int)0x80131402); + internal const int COR_E_FIXUPSINEXE = unchecked((int)0x80131019); + internal const int ERROR_TOO_MANY_OPEN_FILES = unchecked((int)0x80070004); + internal const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020); + internal const int ERROR_LOCK_VIOLATION = unchecked((int)0x80070021); + internal const int ERROR_OPEN_FAILED = unchecked((int)0x8007006E); + internal const int ERROR_DISK_CORRUPT = unchecked((int)0x80070571); + internal const int ERROR_UNRECOGNIZED_VOLUME = unchecked((int)0x800703ED); + internal const int ERROR_DLL_INIT_FAILED = unchecked((int)0x8007045A); + internal const int FUSION_E_CODE_DOWNLOAD_DISABLED = unchecked((int)0x80131048); + internal const int CORSEC_E_MISSING_STRONGNAME = unchecked((int)0x8013141b); + internal const int MSEE_E_ASSEMBLYLOADINPROGRESS = unchecked((int)0x80131016); + internal const int ERROR_FILE_INVALID = unchecked((int)0x800703EE); } } diff --git a/src/libraries/Common/src/System/Net/ArrayBuffer.cs b/src/libraries/Common/src/System/Net/ArrayBuffer.cs index 5dbc5a158cb78..56413164232da 100644 --- a/src/libraries/Common/src/System/Net/ArrayBuffer.cs +++ b/src/libraries/Common/src/System/Net/ArrayBuffer.cs @@ -41,15 +41,12 @@ public void Dispose() _activeStart = 0; _availableStart = 0; - if (_usePool) - { - byte[] array = _bytes; - _bytes = null!; + byte[] array = _bytes; + _bytes = null!; - if (array != null) - { - ArrayPool.Shared.Return(array); - } + if (_usePool && array != null) + { + ArrayPool.Shared.Return(array); } } diff --git a/src/libraries/Common/src/System/Net/CaseInsensitiveAscii.cs b/src/libraries/Common/src/System/Net/CaseInsensitiveAscii.cs index 769b6c6738254..2b0cb7f207dca 100644 --- a/src/libraries/Common/src/System/Net/CaseInsensitiveAscii.cs +++ b/src/libraries/Common/src/System/Net/CaseInsensitiveAscii.cs @@ -5,7 +5,7 @@ namespace System.Net { - internal class CaseInsensitiveAscii : IEqualityComparer, IComparer + internal sealed class CaseInsensitiveAscii : IEqualityComparer, IComparer { // ASCII char ToLower table internal static readonly CaseInsensitiveAscii StaticInstance = new CaseInsensitiveAscii(); diff --git a/src/libraries/Common/src/System/Net/ContextAwareResult.cs b/src/libraries/Common/src/System/Net/ContextAwareResult.cs index a6dd15c638df9..aa073928b3ed4 100644 --- a/src/libraries/Common/src/System/Net/ContextAwareResult.cs +++ b/src/libraries/Common/src/System/Net/ContextAwareResult.cs @@ -18,7 +18,7 @@ namespace System.Net // // For now the state is not included as part of the closure. It is too common a pattern (for example with socket receive) // to have several pending IOs differentiated by their state object. We don't want that pattern to break the cache. - internal class CallbackClosure + internal sealed class CallbackClosure { private readonly AsyncCallback? _savedCallback; private readonly ExecutionContext? _savedContext; diff --git a/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs b/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs index c7271b65546ed..1e623f7a7df0f 100644 --- a/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs +++ b/src/libraries/Common/src/System/Net/Http/WinInetProxyHelper.cs @@ -10,7 +10,7 @@ namespace System.Net.Http { // This class is only used on OS versions where WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY // is not supported (i.e. before Win8.1/Win2K12R2) in the WinHttpOpen() function. - internal class WinInetProxyHelper + internal sealed class WinInetProxyHelper { private const int RecentAutoDetectionInterval = 120_000; // 2 minutes in milliseconds. private readonly string? _autoConfigUrl, _proxy, _proxyBypass; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs index cb6a76350e2db..7b496baf83ee5 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/DynamicTable.cs @@ -3,7 +3,7 @@ namespace System.Net.Http.HPack { - internal class DynamicTable + internal sealed class DynamicTable { private HeaderField[] _buffer; private int _maxSize; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs index 8899104eb18d7..2cce40894f1d0 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecoder.cs @@ -11,7 +11,7 @@ namespace System.Net.Http.HPack { - internal class HPackDecoder + internal sealed class HPackDecoder { private enum State : byte { diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecodingException.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecodingException.cs index 605cac1c0fe4b..b0225bb6f9a14 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecodingException.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackDecodingException.cs @@ -7,7 +7,7 @@ namespace System.Net.Http.HPack { // TODO: Should this be public? [Serializable] - internal class HPackDecodingException : Exception + internal sealed class HPackDecodingException : Exception { public HPackDecodingException() { diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs index 4c3ac29527049..67a61c3c69f3f 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HPackEncoder.cs @@ -8,7 +8,7 @@ namespace System.Net.Http.HPack { - internal partial class HPackEncoder + internal static partial class HPackEncoder { // Things we should add: // * Huffman encoding diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HuffmanDecodingException.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HuffmanDecodingException.cs index 7199e3666809f..287497b7ee115 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HuffmanDecodingException.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/HuffmanDecodingException.cs @@ -7,7 +7,7 @@ namespace System.Net.Http.HPack { // TODO: Should this be public? [Serializable] - internal class HuffmanDecodingException : Exception, ISerializable + internal sealed class HuffmanDecodingException : Exception, ISerializable { public HuffmanDecodingException() { @@ -18,7 +18,7 @@ public HuffmanDecodingException(string message) { } - protected HuffmanDecodingException(SerializationInfo info, StreamingContext context) + private HuffmanDecodingException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/IntegerDecoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/IntegerDecoder.cs index f98fdf7b241a2..6faa058ebf997 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/IntegerDecoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/IntegerDecoder.cs @@ -6,7 +6,7 @@ namespace System.Net.Http.HPack { - internal class IntegerDecoder + internal sealed class IntegerDecoder { private int _i; private int _m; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3ErrorCode.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3ErrorCode.cs index 11e39e5f78482..fdfe04cee1580 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3ErrorCode.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3ErrorCode.cs @@ -52,38 +52,42 @@ internal enum Http3ErrorCode : long /// IdError = 0x108, /// - /// H3_SETTINGS_ERROR (0x10A): - /// An endpoint detected an error in the payload of a SETTINGS frame: a duplicate setting was detected, - /// a client-only setting was sent by a server, or a server-only setting by a client. + /// H3_SETTINGS_ERROR (0x109): + /// An endpoint detected an error in the payload of a SETTINGS frame. /// SettingsError = 0x109, /// - /// H3_MISSING_SETTINGS (0x10B): + /// H3_MISSING_SETTINGS (0x10A): /// No SETTINGS frame was received at the beginning of the control stream. /// MissingSettings = 0x10a, /// - /// H3_REQUEST_REJECTED (0x10C): + /// H3_REQUEST_REJECTED (0x10B): /// A server rejected a request without performing any application processing. /// RequestRejected = 0x10b, /// - /// H3_REQUEST_CANCELLED (0x10D): + /// H3_REQUEST_CANCELLED (0x10C): /// The request or its response (including pushed response) is cancelled. /// RequestCancelled = 0x10c, /// - /// H3_REQUEST_INCOMPLETE (0x10E): - /// The client?s stream terminated without containing a fully-formed request. + /// H3_REQUEST_INCOMPLETE (0x10D): + /// The client's stream terminated without containing a fully-formed request. /// RequestIncomplete = 0x10d, /// - /// H3_CONNECT_ERROR (0x110): + /// H3_MESSAGE_ERROR (0x10E): + /// An HTTP message was malformed and cannot be processed. + /// + MessageError = 0x10e, + /// + /// H3_CONNECT_ERROR (0x10F): /// The connection established in response to a CONNECT request was reset or abnormally closed. /// ConnectError = 0x10f, /// - /// H3_VERSION_FALLBACK (0x111): + /// H3_VERSION_FALLBACK (0x110): /// The requested operation cannot be served over HTTP/3. The peer should retry over HTTP/1.1. /// VersionFallback = 0x110, diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3FrameType.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3FrameType.cs index 08b24cf15f030..841f65677f39b 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3FrameType.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/Frames/Http3FrameType.cs @@ -24,7 +24,6 @@ internal enum Http3FrameType : long GoAway = 0x7, ReservedHttp2WindowUpdate = 0x8, ReservedHttp2Continuation = 0x9, - MaxPushId = 0xD, - DuplicatePush = 0xE + MaxPushId = 0xD } } diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs index fdb03562cc8a9..96de7c6533809 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackDecoder.cs @@ -13,7 +13,7 @@ namespace System.Net.Http.QPack { - internal class QPackDecoder : IDisposable + internal sealed class QPackDecoder : IDisposable { private enum State { diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs index 97b85b41a5273..1b09f8e9ca397 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/QPackEncoder.cs @@ -9,7 +9,7 @@ namespace System.Net.Http.QPack { - internal class QPackEncoder + internal sealed class QPackEncoder { private IEnumerator>? _enumerator; diff --git a/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs b/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs index 9b6f05819de5f..96090ceeeac24 100644 --- a/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs +++ b/src/libraries/Common/src/System/Net/Logging/NetEventSource.Common.cs @@ -52,7 +52,7 @@ internal sealed partial class NetEventSource : EventSource public static readonly NetEventSource Log = new NetEventSource(); #region Metadata - public class Keywords + public static class Keywords { public const EventKeywords Default = (EventKeywords)0x0001; public const EventKeywords Debug = (EventKeywords)0x0002; diff --git a/src/libraries/Common/src/System/Net/Mail/MailAddress.cs b/src/libraries/Common/src/System/Net/Mail/MailAddress.cs index 3a18c009a2f95..8232cbc358345 100644 --- a/src/libraries/Common/src/System/Net/Mail/MailAddress.cs +++ b/src/libraries/Common/src/System/Net/Mail/MailAddress.cs @@ -5,7 +5,7 @@ namespace System.Net.Mail { - internal class MailAddress + internal sealed class MailAddress { public MailAddress(string address) { diff --git a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs index fa4d96807ada4..b98f271183145 100644 --- a/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs +++ b/src/libraries/Common/src/System/Net/NTAuthentication.Common.cs @@ -9,7 +9,7 @@ namespace System.Net { - internal partial class NTAuthentication + internal sealed partial class NTAuthentication { private bool _isServer; diff --git a/src/libraries/Common/src/System/Net/NetworkInformation/HostInformation.cs b/src/libraries/Common/src/System/Net/NetworkInformation/HostInformation.cs index e60de472ede5f..febb6b8ad2453 100644 --- a/src/libraries/Common/src/System/Net/NetworkInformation/HostInformation.cs +++ b/src/libraries/Common/src/System/Net/NetworkInformation/HostInformation.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class HostInformation + internal static class HostInformation { // Specifies the host name for the local computer. internal static string HostName diff --git a/src/libraries/Common/src/System/Net/TlsStream.cs b/src/libraries/Common/src/System/Net/TlsStream.cs index d1557b97ff1be..0d2319916c4f1 100644 --- a/src/libraries/Common/src/System/Net/TlsStream.cs +++ b/src/libraries/Common/src/System/Net/TlsStream.cs @@ -8,7 +8,7 @@ namespace System.Net { - internal class TlsStream : NetworkStream + internal sealed class TlsStream : NetworkStream { private readonly SslStream _sslStream; private readonly string _host; diff --git a/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs b/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs index 729f82602da74..a092c96648389 100644 --- a/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs +++ b/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs @@ -58,7 +58,7 @@ internal static void ValidateSubprotocol(string subProtocol) char ch = subProtocol[i]; if (ch < 0x21 || ch > 0x7e) { - invalidChar = string.Format(CultureInfo.InvariantCulture, "[{0}]", (int)ch); + invalidChar = $"[{(int)ch}]"; break; } diff --git a/src/libraries/Common/src/System/Obsoletions.cs b/src/libraries/Common/src/System/Obsoletions.cs index 1b409c7628f3c..a4f6fb66e1f75 100644 --- a/src/libraries/Common/src/System/Obsoletions.cs +++ b/src/libraries/Common/src/System/Obsoletions.cs @@ -48,5 +48,8 @@ internal static class Obsoletions internal const string WebRequestMessage = "Use HttpClient instead."; internal const string WebRequestDiagId = "SYSLIB0014"; + + internal const string DisablePrivateReflectionAttributeMessage = "DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications."; + internal const string DisablePrivateReflectionAttributeDiagId = "SYSLIB0015"; } } diff --git a/src/libraries/Common/src/System/Resources/ResourceWriter.cs b/src/libraries/Common/src/System/Resources/ResourceWriter.cs index 9cdc0e5763855..71697a29e04bb 100644 --- a/src/libraries/Common/src/System/Resources/ResourceWriter.cs +++ b/src/libraries/Common/src/System/Resources/ResourceWriter.cs @@ -182,7 +182,7 @@ private void AddResourceData(string name, string typeName, object data) // For cases where users can't create an instance of the deserialized // type in memory, and need to pass us serialized blobs instead. // LocStudio's managed code parser will do this in some cases. - private class PrecannedResource + private sealed class PrecannedResource { internal readonly string TypeName; internal readonly object Data; @@ -194,7 +194,7 @@ internal PrecannedResource(string typeName, object data) } } - private class StreamWrapper + private sealed class StreamWrapper { internal readonly Stream Stream; internal readonly bool CloseAfterWrite; diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs index 8db5db75f1715..e8e736e0d7890 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsMethod.cs @@ -11,7 +11,7 @@ namespace System.Runtime.InteropServices /// Part of ComEventHelpers APIs which allow binding /// managed delegates to COM's connection point based events. /// - internal class ComEventsMethod + internal sealed class ComEventsMethod { /// /// This delegate wrapper class handles dynamic invocation of delegates. The reason for the wrapper's diff --git a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsSink.cs b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsSink.cs index 678359974789b..3747e2f331b4b 100644 --- a/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsSink.cs +++ b/src/libraries/Common/src/System/Runtime/InteropServices/ComEventsSink.cs @@ -11,7 +11,7 @@ namespace System.Runtime.InteropServices /// managed delegates to COM's connection point based events. /// [SupportedOSPlatform("windows")] - internal partial class ComEventsSink : IDispatch, ICustomQueryInterface + internal sealed partial class ComEventsSink : IDispatch, ICustomQueryInterface { private Guid _iidSourceItf; private ComTypes.IConnectionPoint? _connectionPoint; diff --git a/src/libraries/Common/src/System/SR.cs b/src/libraries/Common/src/System/SR.cs index 20431f256a1ac..6d116dd9a4fcb 100644 --- a/src/libraries/Common/src/System/SR.cs +++ b/src/libraries/Common/src/System/SR.cs @@ -6,7 +6,7 @@ namespace System { - internal partial class SR + internal static partial class SR { #if (!NETSTANDARD1_0 && !NETSTANDARD1_1 && !NET45) // AppContext is not supported on < NetStandard1.3 or < .NET Framework 4.5 private static readonly bool s_usingResourceKeys = AppContext.TryGetSwitch("System.Resources.UseSystemResourceKeys", out bool usingResourceKeys) ? usingResourceKeys : false; diff --git a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AsnXml.targets b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AsnXml.targets index 751187ddfe69a..9f72a36835ca0 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/Asn1/AsnXml.targets +++ b/src/libraries/Common/src/System/Security/Cryptography/Asn1/AsnXml.targets @@ -29,7 +29,7 @@ + Command="$(_AsnXmlDiffCmd) @(AsnXml -> '"$(_AsnIntermediatePath)%(filename).cs"') @(AsnXml -> '"%(Identity).cs"')"> diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs index 62419f8cc808d..19bc218d3d597 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDiffieHellmanSecurityTransforms.cs @@ -256,7 +256,7 @@ public override byte[] DeriveKeyTls(ECDiffieHellmanPublicKey otherPartyPublicKey public override ECDiffieHellmanPublicKey PublicKey => new ECDiffieHellmanSecurityTransformsPublicKey(ExportParameters(false)); - private class ECDiffieHellmanSecurityTransformsPublicKey : ECDiffieHellmanPublicKey + private sealed class ECDiffieHellmanSecurityTransformsPublicKey : ECDiffieHellmanPublicKey { private EccSecurityTransforms _ecc; diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs index 2fa748a23ace0..ae8d49719643e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAAndroid.cs @@ -587,7 +587,7 @@ private static void CheckBoolReturn(int returnValue) { if (returnValue != 1) { - throw new CryptographicException(); + throw new CryptographicException(); } } @@ -810,6 +810,11 @@ public override bool VerifyHash(ReadOnlySpan hash, ReadOnlySpan sign int ret = Interop.AndroidCrypto.RsaVerificationPrimitive(signature, unwrapped, rsa); CheckReturn(ret); + if (ret == 0) + { + // Return value of 0 from RsaVerificationPrimitive indicates the signature could not be decrypted. + return false; + } Debug.Assert( ret == requiredBytes, diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs b/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs index 7eaf3f9e9a6d3..82709832cd88d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSACng.SignVerify.cs @@ -32,9 +32,9 @@ private static int GetHashSizeInBytes(HashAlgorithmName hashAlgorithm) { return s_hashSizes.GetOrAdd( hashAlgorithm, - alg => + static hashAlgorithm => { - using (HashProviderCng hashProvider = new HashProviderCng(alg.Name!, null)) + using (HashProviderCng hashProvider = new HashProviderCng(hashAlgorithm.Name!, null)) { return hashProvider.HashSizeInBytes; } diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs index df9fd27e532b0..1bbcade3bbef6 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RSAOpenSsl.cs @@ -23,9 +23,6 @@ public sealed partial class RSAOpenSsl : RSA { private const int BitsPerByte = 8; - // 65537 (0x10001) in big-endian form - private static ReadOnlySpan DefaultExponent => new byte[] { 0x01, 0x00, 0x01 }; - private Lazy _key; public RSAOpenSsl() @@ -86,10 +83,9 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) if (padding == null) throw new ArgumentNullException(nameof(padding)); - Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor); - SafeRsaHandle key = GetKey(); - - int rsaSize = Interop.Crypto.RsaSize(key); + ValidatePadding(padding); + SafeEvpPKeyHandle key = GetPKey(); + int rsaSize = Interop.Crypto.EvpPKeySize(key); byte[]? buf = null; Span destination = default; @@ -98,18 +94,15 @@ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) buf = CryptoPool.Rent(rsaSize); destination = new Span(buf, 0, rsaSize); - if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten)) - { - Debug.Fail($"{nameof(TryDecrypt)} should not return false for RSA_size buffer"); - throw new CryptographicException(); - } - + int bytesWritten = Decrypt(key, data, destination, padding); return destination.Slice(0, bytesWritten).ToArray(); } finally { CryptographicOperations.ZeroMemory(destination); CryptoPool.Return(buf!, clearSize: 0); + // Until EVP_PKEY is what gets stored, free the temporary key handle. + key.Dispose(); } } @@ -120,82 +113,73 @@ public override bool TryDecrypt( out int bytesWritten) { if (padding == null) - { throw new ArgumentNullException(nameof(padding)); - } - Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor); - SafeRsaHandle key = GetKey(); - - int keySizeBytes = Interop.Crypto.RsaSize(key); + ValidatePadding(padding); + SafeEvpPKeyHandle key = GetPKey(); + int keySizeBytes = Interop.Crypto.EvpPKeySize(key); - // OpenSSL does not take a length value for the destination, so it can write out of bounds. - // To prevent the OOB write, decrypt into a temporary buffer. + // OpenSSL requires that the decryption buffer be at least as large as EVP_PKEY_size. + // So if the destination is too small, use a temporary buffer so we can match + // Windows behavior of succeeding so long as the buffer can hold the final output. if (destination.Length < keySizeBytes) { - Span tmp = stackalloc byte[0]; + // RSA up through 4096 bits use a stackalloc + Span tmp = stackalloc byte[512]; byte[]? rent = null; - // RSA up through 4096 stackalloc - if (keySizeBytes <= 512) + if (keySizeBytes > tmp.Length) { - tmp = stackalloc byte[keySizeBytes]; - } - else - { - rent = ArrayPool.Shared.Rent(keySizeBytes); + rent = CryptoPool.Rent(keySizeBytes); tmp = rent; } - bool ret = TryDecrypt(key, data, tmp, rsaPadding, oaepProcessor, out bytesWritten); + int written = Decrypt(key, data, tmp, padding); + // Until EVP_PKEY is what gets stored, free the temporary key handle. + key.Dispose(); + bool ret; - if (ret) + if (destination.Length < written) { - tmp = tmp.Slice(0, bytesWritten); - - if (bytesWritten > destination.Length) - { - ret = false; - bytesWritten = 0; - } - else - { - tmp.CopyTo(destination); - } - - CryptographicOperations.ZeroMemory(tmp); + bytesWritten = 0; + ret = false; + } + else + { + tmp.Slice(0, written).CopyTo(destination); + bytesWritten = written; + ret = true; } + // Whether a stackalloc or a rented array, clear our copy of + // the decrypted content. + CryptographicOperations.ZeroMemory(tmp.Slice(0, written)); + if (rent != null) { - // Already cleared - ArrayPool.Shared.Return(rent); + // Already cleared. + CryptoPool.Return(rent, clearSize: 0); } return ret; } - return TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out bytesWritten); + bytesWritten = Decrypt(key, data, destination, padding); + // Until EVP_PKEY is what gets stored, free the temporary key handle. + key.Dispose(); + return true; } - private static bool TryDecrypt( - SafeRsaHandle key, + private static int Decrypt( + SafeEvpPKeyHandle key, ReadOnlySpan data, Span destination, - Interop.Crypto.RsaPadding rsaPadding, - RsaPaddingProcessor? rsaPaddingProcessor, - out int bytesWritten) + RSAEncryptionPadding padding) { - // If rsaPadding is PKCS1 or OAEP-SHA1 then no depadding method should be present. - // If rsaPadding is NoPadding then a depadding method should be present. - Debug.Assert( - (rsaPadding == Interop.Crypto.RsaPadding.NoPadding) == - (rsaPaddingProcessor != null)); - // Caller should have already checked this. Debug.Assert(!key.IsInvalid); - int rsaSize = Interop.Crypto.RsaSize(key); + int rsaSize = Interop.Crypto.EvpPKeySize(key); if (data.Length != rsaSize) { @@ -204,50 +188,24 @@ private static bool TryDecrypt( if (destination.Length < rsaSize) { - bytesWritten = 0; - return false; + Debug.Fail("Caller is responsible for temporary decryption buffer creation"); + throw new CryptographicException(); } - Span decryptBuf = destination; - byte[]? paddingBuf = null; + IntPtr hashAlgorithm = IntPtr.Zero; - if (rsaPaddingProcessor != null) + if (padding.Mode == RSAEncryptionPaddingMode.Oaep) { - paddingBuf = CryptoPool.Rent(rsaSize); - decryptBuf = paddingBuf; + Debug.Assert(padding.OaepHashAlgorithm.Name != null); + hashAlgorithm = Interop.Crypto.HashAlgorithmToEvp(padding.OaepHashAlgorithm.Name); } - try - { - int returnValue = Interop.Crypto.RsaPrivateDecrypt(data.Length, data, decryptBuf, key, rsaPadding); - CheckReturn(returnValue); - - if (rsaPaddingProcessor != null) - { - return rsaPaddingProcessor.DepadOaep(paddingBuf, destination, out bytesWritten); - } - else - { - // If the padding mode is RSA_NO_PADDING then the size of the decrypted block - // will be RSA_size. If any padding was used, then some amount (determined by the padding algorithm) - // will have been reduced, and only returnValue bytes were part of the decrypted - // body. Either way, we can just use returnValue, but some additional bytes may have been overwritten - // in the destination span. - bytesWritten = returnValue; - } - - return true; - } - finally - { - if (paddingBuf != null) - { - // DecryptBuf is paddingBuf if paddingBuf is not null, erase it before returning it. - // If paddingBuf IS null then decryptBuf was destination, and shouldn't be cleared. - CryptographicOperations.ZeroMemory(decryptBuf); - CryptoPool.Return(paddingBuf, clearSize: 0); - } - } + return Interop.Crypto.RsaDecrypt( + key, + data, + padding.Mode, + hashAlgorithm, + destination); } public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) @@ -558,6 +516,30 @@ private void ThrowIfDisposed() } } + private SafeEvpPKeyHandle GetPKey() + { + SafeRsaHandle currentKey = GetKey(); + SafeEvpPKeyHandle pkeyHandle = Interop.Crypto.EvpPkeyCreate(); + + try + { + // Wrapping our key in an EVP_PKEY will up_ref our key. + // When the EVP_PKEY is Disposed it will down_ref the key. + // So everything should be copacetic. + if (!Interop.Crypto.EvpPkeySetRsa(pkeyHandle, currentKey)) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + catch + { + pkeyHandle.Dispose(); + throw; + } + + return pkeyHandle; + } + private SafeRsaHandle GetKey() { ThrowIfDisposed(); @@ -590,36 +572,18 @@ private static void CheckBoolReturn(int returnValue) private SafeRsaHandle GenerateKey() { - SafeRsaHandle key = Interop.Crypto.RsaCreate(); - bool generated = false; - - Interop.Crypto.CheckValidOpenSslHandle(key); - - try - { - using (SafeBignumHandle exponent = Interop.Crypto.CreateBignum(DefaultExponent)) - { - // The documentation for RSA_generate_key_ex does not say that it returns only - // 0 or 1, so the call marshals it back as a full Int32 and checks for a value - // of 1 explicitly. - int response = Interop.Crypto.RsaGenerateKeyEx( - key, - KeySize, - exponent); - - CheckBoolReturn(response); - generated = true; - } - } - finally + using (SafeEvpPKeyHandle pkey = Interop.Crypto.RsaGenerateKey(KeySize)) { - if (!generated) + SafeRsaHandle rsa = Interop.Crypto.EvpPkeyGetRsa(pkey); + + if (rsa.IsInvalid) { - key.Dispose(); + rsa.Dispose(); + throw Interop.Crypto.CreateOpenSslCryptographicException(); } - } - return key; + return rsa; + } } protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) => @@ -696,84 +660,33 @@ private bool TrySignHash( { Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name)); Debug.Assert(padding != null); + ValidatePadding(padding); signature = null; - // Do not factor out getting _key.Value, since the key creation should not happen on - // invalid padding modes. + IntPtr digestAlgorithm = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithm.Name); + SafeEvpPKeyHandle key = GetPKey(); + int bytesRequired = Interop.Crypto.EvpPKeySize(key); - if (padding.Mode == RSASignaturePaddingMode.Pkcs1) + if (allocateSignature) { - int algorithmNid = GetAlgorithmNid(hashAlgorithm); - SafeRsaHandle rsa = GetKey(); - - int bytesRequired = Interop.Crypto.RsaSize(rsa); - - if (allocateSignature) - { - Debug.Assert(destination.Length == 0); - signature = new byte[bytesRequired]; - destination = signature; - } - - if (destination.Length < bytesRequired) - { - bytesWritten = 0; - return false; - } - - if (!Interop.Crypto.RsaSign(algorithmNid, hash, hash.Length, destination, out int signatureSize, rsa)) - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } - - Debug.Assert( - signatureSize == bytesRequired, - $"RSA_sign reported signatureSize was {signatureSize}, when {bytesRequired} was expected"); - - bytesWritten = signatureSize; - return true; + Debug.Assert(destination.Length == 0); + signature = new byte[bytesRequired]; + destination = signature; } - else if (padding.Mode == RSASignaturePaddingMode.Pss) + else if (destination.Length < bytesRequired) { - RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm); - SafeRsaHandle rsa = GetKey(); - - int bytesRequired = Interop.Crypto.RsaSize(rsa); - - if (allocateSignature) - { - Debug.Assert(destination.Length == 0); - signature = new byte[bytesRequired]; - destination = signature; - } - - if (destination.Length < bytesRequired) - { - bytesWritten = 0; - return false; - } - - byte[] pssRented = CryptoPool.Rent(bytesRequired); - Span pssBytes = new Span(pssRented, 0, bytesRequired); - - processor.EncodePss(hash, pssBytes, KeySize); - - int ret = Interop.Crypto.RsaSignPrimitive(pssBytes, destination, rsa); - - CryptoPool.Return(pssRented, bytesRequired); - - CheckReturn(ret); - - Debug.Assert( - ret == bytesRequired, - $"RSA_private_encrypt returned {ret} when {bytesRequired} was expected"); - - bytesWritten = ret; - return true; + bytesWritten = 0; + return false; } - throw PaddingModeNotSupported(); + int written = Interop.Crypto.RsaSignHash(key, padding.Mode, digestAlgorithm, hash, destination); + Debug.Assert(written == bytesRequired); + bytesWritten = written; + + // Until EVP_PKEY is what gets stored, free the temporary key handle. + key.Dispose(); + return true; } public override bool VerifyHash( @@ -869,6 +782,55 @@ private static int GetAlgorithmNid(HashAlgorithmName hashAlgorithmName) return nid; } + private static void ValidatePadding(RSAEncryptionPadding padding) + { + if (padding == null) + { + throw new ArgumentNullException(nameof(padding)); + } + + // There are currently two defined padding modes: + // * Oaep has an option (the hash algorithm) + // * Pkcs1 has no options + // + // Anything other than those to modes is an error, + // and Pkcs1 having options set is an error, so compare it to + // the padding struct instead of the padding mode enum. + if (padding.Mode != RSAEncryptionPaddingMode.Oaep && + padding != RSAEncryptionPadding.Pkcs1) + { + throw PaddingModeNotSupported(); + } + } + + private static void ValidatePadding(RSASignaturePadding padding) + { + if (padding == null) + { + throw new ArgumentNullException(nameof(padding)); + } + + // RSASignaturePadding currently only has the mode property, so + // there's no need for a runtime check that PKCS#1 doesn't use + // nonsensical options like with RSAEncryptionPadding. + // + // This would change if we supported PSS with an MGF other than MGF-1, + // or with a custom salt size, or with a different MGF digest algorithm + // than the data digest algorithm. + if (padding.Mode == RSASignaturePaddingMode.Pkcs1) + { + Debug.Assert(padding == RSASignaturePadding.Pkcs1); + } + else if (padding.Mode == RSASignaturePaddingMode.Pss) + { + Debug.Assert(padding == RSASignaturePadding.Pss); + } + else + { + throw PaddingModeNotSupported(); + } + } + private static Exception PaddingModeNotSupported() => new CryptographicException(SR.Cryptography_InvalidPaddingMode); diff --git a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs index f7aa5b4204b31..9538db1414322 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs @@ -39,7 +39,7 @@ internal sealed class RsaPaddingProcessor private static readonly byte[] s_digestInfoSha512 = { - 0x30, 0x41, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, }; @@ -74,7 +74,7 @@ internal static RsaPaddingProcessor OpenProcessor(HashAlgorithmName hashAlgorith { return s_lookup.GetOrAdd( hashAlgorithmName, - alg => + static hashAlgorithmName => { using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName)) { diff --git a/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs b/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs index f218e5c42997e..5fd1509e7bd89 100644 --- a/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs +++ b/src/libraries/Common/src/System/Threading/Tasks/TaskCompletionSourceWithCancellation.cs @@ -8,7 +8,7 @@ namespace System.Threading.Tasks /// s contain the relevant , /// while also avoiding unnecessary allocations for closure captures. /// - internal class TaskCompletionSourceWithCancellation : TaskCompletionSource + internal sealed class TaskCompletionSourceWithCancellation : TaskCompletionSource { public TaskCompletionSourceWithCancellation() : base(TaskCreationOptions.RunContinuationsAsynchronously) { diff --git a/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs b/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs new file mode 100644 index 0000000000000..7b67a1338e5a6 --- /dev/null +++ b/src/libraries/Common/tests/SingleFileTestRunner/SingleFileTestRunner.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Threading.Tasks; +using System.Xml.Linq; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +public class SingleFileTestRunner : XunitTestFramework +{ + private SingleFileTestRunner(IMessageSink messageSink) + : base(messageSink) { } + + public static int Main(string[] args) + { + var asm = typeof(SingleFileTestRunner).Assembly; + Console.WriteLine("Running assembly:" + asm.FullName); + + var diagnosticSink = new ConsoleDiagnosticMessageSink(); + var testsFinished = new TaskCompletionSource(); + var testSink = new TestMessageSink(); + var summarySink = new DelegatingExecutionSummarySink(testSink, + () => false, + (completed, summary) => Console.WriteLine($"Tests run: {summary.Total}, Errors: {summary.Errors}, Failures: {summary.Failed}, Skipped: {summary.Skipped}. Time: {TimeSpan.FromSeconds((double)summary.Time).TotalSeconds}s")); + var resultsXmlAssembly = new XElement("assembly"); + var resultsSink = new DelegatingXmlCreationSink(summarySink, resultsXmlAssembly); + + testSink.Execution.TestSkippedEvent += args => { Console.WriteLine($"[SKIP] {args.Message.Test.DisplayName}"); }; + testSink.Execution.TestFailedEvent += args => { Console.WriteLine($"[FAIL] {args.Message.Test.DisplayName}{Environment.NewLine}{Xunit.ExceptionUtility.CombineMessages(args.Message)}{Environment.NewLine}{Xunit.ExceptionUtility.CombineStackTraces(args.Message)}"); }; + + testSink.Execution.TestAssemblyFinishedEvent += args => + { + Console.WriteLine($"Finished {args.Message.TestAssembly.Assembly}{Environment.NewLine}"); + testsFinished.SetResult(); + }; + + var xunitTestFx = new SingleFileTestRunner(diagnosticSink); + var asmInfo = Reflector.Wrap(asm); + var asmName = asm.GetName(); + + var discoverySink = new TestDiscoverySink(); + var discoverer = xunitTestFx.CreateDiscoverer(asmInfo); + discoverer.Find(false, discoverySink, TestFrameworkOptions.ForDiscovery()); + discoverySink.Finished.WaitOne(); + XunitFilters filters = new XunitFilters(); + filters.ExcludedTraits.Add("category", new List { "failing" }); + var filteredTestCases = discoverySink.TestCases.Where(filters.Filter).ToList(); + var executor = xunitTestFx.CreateExecutor(asmName); + executor.RunTests(filteredTestCases, resultsSink, TestFrameworkOptions.ForExecution()); + + resultsSink.Finished.WaitOne(); + + var failed = resultsSink.ExecutionSummary.Failed > 0 || resultsSink.ExecutionSummary.Errors > 0; + return failed ? 1 : 0; + } +} + +internal class ConsoleDiagnosticMessageSink : IMessageSink +{ + public bool OnMessage(IMessageSinkMessage message) + { + if (message is IDiagnosticMessage diagnosticMessage) + { + return true; + } + return false; + } +} diff --git a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs index 69ba465eab5e1..b6ba47288f47e 100644 --- a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs +++ b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs @@ -2240,7 +2240,7 @@ public virtual async Task ReadWrite_CustomMemoryManager_Success(bool useAsync) Assert.Equal(1024, writeBuffer.Memory.Length); Assert.Equal(writeBuffer.Memory.Length, readBuffer.Memory.Length); - new Random().NextBytes(writeBuffer.Memory.Span); + Random.Shared.NextBytes(writeBuffer.Memory.Span); readBuffer.Memory.Span.Clear(); Task write = useAsync ? diff --git a/src/libraries/Common/tests/System/Collections/ICollection.Generic.Tests.cs b/src/libraries/Common/tests/System/Collections/ICollection.Generic.Tests.cs index bd70ca57a9f9e..bc528e67c0a7e 100644 --- a/src/libraries/Common/tests/System/Collections/ICollection.Generic.Tests.cs +++ b/src/libraries/Common/tests/System/Collections/ICollection.Generic.Tests.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; using Xunit; namespace System.Collections.Tests @@ -334,6 +336,46 @@ public void ICollection_Generic_Clear_Repeatedly(int count) } } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + [InlineData(false)] + [InlineData(true)] + public void ICollection_Generic_Remove_ReferenceRemovedFromCollection(bool useRemove) + { + if (typeof(T).IsValueType || IsReadOnly || AddRemoveClear_ThrowsNotSupported) + { + return; + } + + ICollection collection = GenericICollectionFactory(); + + WeakReference wr = PopulateAndRemove(collection, useRemove); + Assert.True(SpinWait.SpinUntil(() => + { + GC.Collect(); + return !wr.TryGetTarget(out _); + }, 30_000)); + GC.KeepAlive(collection); + + [MethodImpl(MethodImplOptions.NoInlining)] + WeakReference PopulateAndRemove(ICollection collection, bool useRemove) + { + AddToCollection(collection, 1); + T value = collection.First(); + + if (useRemove) + { + Assert.True(collection.Remove(value)); + } + else + { + collection.Clear(); + Assert.Equal(0, collection.Count); + } + + return new WeakReference(value); + } + } + #endregion #region Contains diff --git a/src/libraries/Common/tests/System/Collections/IDictionary.Generic.Tests.cs b/src/libraries/Common/tests/System/Collections/IDictionary.Generic.Tests.cs index 204fe7277d73f..d701197d848b6 100644 --- a/src/libraries/Common/tests/System/Collections/IDictionary.Generic.Tests.cs +++ b/src/libraries/Common/tests/System/Collections/IDictionary.Generic.Tests.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; using Xunit; namespace System.Collections.Tests @@ -817,6 +819,50 @@ public void IDictionary_Generic_RemoveKey_DefaultKeyContainedInDictionary(int co } } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + [InlineData(false)] + [InlineData(true)] + public void IDictionary_Generic_Remove_ReferenceRemovedFromCollection(bool useRemove) + { + if (IsReadOnly || AddRemoveClear_ThrowsNotSupported) + { + return; + } + + IDictionary dictionary = GenericIDictionaryFactory(); + + // If TKey or TValue is a value type, the test will be meaningless for that part of it, + // but it will still pass. + KeyValuePair, WeakReference> wr = PopulateAndRemove(dictionary, useRemove); + Assert.True(SpinWait.SpinUntil(() => + { + GC.Collect(); + return !wr.Key.TryGetTarget(out _) && !wr.Value.TryGetTarget(out _); + }, 30_000)); + + GC.KeepAlive(dictionary); + + [MethodImpl(MethodImplOptions.NoInlining)] + KeyValuePair, WeakReference> PopulateAndRemove(IDictionary collection, bool useRemove) + { + AddToCollection(collection, 1); + KeyValuePair item = collection.First(); + + if (useRemove) + { + Assert.True(collection.Remove(item)); + } + else + { + collection.Clear(); + Assert.Equal(0, collection.Count); + } + + return new KeyValuePair, WeakReference>( + new WeakReference(item.Key), new WeakReference(item.Value)); + } + } + #endregion #region TryGetValue diff --git a/src/libraries/Common/tests/System/Collections/IGenericSharedAPI.Tests.cs b/src/libraries/Common/tests/System/Collections/IGenericSharedAPI.Tests.cs index df89d266e6e68..22e81af5d6298 100644 --- a/src/libraries/Common/tests/System/Collections/IGenericSharedAPI.Tests.cs +++ b/src/libraries/Common/tests/System/Collections/IGenericSharedAPI.Tests.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; using Xunit; namespace System.Collections.Tests @@ -269,6 +271,46 @@ public void IGenericSharedAPI_Clear(int count) } } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + [InlineData(false)] + [InlineData(true)] + public void IGenericSharedAPI_ClearOrRemove_ReferenceRemovedFromCollection(bool useRemove) + { + if (typeof(T).IsValueType || IsReadOnly) + { + return; + } + + IEnumerable collection = GenericIEnumerableFactory(); + + WeakReference wr = PopulateAndRemove(collection, useRemove); + Assert.True(SpinWait.SpinUntil(() => + { + GC.Collect(); + return !wr.TryGetTarget(out _); + }, 30_000)); + GC.KeepAlive(collection); + + [MethodImpl(MethodImplOptions.NoInlining)] + WeakReference PopulateAndRemove(IEnumerable collection, bool useRemove) + { + AddToCollection(collection, 1); + T value = collection.First(); + + if (useRemove) + { + Assert.True(Remove(collection)); + } + else + { + Clear(collection); + } + Assert.Equal(0, Count(collection)); + + return new WeakReference(value); + } + } + #endregion #region Contains diff --git a/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs b/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs index 1d788acbb6913..27f81002f5f1c 100644 --- a/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs +++ b/src/libraries/Common/tests/System/Net/Configuration.Certificates.cs @@ -16,8 +16,8 @@ public static partial class Configuration { public static partial class Certificates { - private const string CertificatePassword = "testcertificate"; - private const string TestDataFolder = "TestData"; + private const string CertificatePassword = "PLACEHOLDER"; + private const string TestDataFolder = "TestDataCertificates"; private const int MutexTimeoutMs = 120_000; private static readonly X509Certificate2 s_serverCertificate; diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs index 3dd6e7ed9ba7a..4df4100a8ed41 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Authentication.cs @@ -33,7 +33,6 @@ public abstract class HttpClientHandler_Authentication_Test : HttpClientHandlerT private async Task CreateAndValidateRequest(HttpClientHandler handler, Uri url, HttpStatusCode expectedStatusCode, ICredentials credentials) { handler.Credentials = credentials; - using (HttpClient client = CreateHttpClient(handler)) using (HttpResponseMessage response = await client.GetAsync(url)) { @@ -94,6 +93,13 @@ public static IEnumerable Authentication_SocketsHttpHandler_TestData() yield return new object[] { $"Digest realm=\"testrealm\", algorithm=sha-256, nonce=\"testnonce\"", true }; yield return new object[] { $"Digest realm=\"testrealm\", algorithm=sha-256-SESS, nonce=\"testnonce\", qop=\"auth\"", true }; } + + // Add tests cases for empty values that are not mandatory + if (!IsWinHttpHandler) + { + yield return new object[] { "Digest realm=\"testrealm\",nonce=\"6afd170437eb5144258b308f7c491d96\",opaque=\"\",stale=FALSE,algorithm=MD5,qop=\"auth\"", true }; + yield return new object[] { "Digest realm=\"testrealm\", domain=\"\", nonce=\"NA42+vpOFQd1GwCyVRZuhhy+jDn4BMRl\", algorithm=MD5, qop=\"auth\", stale=false", true }; + } } [Theory] diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs index 5db0b7b25b810..31af521cee924 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.DefaultProxyCredentials.cs @@ -80,8 +80,6 @@ await server.AcceptConnectionSendResponseAndCloseAsync( } #if !WINHTTPHANDLER_TEST - [ActiveIssue("https://github.com/dotnet/runtime/issues/31380")] - [OuterLoop("Uses external server")] [PlatformSpecific(TestPlatforms.AnyUnix)] // The default proxy is resolved via WinINet on Windows. [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(false)] @@ -92,21 +90,12 @@ public async Task ProxySetViaEnvironmentVariable_DefaultProxyCredentialsUsed(boo const string ExpectedPassword = "rightpassword"; LoopbackServer.Options options = new LoopbackServer.Options { IsProxy = true, Username = ExpectedUsername, Password = ExpectedPassword }; - await LoopbackServer.CreateServerAsync(async (proxyServer, proxyUri) => + await LoopbackServer.CreateClientAndServerAsync(uri => Task.Run(() => { - // SocketsHttpHandler can read a default proxy from the http_proxy environment variable. Ensure that when it does, - // our default proxy credentials are used. To avoid messing up anything else in this process, we run the - // test in another process. var psi = new ProcessStartInfo(); - Task> proxyTask = null; - - if (useProxy) - { - proxyTask = proxyServer.AcceptConnectionPerformAuthenticationAndCloseAsync("Proxy-Authenticate: Basic realm=\"NetCore\"\r\n"); - psi.Environment.Add("http_proxy", $"http://{proxyUri.Host}:{proxyUri.Port}"); - } + psi.Environment.Add("http_proxy", $"http://{uri.Host}:{uri.Port}"); - RemoteExecutor.Invoke(async (useProxyString, useVersionString) => + RemoteExecutor.Invoke(async (useProxyString, useVersionString, uriString) => { using (HttpClientHandler handler = CreateHttpClientHandler(useVersionString)) using (HttpClient client = CreateHttpClient(handler, useVersionString)) @@ -115,16 +104,46 @@ await LoopbackServer.CreateServerAsync(async (proxyServer, proxyUri) => handler.DefaultProxyCredentials = creds; handler.UseProxy = bool.Parse(useProxyString); - HttpResponseMessage response = await client.GetAsync(Configuration.Http.RemoteEchoServer); + HttpResponseMessage response = await client.GetAsync(uriString); // Correctness of user and password is done in server part. Assert.True(response.StatusCode == HttpStatusCode.OK); - } - }, useProxy.ToString(), UseVersion.ToString(), new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + }; + }, useProxy.ToString(), UseVersion.ToString(), + // If proxy is used , the url does not matter. We set it to be different to avoid confusion. + useProxy ? Configuration.Http.RemoteEchoServer.ToString() : uri.ToString(), + new RemoteInvokeOptions { StartInfo = psi }).Dispose(); + }), + server => server.AcceptConnectionAsync(async connection => + { + const string headerName = "Proxy-Authorization"; + List lines = await connection.ReadRequestHeaderAsync().ConfigureAwait(false); + + // First request should not have proxy credentials in either case. + for (int i = 1; i < lines.Count; i++) + { + Assert.False(lines[i].StartsWith(headerName)); + } + if (useProxy) { - await proxyTask; + // Reject request and wait for authenticated one. + await connection.SendResponseAsync(HttpStatusCode.ProxyAuthenticationRequired, "Proxy-Authenticate: Basic realm=\"NetCore\"\r\n").ConfigureAwait(false); + + lines = await connection.ReadRequestHeaderAsync().ConfigureAwait(false); + bool valid = false; + for (int i = 1; i < lines.Count; i++) + { + if (lines[i].StartsWith(headerName)) + { + valid = LoopbackServer.IsBasicAuthTokenValid(lines[i], options); + } + } + + Assert.True(valid); } - }, options); + + await connection.SendResponseAsync(HttpStatusCode.OK).ConfigureAwait(false); + })); } #endif diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs index 2eb655ee7779f..208ac1ad2916c 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.Proxy.cs @@ -280,9 +280,8 @@ public async Task AuthenticatedProxiedRequest_GetAsyncWithNoCreds_ProxyAuthentic } } - [OuterLoop("Uses external server")] [Fact] - public async Task AuthenticatedProxyTunnelRequest_PostAsyncWithNoCreds_ProxyAuthenticationRequiredStatusCode() + public async Task AuthenticatedProxyTunnelRequest_PostAsyncWithNoCreds_Throws() { if (IsWinHttpHandler) { @@ -300,14 +299,13 @@ public async Task AuthenticatedProxyTunnelRequest_PostAsyncWithNoCreds_ProxyAuth handler.Proxy = new WebProxy(proxyServer.Uri); handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; using (HttpClient client = CreateHttpClient(handler)) - using (HttpResponseMessage response = await client.PostAsync(Configuration.Http.SecureRemoteEchoServer, new StringContent(content))) { - Assert.Equal(HttpStatusCode.ProxyAuthenticationRequired, response.StatusCode); + HttpRequestException e = await Assert.ThrowsAnyAsync(async () => await client.PostAsync("https://nosuchhost.invalid", new StringContent(content))); + Assert.Contains("407", e.Message); } } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/18258")] public async Task Proxy_SslProxyUnsupported_Throws() { diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 1e3089f328925..ac49da68e4e6b 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -306,7 +306,7 @@ public async Task GetAsync_ServerNeedsNonStandardAuthAndSetCredential_StatusCode await LoopbackServerFactory.CreateServerAsync(async (server, url) => { HttpClientHandler handler = CreateHttpClientHandler(); - handler.Credentials = new NetworkCredential("unused", "unused"); + handler.Credentials = new NetworkCredential("unused", "PLACEHOLDER"); using (HttpClient client = CreateHttpClient(handler)) { Task getResponseTask = client.GetAsync(url); @@ -630,7 +630,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => $"Accept-Patch:{fold} text/example;charset=utf-8{newline}" + $"Accept-Ranges:{fold} bytes{newline}" + $"Age: {fold}12{newline}" + - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy authorization.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy authorization.")] $"Authorization: Bearer 63123a47139a49829bcd8d03005ca9d7{newline}" + $"Allow: {fold}GET, HEAD{newline}" + $"Alt-Svc:{fold} http/1.1=\"http2.example.com:8001\"; ma=7200{newline}" + diff --git a/src/libraries/Common/tests/System/Net/Http/PostScenarioTest.cs b/src/libraries/Common/tests/System/Net/Http/PostScenarioTest.cs index f547766ed5d20..6fc23e8f27d5e 100644 --- a/src/libraries/Common/tests/System/Net/Http/PostScenarioTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/PostScenarioTest.cs @@ -23,7 +23,7 @@ public abstract class PostScenarioTest : HttpClientHandlerTestBase { private const string ExpectedContent = "Test contest"; private const string UserName = "user1"; - private const string Password = "password1"; + private const string Password = "PLACEHOLDER"; public PostScenarioTest(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_certificates.ps1 b/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_certificates.ps1 index a41139dff18a1..3a52a66720e43 100644 --- a/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_certificates.ps1 +++ b/src/libraries/Common/tests/System/Net/Prerequisites/Deployment/setup_certificates.ps1 @@ -5,15 +5,15 @@ # Certificate configuration -$script:testDataUri = "https://github.com/dotnet/runtime-assets/archive/master.zip" +$script:testDataUri = "https://github.com/dotnet/runtime-assets/archive/main.zip" $script:testData = "runtime-assets" -$script:certificatePath = "$($script:testData)\runtime-assets-master\System.Net.TestData" +$script:certificatePath = "$($script:testData)\src\System.Net.TestData\TestDataCertificates" $script:clientPrivateKeyPath = Join-Path $script:certificatePath "testclient1_at_contoso.com.pfx" -$script:clientPrivateKeyPassword = "testcertificate" +$script:clientPrivateKeyPassword = "PLACEHOLDER" $script:serverPrivateKeyPath = Join-Path $script:certificatePath "contoso.com.pfx" -$script:serverPrivateKeyPassword = "testcertificate" +$script:serverPrivateKeyPassword = "PLACEHOLDER" Function GetFullPath($relativePath) { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs index 2a86ad4f4313a..885c9a075261f 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs @@ -27,7 +27,7 @@ public static void UseAfterDispose_ImportedKey() private static void UseAfterDispose(bool importKey) { - DSA key = importKey ? DSAFactory.Create(DSATestData.GetDSA1024Params()) : DSAFactory.Create(512); + DSA key = importKey ? DSAFactory.Create(DSATestData.GetDSA1024Params()) : DSAFactory.Create(1024); byte[] pkcs8Private; byte[] pkcs8EncryptedPrivate; diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs index 0d6f24425ce14..3c2b5974684f5 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs @@ -9,6 +9,7 @@ namespace System.Security.Cryptography.Dsa.Tests public partial class DSAKeyGeneration { public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration; + public static bool HasSecondMinSize { get; } = GetHasSecondMinSize(); [Fact] public static void VerifyDefaultKeySize_Fips186_2() @@ -28,7 +29,7 @@ public static void GenerateMinKey() GenerateKey(dsa => GetMin(dsa.LegalKeySizes)); } - [ConditionalFact(nameof(SupportsKeyGeneration))] + [ConditionalFact(nameof(SupportsKeyGeneration), nameof(HasSecondMinSize))] public static void GenerateSecondMinKey() { GenerateKey(dsa => GetSecondMin(dsa.LegalKeySizes)); @@ -118,5 +119,13 @@ private static int GetSecondMin(KeySizes[] keySizes) return secondMin; } + + private static bool GetHasSecondMinSize() + { + using (DSA dsa = DSAFactory.Create()) + { + return GetSecondMin(dsa.LegalKeySizes) != int.MaxValue; + } + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs index 559fa81fc248e..b1a8c9e48fa26 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs @@ -163,7 +163,21 @@ private void UseAfterDispose(bool importKey) Assert.Throws( () => { - key.KeySize = 576; + try + { + key.KeySize = 576; + } + catch (CryptographicException) + { + // DSACryptoServiceProvider on Android only supports 1024 and does an early check for legal + // key sizes, since it is more restrictive than the wrapped implementation. It will throw + // CryptographicException. SignData should still throw ObjectDisposedException. + if (!PlatformDetection.IsAndroid) + { + throw; + } + } + SignData(key, data, HashAlgorithmName.SHA1); }); } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs index 5b6b786fcab77..ca0f9967c13e4 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.LimitedPrivate.cs @@ -134,7 +134,7 @@ public void ReadWriteNistP256ExplicitPkcs8_LimitedPrivate() AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBCcwJQIBAQQgcKEsLbFoRe1W /2jPwhpHKz8E19aFG/Y0ny19WzRSs4o=", EccTestData.GetNistP256ReferenceKeyExplicit(), - SupportsExplicitCurves); + SupportsExplicitCurves && CanDeriveNewPublicKey); } [Fact] @@ -177,7 +177,7 @@ public void ReadWriteBrainpoolKey1Pkcs8_LimitedPrivate() MDYCAQAwFAYHKoZIzj0CAQYJKyQDAwIIAQEBBBswGQIBAQQUxdlEVH3hFdsliNxv 3ro7Rz6cTZY=", EccTestData.BrainpoolP160r1Key1, - SupportsBrainpool); + SupportsBrainpool && CanDeriveNewPublicKey); } [Fact] @@ -195,7 +195,7 @@ public void ReadWriteBrainpoolKey1EncryptedPkcs8_LimitedPrivate() HashAlgorithmName.SHA384, 4096), EccTestData.BrainpoolP160r1Key1, - SupportsBrainpool); + SupportsBrainpool && CanDeriveNewPublicKey); } [Fact] @@ -204,7 +204,7 @@ public void ReadWriteSect163k1Key1ECPrivateKey_LimitedPrivate() ReadWriteBase64ECPrivateKey( "MCMCAQEEFQPBmVrfrowFGNwT3+YwS7AQF+akEqAHBgUrgQQAAQ==", EccTestData.Sect163k1Key1, - SupportsSect163k1 && CanDeriveNewPublicKey); + SupportsSect163k1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -215,7 +215,7 @@ public void ReadWriteSect163k1Key1Pkcs8_LimitedPrivate() MDMCAQAwEAYHKoZIzj0CAQYFK4EEAAEEHDAaAgEBBBUDwZla366MBRjcE9/mMEuw EBfmpBI=", EccTestData.Sect163k1Key1, - SupportsSect163k1); + SupportsSect163k1 && CanDeriveNewPublicKey); } [Fact] @@ -229,7 +229,7 @@ public void ReadWriteSect163k1Key1ExplicitECPrivateKey_LimitedPrivate() XlyU7ugCiQcPsF04/1gyHy6ABTbVOMzao9kCFQQAAAAAAAAAAAACAQii4MwNmfil 7wIBAg==", EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1 && CanDeriveNewPublicKey); + SupportsSect163k1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -243,7 +243,7 @@ public void ReadWriteSect163k1Key1ExplicitPkcs8_LimitedPrivate() Mh8ugAU21TjM2qPZAhUEAAAAAAAAAAAAAgEIouDMDZn4pe8CAQIEHDAaAgEBBBUD wZla366MBRjcE9/mMEuwEBfmpBI=", EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1); + SupportsSect163k1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -261,7 +261,7 @@ public void ReadWriteSect163k1Key1EncryptedPkcs8_LimitedPrivate() HashAlgorithmName.SHA256, 7), EccTestData.Sect163k1Key1, - SupportsSect163k1); + SupportsSect163k1 && CanDeriveNewPublicKey); } [Fact] @@ -282,7 +282,7 @@ public void ReadWriteSect163k1Key1ExplicitEncryptedPkcs8_LimitedPrivate() HashAlgorithmName.SHA256, 7), EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1); + SupportsSect163k1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -307,7 +307,7 @@ public void ReadWriteC2pnb163v1ExplicitECPrivateKey_LimitedPrivate() VhUXVAQrBAevaZiVRhA9eTKfzD10iA8zu+gDywHsIyEbWWat6h0/h/fqWEiu8LfK nwIVBAAAAAAAAAAAAAHmD8iCHMdNrq/BAgEC", EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1 && CanDeriveNewPublicKey); + SupportsC2pnb163v1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -321,7 +321,7 @@ public void ReadWriteC2pnb163v1ExplicitPkcs8_LimitedPrivate() PXkyn8w9dIgPM7voA8sB7CMhG1lmreodP4f36lhIrvC3yp8CFQQAAAAAAAAAAAAB 5g/IghzHTa6vwQIBAgQcMBoCAQEEFQD00koUBxIvRFlnvh2TwAk6ZTZ5hg==", EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1); + SupportsC2pnb163v1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -342,7 +342,7 @@ public void ReadWriteC2pnb163v1ExplicitEncryptedPkcs8_LimitedPrivate() HashAlgorithmName.SHA256, 7), EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1); + SupportsC2pnb163v1Explicit && CanDeriveNewPublicKey); } [Fact] @@ -353,7 +353,7 @@ public void ReadWriteSect283k1Key1Pkcs8_LimitedPrivate() MEICAQAwEAYHKoZIzj0CAQYFK4EEABAEKzApAgEBBCQAtPGuHn/c1LDoIFPAipCI UrJiMebAFnD8xsPqLF0/7UDt8Dc=", EccTestData.Sect283k1Key1, - SupportsSect283k1); + SupportsSect283k1 && CanDeriveNewPublicKey); } [Fact] @@ -371,7 +371,7 @@ public void ReadWriteSect283k1Key1EncryptedPkcs8_LimitedPrivate() HashAlgorithmName.SHA384, 4096), EccTestData.Sect283k1Key1, - SupportsSect283k1); + SupportsSect283k1 && CanDeriveNewPublicKey); } [Fact] @@ -391,7 +391,7 @@ public void ReadWriteC2pnb163v1Pkcs8_LimitedPrivate() MDYCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAAEEHDAaAgEBBBUA9NJKFAcSL0RZZ74d k8AJOmU2eYY=", EccTestData.C2pnb163v1Key1, - SupportsC2pnb163v1); + SupportsC2pnb163v1 && CanDeriveNewPublicKey); } [Fact] @@ -409,7 +409,7 @@ public void ReadWriteC2pnb163v1EncryptedPkcs8_LimitedPrivate() HashAlgorithmName.SHA512, 1024), EccTestData.C2pnb163v1Key1, - SupportsC2pnb163v1); + SupportsC2pnb163v1 && CanDeriveNewPublicKey); } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs index 6d64fd3cc3c45..66b80c93380e9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyFileTests.cs @@ -20,17 +20,22 @@ public abstract partial class ECKeyFileTests where T : AsymmetricAlgorithm protected abstract void Exercise(T key); protected virtual Func PublicKeyWriteArrayFunc { get; } = null; protected virtual WriteKeyToSpanFunc PublicKeyWriteSpanFunc { get; } = null; + + // This would need to be virtualized if there was ever a platform that + // allowed explicit in ECDH or ECDSA but not the other. + public static bool SupportsExplicitCurves { get; } = EcDiffieHellman.Tests.ECDiffieHellmanFactory.ExplicitCurvesSupported; + + public static bool CanDeriveNewPublicKey { get; } = EcDiffieHellman.Tests.ECDiffieHellmanFactory.CanDeriveNewPublicKey; public static bool SupportsBrainpool { get; } = IsCurveSupported(ECCurve.NamedCurves.brainpoolP160r1.Oid); public static bool SupportsSect163k1 { get; } = IsCurveSupported(EccTestData.Sect163k1Key1.Curve.Oid); public static bool SupportsSect283k1 { get; } = IsCurveSupported(EccTestData.Sect283k1Key1.Curve.Oid); public static bool SupportsC2pnb163v1 { get; } = IsCurveSupported(EccTestData.C2pnb163v1Key1.Curve.Oid); - // This would need to be virtualized if there was ever a platform that - // allowed explicit in ECDH or ECDSA but not the other. - public static bool SupportsExplicitCurves { get; } = EcDiffieHellman.Tests.ECDiffieHellmanFactory.ExplicitCurvesSupported; - - public static bool CanDeriveNewPublicKey { get; } = EcDiffieHellman.Tests.ECDiffieHellmanFactory.CanDeriveNewPublicKey; + // Some platforms support explicitly specifying these curves, but do not support specifying them by name. + public static bool ExplicitNamedSameSupport { get; } = !PlatformDetection.IsAndroid; + public static bool SupportsSect163k1Explicit { get; } = SupportsSect163k1 || (!ExplicitNamedSameSupport && SupportsExplicitCurves); + public static bool SupportsC2pnb163v1Explicit { get; } = SupportsC2pnb163v1 || (!ExplicitNamedSameSupport && SupportsExplicitCurves); private static bool IsCurveSupported(Oid oid) { @@ -182,7 +187,7 @@ public void ReadNistP521EncryptedPkcs8_Pbes2_Aes128_Sha384() public void ReadNistP521EncryptedPkcs8_Pbes2_Aes128_Sha384_PasswordBytes() { // PBES2, PBKDF2 (SHA384), AES128 - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test key.")] const string base64 = @" MIIBXTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI/JyXWyp/t3kCAggA MAwGCCqGSIb3DQIKBQAwHQYJYIZIAWUDBAECBBA3H8mbFK5afB5GzIemCCQkBIIB @@ -421,7 +426,7 @@ public void ReadWriteSect163k1Key1ExplicitECPrivateKey() 4MwNmfil7wIBAqEuAywABAYXnjcZzIElQ1/mRYnV/KbcGIdVHQeI/rti/8kkjYs5 iv4+C1w8ArP+Nw==", EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1); + SupportsSect163k1Explicit); } [Fact] @@ -435,7 +440,7 @@ public void ReadWriteSect163k1Key1ExplicitPkcs8() SgIBAQQVA8GZWt+ujAUY3BPf5jBLsBAX5qQSoS4DLAAEBheeNxnMgSVDX+ZFidX8 ptwYh1UdB4j+u2L/ySSNizmK/j4LXDwCs/43", EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1); + SupportsSect163k1Explicit); } [Fact] @@ -455,7 +460,7 @@ public void ReadWriteSect163k1Key1ExplicitEncryptedPkcs8() HashAlgorithmName.SHA256, 12), EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1); + SupportsSect163k1Explicit); } [Fact] @@ -468,7 +473,7 @@ public void ReadWriteSect163k1Key1ExplicitSubjectPublicKeyInfo() XTj/WDIfLoAFNtU4zNqj2QIVBAAAAAAAAAAAAAIBCKLgzA2Z+KXvAgECAywABAYX njcZzIElQ1/mRYnV/KbcGIdVHQeI/rti/8kkjYs5iv4+C1w8ArP+Nw==", EccTestData.Sect163k1Key1Explicit, - SupportsSect163k1); + SupportsSect163k1Explicit); } [Fact] @@ -589,7 +594,7 @@ public void ReadWriteC2pnb163v1ExplicitECPrivateKey() nwIVBAAAAAAAAAAAAAHmD8iCHMdNrq/BAgECoS4DLAAEAhEnLxxVgkJoiOkb1pJX dJQjIkiqBCcIMPehAJrWcKiN6SvVkkjMgTtF", EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1); + SupportsC2pnb163v1Explicit); } [Fact] @@ -604,7 +609,7 @@ public void ReadWriteC2pnb163v1ExplicitPkcs8() 5g/IghzHTa6vwQIBAgRMMEoCAQEEFQD00koUBxIvRFlnvh2TwAk6ZTZ5hqEuAywA BAIRJy8cVYJCaIjpG9aSV3SUIyJIqgQnCDD3oQCa1nCojekr1ZJIzIE7RQ==", EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1); + SupportsC2pnb163v1Explicit); } [Fact] @@ -625,7 +630,7 @@ public void ReadWriteC2pnb163v1ExplicitEncryptedPkcs8() HashAlgorithmName.SHA512, 1024), EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1); + SupportsC2pnb163v1Explicit); } [Fact] @@ -640,7 +645,7 @@ public void ReadWriteC2pnb163v1ExplicitSubjectPublicKeyInfo() HMdNrq/BAgECAywABAIRJy8cVYJCaIjpG9aSV3SUIyJIqgQnCDD3oQCa1nCojekr 1ZJIzIE7RQ==", EccTestData.C2pnb163v1Key1Explicit, - SupportsC2pnb163v1); + SupportsC2pnb163v1Explicit); } [Fact] diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs index 01674560c7028..4992fc7ef86cd 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDiffieHellman/ECDiffieHellmanTests.ImportExport.cs @@ -154,6 +154,7 @@ public static void TestKeySizeCreateKey() } [Fact] + [PlatformSpecific(~TestPlatforms.Android)] // Android does not validate curve parameters public static void TestExplicitImportValidationNegative() { if (!ECDiffieHellmanFactory.ExplicitCurvesSupported) diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs index 89144c6fc46d4..777b217be37b1 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs @@ -188,6 +188,7 @@ public static void TestKeySizeCreateKey() } [ConditionalFact(nameof(ECExplicitCurvesSupported))] + [PlatformSpecific(~TestPlatforms.Android)] // Android does not validate curve parameters public static void TestExplicitImportValidationNegative() { unchecked diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs index 28b2f4dcd3cc3..92d5b21e10433 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyFileTests.cs @@ -765,7 +765,7 @@ public static void ReadPbes2Rc2EncryptedDiminishedDP() public static void ReadPbes2Rc2EncryptedDiminishedDP_PasswordBytes() { // PBES2: PBKDF2 + RC2-128 - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test key.")] const string base64 = @" MIIBrjBIBgkqhkiG9w0BBQ0wOzAeBgkqhkiG9w0BBQwwEQQIKZEFT76zCFECAggA AgEQMBkGCCqGSIb3DQMCMA0CAToECE1Yyzk6++IPBIIBYDDvaYLkET8eudcYLQMf @@ -791,7 +791,7 @@ public static void ReadPbes2Rc2EncryptedDiminishedDP_PasswordBytes() [Fact] public static void ReadEncryptedDiminishedDP_EmptyPassword() { - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test key.")] const string base64 = @" MIIBgTAbBgkqhkiG9w0BBQMwDgQIJtjMez/9Gg4CAggABIIBYElq9UOOphEPU3b7 G/mV8M1uEdjigidMPih3b9IIJhrjMAEix2IjS+brFL7KRQgucpZZoaFU1utvkUHg @@ -816,7 +816,7 @@ public static void ReadEncryptedDiminishedDP_EmptyPassword() [Fact] public static void ReadEncryptedDiminishedDP_EmptyPasswordBytes() { - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test key.")] const string base64 = @" MIIBgTAbBgkqhkiG9w0BBQMwDgQIJtjMez/9Gg4CAggABIIBYElq9UOOphEPU3b7 G/mV8M1uEdjigidMPih3b9IIJhrjMAEix2IjS+brFL7KRQgucpZZoaFU1utvkUHg diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs index 4af160ae005a2..a0ef210d47e80 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAXml.cs @@ -1340,7 +1340,7 @@ public static void FromInvalidXml() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/29515", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/29515", TestPlatforms.OSX | TestPlatforms.Android)] public static void FromNonsenseXml() { // This is DiminishedDPParameters XML, but with a P that is way too long. diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs index 0ff2bde7f066d..3685c0d6f03a7 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/CertificateAuthority.cs @@ -517,8 +517,9 @@ singleExtensions [1] EXPLICIT Extensions OPTIONAL } } else if (status == CertStatus.Revoked) { + // Android does not support all precisions for seconds - just omit fractional seconds for testing on Android writer.PushSequence(s_context1); - writer.WriteGeneralizedTime(revokedTime); + writer.WriteGeneralizedTime(revokedTime, omitFractionalSeconds: OperatingSystem.IsAndroid()); writer.PopSequence(s_context1); } else diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs index 67b5ad4a2a455..4e9e5e20612a9 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs @@ -48,17 +48,23 @@ internal void AddCertificateAuthority(CertificateAuthority authority) { if (authority.AiaHttpUri != null && authority.AiaHttpUri.StartsWith(UriPrefix)) { - _aiaPaths.Add(authority.AiaHttpUri.Substring(UriPrefix.Length - 1), authority); + string path = authority.AiaHttpUri.Substring(UriPrefix.Length - 1); + Trace($"Adding AIA path : {path}"); + _aiaPaths.Add(path, authority); } if (authority.CdpUri != null && authority.CdpUri.StartsWith(UriPrefix)) { - _crlPaths.Add(authority.CdpUri.Substring(UriPrefix.Length - 1), authority); + string path = authority.CdpUri.Substring(UriPrefix.Length - 1); + Trace($"Adding CRL path : {path}"); + _crlPaths.Add(path, authority); } if (authority.OcspUri != null && authority.OcspUri.StartsWith(UriPrefix)) { - _ocspAuthorities.Add((authority.OcspUri.Substring(UriPrefix.Length - 1), authority)); + string path = authority.OcspUri.Substring(UriPrefix.Length - 1); + Trace($"Adding OCSP path : {path}"); + _ocspAuthorities.Add((path, authority)); } } @@ -211,20 +217,18 @@ private void HandleRequest(HttpListenerContext context, ref bool responded) if (url.StartsWith(prefix)) { - if (context.Request.HttpMethod == "GET") + byte[] reqBytes; + if (TryGetOcspRequestBytes(context.Request, prefix, out reqBytes)) { ReadOnlyMemory certId; ReadOnlyMemory nonce; - try { - string base64 = HttpUtility.UrlDecode(url.Substring(prefix.Length + 1)); - byte[] reqBytes = Convert.FromBase64String(base64); DecodeOcspRequest(reqBytes, out certId, out nonce); } - catch (Exception) + catch (Exception e) { - Trace($"OcspRequest Decode failed ({url})"); + Trace($"OcspRequest Decode failed ({url}) - {e}"); context.Response.StatusCode = 400; context.Response.Close(); return; @@ -291,6 +295,36 @@ private static HttpListener OpenListener(out string uriPrefix) } } + private static bool TryGetOcspRequestBytes(HttpListenerRequest request, string prefix, out byte[] requestBytes) + { + requestBytes = null; + try + { + if (request.HttpMethod == "GET") + { + string base64 = HttpUtility.UrlDecode(request.RawUrl.Substring(prefix.Length + 1)); + requestBytes = Convert.FromBase64String(base64); + return true; + } + else if (request.HttpMethod == "POST" && request.ContentType == "application/ocsp-request") + { + using (System.IO.Stream stream = request.InputStream) + { + requestBytes = new byte[request.ContentLength64]; + int read = stream.Read(requestBytes, 0, requestBytes.Length); + System.Diagnostics.Debug.Assert(read == requestBytes.Length); + return true; + } + } + } + catch (Exception e) + { + Trace($"Failed to get OCSP request bytes ({request.RawUrl}) - {e}"); + } + + return false; + } + private static void DecodeOcspRequest( byte[] requestBytes, out ReadOnlyMemory certId, diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index c41a3df0cd5ac..75879136f3291 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -26,7 +26,7 @@ public static partial class PlatformDetection public static bool IsMonoInterpreter => GetIsRunningOnMonoInterpreter(); public static bool IsFreeBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")); public static bool IsNetBSD => RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD")); - public static bool IsAndroid => RuntimeInformation.IsOSPlatform(OSPlatform.Create("Android")); + public static bool IsAndroid => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")); public static bool IsiOS => RuntimeInformation.IsOSPlatform(OSPlatform.Create("IOS")); public static bool IstvOS => RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS")); public static bool IsMacCatalyst => RuntimeInformation.IsOSPlatform(OSPlatform.Create("MACCATALYST")); @@ -85,6 +85,8 @@ public static bool IsDrawingSupported } } + + public static bool IsLineNumbersSupported => true; public static bool IsInContainer => GetIsInContainer(); public static bool SupportsComInterop => IsWindows && IsNotMonoRuntime; // matches definitions in clr.featuredefines.props @@ -187,14 +189,14 @@ public static string GetDistroVersionString() } } - private static readonly Lazy m_isInvariant = new Lazy(GetIsInvariantGlobalization); + private static readonly Lazy m_isInvariant = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("System.Globalization.GlobalizationMode", "Invariant")); - private static bool GetIsInvariantGlobalization() + private static bool GetStaticNonPublicBooleanPropertyValue(string typeName, string propertyName) { - Type globalizationMode = Type.GetType("System.Globalization.GlobalizationMode"); + Type globalizationMode = Type.GetType(typeName); if (globalizationMode != null) { - MethodInfo methodInfo = globalizationMode.GetProperty("Invariant", BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod; + MethodInfo methodInfo = globalizationMode.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Static)?.GetMethod; if (methodInfo != null) { return (bool)methodInfo.Invoke(null, null); @@ -235,6 +237,10 @@ private static Version GetICUVersion() version & 0xFF); } + private static readonly Lazy _net5CompatFileStream = new Lazy(() => GetStaticNonPublicBooleanPropertyValue("System.IO.FileStreamHelpers", "UseNet5CompatStrategy")); + + public static bool IsNet5CompatFileStreamEnabled => _net5CompatFileStream.Value; + private static bool GetIsInContainer() { if (IsWindows) diff --git a/src/libraries/Common/tests/Tests/System/StringTests.cs b/src/libraries/Common/tests/Tests/System/StringTests.cs index 4c79d008e84ff..fcee8ec7d78cc 100644 --- a/src/libraries/Common/tests/Tests/System/StringTests.cs +++ b/src/libraries/Common/tests/Tests/System/StringTests.cs @@ -1821,7 +1821,7 @@ public static void SameSpanEndsWith_Char() [Fact] public static void LengthMismatchEndsWith_Char() { - string value = "456";; + string value = "456"; string s1 = value.Substring(0, 2); string s2 = value.Substring(0, 3); @@ -4496,7 +4496,7 @@ public static void PadRight_NegativeTotalWidth_ThrowsArgumentOutOfRangeException [InlineData("", 0, 0, "")] public static void Remove(string s, int startIndex, int count, string expected) { - if (startIndex + count == s.Length && count != 0) + if (startIndex + count == s.Length) { Assert.Equal(expected, s.Remove(startIndex)); } @@ -4512,8 +4512,8 @@ public static void Remove_Invalid() AssertExtensions.Throws("startIndex", () => s.Remove(-1)); AssertExtensions.Throws("startIndex", () => s.Remove(-1, 0)); - // Start index >= string.Length - AssertExtensions.Throws("startIndex", () => s.Remove(s.Length)); + // Start index > string.Length + AssertExtensions.Throws("startIndex", () => s.Remove(s.Length + 1)); // Count < 0 AssertExtensions.Throws("count", () => s.Remove(0, -1)); diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 7803ca3e6f1c5..f36f83de45246 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -21,7 +21,7 @@ $(RepositoryEngineeringDir)LicenseHeader.txt - + $([System.Text.RegularExpressions.Regex]::Replace('$(TargetFramework)', '(-[^;]+)', '')) diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 86c50d3b24ee7..a04a93dfb0d6b 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -14,10 +14,10 @@ $(PackageOutputPath) $(NoWarn);nullable - $(NoWarn);nullable + $(NoWarn);nullable;CA1052 - $(NoWarn);SYSLIB0003;SYSLIB0004 + $(NoWarn);SYSLIB0003;SYSLIB0004;SYSLIB0015 @@ -116,7 +116,7 @@ - + diff --git a/src/libraries/Microsoft.CSharp/src/ILLink/ILLinkTrim.xml b/src/libraries/Microsoft.CSharp/src/ILLink/ILLink.Descriptors.xml similarity index 100% rename from src/libraries/Microsoft.CSharp/src/ILLink/ILLinkTrim.xml rename to src/libraries/Microsoft.CSharp/src/ILLink/ILLink.Descriptors.xml diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderEquivalence.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderEquivalence.cs index d9dabd2e838e2..ccd91a4f12d8e 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderEquivalence.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/BinderEquivalence.cs @@ -52,7 +52,7 @@ internal static T TryGetExisting(this T binder) return (T)fromCache; } - internal class BinderEqualityComparer : IEqualityComparer + internal sealed class BinderEqualityComparer : IEqualityComparer { public bool Equals(ICSharpBinder x, ICSharpBinder y) => x.IsEquivalentTo(y); public int GetHashCode(ICSharpBinder obj) => obj.GetGetBinderEquivalenceHash(); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs index 72cb677570023..e8d9ec19aedce 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComBinder.cs @@ -282,7 +282,7 @@ private static bool TryGetMetaObjectInvoke(ref DynamicMetaObject instance) /// /// Special binder that indicates special semantics for COM GetMember operation. /// - internal class ComGetMemberBinder : GetMemberBinder + internal sealed class ComGetMemberBinder : GetMemberBinder { private readonly GetMemberBinder _originalBinder; internal bool _canReturnCallables; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs index 45075a2cc1758..c61fc467d991a 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComClassMetaObject.cs @@ -6,7 +6,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ComClassMetaObject : DynamicMetaObject + internal sealed class ComClassMetaObject : DynamicMetaObject { internal ComClassMetaObject(Expression expression, ComTypeClassDesc cls) : base(expression, BindingRestrictions.Empty, cls) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventDesc.cs index bf728a6e7ec1f..b80397a1231fe 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventDesc.cs @@ -5,7 +5,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ComEventDesc + internal sealed class ComEventDesc { public Guid SourceIID; public int Dispid; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventSinksContainer.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventSinksContainer.cs index 98a4d21ce2af3..9c06e6cdf311f 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventSinksContainer.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventSinksContainer.cs @@ -12,7 +12,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop /// This list is usually attached as a custom data for RCW object and /// is finalized whenever RCW is finalized. /// - internal class ComEventSinksContainer : List, IDisposable + internal sealed class ComEventSinksContainer : List, IDisposable { private ComEventSinksContainer() { diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs index 48347611f4085..c6bd8a9c459c3 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComEventsSink.Extended.cs @@ -7,7 +7,7 @@ namespace System.Runtime.InteropServices { - internal partial class ComEventsSink + internal sealed partial class ComEventsSink { private void Initialize(object rcw, Guid iid) { diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs index 47e8f42414b23..e27839d60935b 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMetaObject.cs @@ -7,7 +7,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { // Note: we only need to support the operations used by ComBinder - internal class ComMetaObject : DynamicMetaObject + internal sealed class ComMetaObject : DynamicMetaObject { internal ComMetaObject(Expression expression, BindingRestrictions restrictions, object arg) : base(expression, restrictions, arg) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMethodDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMethodDesc.cs index 5b3a749a20a9d..1b47f28e30aaa 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMethodDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComMethodDesc.cs @@ -6,7 +6,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ComMethodDesc + internal sealed class ComMethodDesc { private readonly INVOKEKIND _invokeKind; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs index 1c33aff3b4358..3fa5ca8c7c754 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeClassDesc.cs @@ -9,7 +9,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ComTypeClassDesc : ComTypeDesc, IDynamicMetaObjectProvider + internal sealed class ComTypeClassDesc : ComTypeDesc, IDynamicMetaObjectProvider { private LinkedList _itfs; // implemented interfaces private LinkedList _sourceItfs; // source interfaces supported by this coclass diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeEnumDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeEnumDesc.cs index 470f55e59a00b..0d9b7998872d4 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeEnumDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeEnumDesc.cs @@ -15,10 +15,7 @@ internal sealed class ComTypeEnumDesc : ComTypeDesc, IDynamicMetaObjectProvider private readonly string[] _memberNames; private readonly object[] _memberValues; - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "", TypeName); - } + public override string ToString() => $""; internal ComTypeEnumDesc(ComTypes.ITypeInfo typeInfo, ComTypeLibDesc typeLibDesc) : base(typeInfo, typeLibDesc) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs index a65ba460a3bd7..522ce0135eafc 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ComTypeLibDesc.cs @@ -32,10 +32,7 @@ private ComTypeLibDesc() _classes = new LinkedList(); } - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "", Name); - } + public override string ToString() => $""; public string Documentation { diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs index dccd3ab0768e7..6958c6a0223c8 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConversionArgBuilder.cs @@ -6,7 +6,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ConversionArgBuilder : ArgBuilder + internal sealed class ConversionArgBuilder : ArgBuilder { private readonly SimpleArgBuilder _innerBuilder; private readonly Type _parameterType; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs index 122ad62a83e72..668d269ba2ed4 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertArgBuilder.cs @@ -6,7 +6,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ConvertArgBuilder : SimpleArgBuilder + internal sealed class ConvertArgBuilder : SimpleArgBuilder { private readonly Type _marshalType; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs index b9dd4b1a3c8ca..7e18343a4eaf4 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ConvertibleArgBuilder.cs @@ -6,7 +6,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ConvertibleArgBuilder : ArgBuilder + internal sealed class ConvertibleArgBuilder : ArgBuilder { internal override Expression Marshal(Expression parameter) { diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs index 53f9d453a6c72..d4ec6cb6b168e 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallable.cs @@ -19,10 +19,7 @@ internal DispCallable(IDispatchComObject dispatch, string memberName, int dispId DispId = dispId; } - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "", MemberName); - } + public override string ToString() => $""; public IDispatchComObject DispatchComObject { get; } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs index b3a2afcf098d0..57ad83ac75f92 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispCallableMetaObject.cs @@ -6,7 +6,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class DispCallableMetaObject : DynamicMetaObject + internal sealed class DispCallableMetaObject : DynamicMetaObject { private readonly DispCallable _callable; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs index 2c0c9d05af987..a44fe5799a878 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/DispatchArgBuilder.cs @@ -7,7 +7,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class DispatchArgBuilder : SimpleArgBuilder + internal sealed class DispatchArgBuilder : SimpleArgBuilder { private readonly bool _isWrapper; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs index ce82891bddc30..a82f709943957 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/ErrorArgBuilder.cs @@ -8,7 +8,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class ErrorArgBuilder : SimpleArgBuilder + internal sealed class ErrorArgBuilder : SimpleArgBuilder { internal ErrorArgBuilder(Type parameterType) : base(parameterType) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs index 6a7a29764b8ad..3d8371d0d5188 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/IDispatchComObject.cs @@ -99,7 +99,7 @@ public override string ToString() typeName = "IDispatch"; } - return string.Format(CultureInfo.CurrentCulture, "{0} ({1})", RuntimeCallableWrapper.ToString(), typeName); + return $"{RuntimeCallableWrapper.ToString()} ({typeName})"; } public ComTypeDesc ComTypeDesc diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs index be07da7422955..2eaace5023405 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/StringArgBuilder.cs @@ -8,7 +8,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class StringArgBuilder : SimpleArgBuilder + internal sealed class StringArgBuilder : SimpleArgBuilder { private readonly bool _isWrapper; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeEnumMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeEnumMetaObject.cs index b2b55a25b2600..2405567b372e4 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeEnumMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeEnumMetaObject.cs @@ -8,7 +8,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class TypeEnumMetaObject : DynamicMetaObject { + internal sealed class TypeEnumMetaObject : DynamicMetaObject { private readonly ComTypeEnumDesc _desc; internal TypeEnumMetaObject(ComTypeEnumDesc desc, Expression expression) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeLibMetaObject.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeLibMetaObject.cs index ffc383536673a..b3395b4e0240f 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeLibMetaObject.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/TypeLibMetaObject.cs @@ -7,7 +7,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class TypeLibMetaObject : DynamicMetaObject { + internal sealed class TypeLibMetaObject : DynamicMetaObject { private readonly ComTypeLibDesc _lib; internal TypeLibMetaObject(Expression expression, ComTypeLibDesc lib) diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs index a587e20cc391a..9c0dd4c5c7bcc 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/UnknownArgBuilder.cs @@ -7,7 +7,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class UnknownArgBuilder : SimpleArgBuilder + internal sealed class UnknownArgBuilder : SimpleArgBuilder { private readonly bool _isWrapper; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs index 435e94f96fa73..40d36cae21e7f 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VarEnumSelector.cs @@ -25,7 +25,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop /// /// VarEnumSelector implements option # 3 /// - internal class VarEnumSelector + internal sealed class VarEnumSelector { private static readonly Dictionary s_comToManagedPrimitiveTypes = CreateComToManagedPrimitiveTypes(); private static readonly IList> s_comPrimitiveTypeFamilies = CreateComPrimitiveTypeFamilies(); diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/Variant.Extended.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/Variant.Extended.cs index 777518d1a6dbc..becef48a1466c 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/Variant.Extended.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/Variant.Extended.cs @@ -281,10 +281,7 @@ internal static System.Reflection.MethodInfo GetByrefSetter(VarEnum varType) } } - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "Variant ({0})", VariantType); - } + public override string ToString() => $"Variant ({VariantType})"; public void SetAsIConvertible(IConvertible value) { diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs index f6cd5dc34d3c6..de0493a771c56 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantArgBuilder.cs @@ -8,7 +8,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop { - internal class VariantArgBuilder : SimpleArgBuilder + internal sealed class VariantArgBuilder : SimpleArgBuilder { private readonly bool _isWrapper; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs index c09e2aa4e1c41..5d3da6f009c12 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/ComInterop/VariantBuilder.cs @@ -10,7 +10,7 @@ namespace Microsoft.CSharp.RuntimeBinder.ComInterop /// /// VariantBuilder handles packaging of arguments into a Variant for a call to IDispatch.Invoke /// - internal class VariantBuilder + internal sealed class VariantBuilder { private MemberExpression _variant; private readonly ArgBuilder _argBuilder; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs index c859eb5326ba6..5f0438a8ecc41 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/DynamicDebuggerProxy.cs @@ -51,7 +51,7 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject self, Dyna internal sealed class DynamicMetaObjectProviderDebugView { [System.Diagnostics.DebuggerDisplay("{value}", Name = "{name, nq}", Type = "{type, nq}")] - internal class DynamicProperty + internal sealed class DynamicProperty { [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] private readonly string name; @@ -452,13 +452,13 @@ private static IList> QueryDynamicObject(object obj } [Serializable] - internal class DynamicDebugViewEmptyException : Exception + internal sealed class DynamicDebugViewEmptyException : Exception { public DynamicDebugViewEmptyException() { } - protected DynamicDebugViewEmptyException(SerializationInfo info, StreamingContext context) + private DynamicDebugViewEmptyException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs index 15e85f43dfaeb..4d3538a495686 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MemberLookupResults.cs @@ -11,7 +11,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // to get at the inaccessible symbols, bogus symbols, and validly bound symbols. // ---------------------------------------------------------------------------- - internal partial class CMemberLookupResults + internal sealed partial class CMemberLookupResults { private TypeArray ContainingTypes { get; }// Types that contain the member we're looking for. diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs index a27bc633f07d4..764189e817408 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/MethodIterator.cs @@ -7,7 +7,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics { - internal partial class CMemberLookupResults + internal sealed partial class CMemberLookupResults { public class CMethodIterator { diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs index 9d9c3b6d3037b..7076d277fbbd3 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/AggregateSymbol.cs @@ -14,7 +14,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // interfaces, and structs. Parent is a namespace or class. Children are methods, // properties, and member variables, and types (including its own AGGTYPESYMs). - internal class AggregateSymbol : NamespaceOrAggregateSymbol + internal sealed class AggregateSymbol : NamespaceOrAggregateSymbol { public Type AssociatedSystemType; public Assembly AssociatedAssembly; diff --git a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/FieldSymbol.cs b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/FieldSymbol.cs index 8e9bb895a21db..9512e6e63d638 100644 --- a/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/FieldSymbol.cs +++ b/src/libraries/Microsoft.CSharp/src/Microsoft/CSharp/RuntimeBinder/Semantics/Symbols/FieldSymbol.cs @@ -15,7 +15,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Semantics // // ---------------------------------------------------------------------------- - internal class FieldSymbol : VariableSymbol + internal sealed class FieldSymbol : VariableSymbol { public new bool isStatic; // Static member? public bool isReadOnly; // Can only be changed from within constructor. diff --git a/src/libraries/Microsoft.CSharp/tests/DefaultParameterTests.cs b/src/libraries/Microsoft.CSharp/tests/DefaultParameterTests.cs index e6b7d359bd7e9..beae75bc5dea9 100644 --- a/src/libraries/Microsoft.CSharp/tests/DefaultParameterTests.cs +++ b/src/libraries/Microsoft.CSharp/tests/DefaultParameterTests.cs @@ -9,6 +9,7 @@ namespace Microsoft.CSharp.RuntimeBinder.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class DefaultParameterTests { diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryState.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryState.cs index 77aac7b8a6d9e..a3774fef395de 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryState.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryState.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Caching.Memory { - internal partial class CacheEntry + internal sealed partial class CacheEntry { // this type exists just to reduce CacheEntry size by replacing many enum & boolean fields with one of a size of Int32 private struct CacheEntryState diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs index 94e7144bb9499..b67106dd7e0d5 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.CacheEntryTokens.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Caching.Memory { - internal partial class CacheEntry + internal sealed partial class CacheEntry { // this type exists just to reduce average CacheEntry size // which typically is not using expiration tokens or callbacks diff --git a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs index 5a88f8a8a8cda..cd3aabcf840a3 100644 --- a/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs +++ b/src/libraries/Microsoft.Extensions.Caching.Memory/src/CacheEntry.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Caching.Memory { - internal partial class CacheEntry : ICacheEntry + internal sealed partial class CacheEntry : ICacheEntry { private static readonly Action ExpirationCallback = ExpirationTokensExpired; diff --git a/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/CommandLineConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/CommandLineConfigurationProvider.cs index 7c4098b20e72d..fb1b17a521630 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/CommandLineConfigurationProvider.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.CommandLine/src/CommandLineConfigurationProvider.cs @@ -60,7 +60,7 @@ public override void Load() { // "/SomeSwitch" is equivalent to "--SomeSwitch" when interpreting switch mappings // So we do a conversion to simplify later processing - currentArg = string.Format("--{0}", currentArg.Substring(1)); + currentArg = $"--{currentArg.Substring(1)}"; keyStartIndex = 2; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs b/src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs index d8cdbc4438ad3..2104b66ea66c4 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs @@ -13,7 +13,7 @@ internal sealed class JsonConfigurationFileParser { private JsonConfigurationFileParser() { } - private readonly SortedDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _data = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Stack _paths = new Stack(); public static IDictionary Parse(Stream input) diff --git a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs index 7859e9f01f5fa..03667ab17b33c 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.UserSecrets/tests/ConfigurationExtensionTest.cs @@ -52,7 +52,7 @@ private void SetSecret(string id, string key, string value) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void AddUserSecrets_FindsAssemblyAttribute() { var randValue = Guid.NewGuid().ToString(); @@ -67,7 +67,7 @@ public void AddUserSecrets_FindsAssemblyAttribute() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void AddUserSecrets_FindsAssemblyAttributeFromType() { var randValue = Guid.NewGuid().ToString(); @@ -121,16 +121,16 @@ public void AddUserSecrets_DoesThrowsIfNotOptionalAndSecretDoesNotExist() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void AddUserSecrets_With_SecretsId_Passed_Explicitly() { var userSecretsId = Guid.NewGuid().ToString(); - SetSecret(userSecretsId, "Facebook:AppSecret", "value1"); + SetSecret(userSecretsId, "Facebook:PLACEHOLDER", "value1"); var builder = new ConfigurationBuilder().AddUserSecrets(userSecretsId); var configuration = builder.Build(); - Assert.Equal("value1", configuration["Facebook:AppSecret"]); + Assert.Equal("value1", configuration["Facebook:PLACEHOLDER"]); } [Fact] @@ -141,7 +141,7 @@ public void AddUserSecrets_Does_Not_Fail_On_Non_Existing_File() var builder = new ConfigurationBuilder().AddUserSecrets(userSecretsId); var configuration = builder.Build(); - Assert.Null(configuration["Facebook:AppSecret"]); + Assert.Null(configuration["Facebook:PLACEHOLDER"]); Assert.False(File.Exists(secretFilePath)); } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElement.cs b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElement.cs index 26441a447436c..dde4dd82f32e3 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElement.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElement.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Configuration.Xml { - internal class XmlConfigurationElement + internal sealed class XmlConfigurationElement { public string ElementName { get; } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementAttributeValue.cs b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementAttributeValue.cs index bdea5cd8e60f5..a07231787a9ba 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementAttributeValue.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementAttributeValue.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Configuration.Xml { - internal class XmlConfigurationElementAttributeValue + internal sealed class XmlConfigurationElementAttributeValue { public XmlConfigurationElementAttributeValue(string attribute, string value, int? lineNumber, int? linePosition) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementTextContent.cs b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementTextContent.cs index 76dab1529ddda..c3ef8fee108c0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementTextContent.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlConfigurationElementTextContent.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Configuration.Xml { - internal class XmlConfigurationElementTextContent + internal sealed class XmlConfigurationElementTextContent { public XmlConfigurationElementTextContent(string textContent, int? linePosition, int? lineNumber) { diff --git a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlStreamConfigurationProvider.cs b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlStreamConfigurationProvider.cs index bfcd4adae3894..920a9968f4176 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlStreamConfigurationProvider.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Xml/src/XmlStreamConfigurationProvider.cs @@ -407,7 +407,7 @@ void AddToConfiguration(string key, string value, int? lineNumber, int? linePosi /// /// Helper class to build the configuration keys in a way that does not require string.Join /// - internal class Prefix + internal sealed class Prefix { private readonly StringBuilder _sb; private readonly Stack _lengths; diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ArrayTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ArrayTests.cs index 8836d7a8f65aa..0ef348ac163fd 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ArrayTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ArrayTests.cs @@ -47,7 +47,7 @@ public class ArrayTests : IDisposable "; [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void DifferentConfigSources_Merged_KeysAreSorted() { var config = BuildConfig(); @@ -76,7 +76,7 @@ public void DifferentConfigSources_Merged_KeysAreSorted() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void DifferentConfigSources_Merged_WithOverwrites() { var config = BuildConfig(); diff --git a/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs b/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs index 87e2c9e51274a..62b2589ad1660 100644 --- a/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/ConfigurationTests.cs @@ -207,7 +207,7 @@ public void MissingFileDoesNotIncludesAbsolutePathIfWithNoPhysicalPath() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() { WriteTestFiles(); @@ -247,7 +247,7 @@ public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProvidersWithAbsolutePath() { WriteTestFiles(); @@ -288,7 +288,7 @@ public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProvidersWithAb } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CanOverrideValuesWithNewConfigurationProvider() { WriteTestFiles(); @@ -361,7 +361,7 @@ public IConfigurationProvider Build(IConfigurationBuilder builder) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void OnLoadErrorWillBeCalledOnJsonParseError() { _fileSystem.WriteFile(Path.Combine(_basePath, "error.json"), @"{""JsonKey1"": ", absolute: true); @@ -390,7 +390,7 @@ public void OnLoadErrorWillBeCalledOnJsonParseError() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void OnLoadErrorWillBeCalledOnXmlParseError() { _fileSystem.WriteFile("error.xml", @"gobblygook"); @@ -418,7 +418,7 @@ public void OnLoadErrorWillBeCalledOnXmlParseError() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void OnLoadErrorWillBeCalledOnIniLoadError() { _fileSystem.WriteFile("error.ini", @"IniKey1=IniValue1 @@ -446,7 +446,7 @@ public void OnLoadErrorWillBeCalledOnIniLoadError() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void OnLoadErrorCanIgnoreErrors() { _fileSystem.WriteFile("error.json", @"{""JsonKey1"": "); @@ -819,7 +819,7 @@ await WaitForChange( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void LoadIncorrectJsonFile_ThrowException() { var json = @"{ @@ -836,7 +836,7 @@ public void LoadIncorrectJsonFile_ThrowException() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void SetBasePathCalledMultipleTimesForEachSourceLastOneWins() { var builder = new ConfigurationBuilder(); @@ -870,7 +870,7 @@ public void SetBasePathCalledMultipleTimesForEachSourceLastOneWins() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void GetDefaultBasePathForSources() { var builder = new ConfigurationBuilder(); @@ -900,7 +900,7 @@ public void GetDefaultBasePathForSources() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [SkipOnMono("System.IO.FileSystem.Watcher is not supported on wasm", TestPlatforms.Browser)] public void CanEnumerateProviders() { @@ -948,7 +948,7 @@ await WaitForChange( } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void BindingDoesNotThrowIfReloadedDuringBinding() { WriteTestFiles(); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs index bf39993860f14..a853a9fe5b590 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/CallSiteJsonFormatter.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.DependencyInjection { - internal class CallSiteJsonFormatter: CallSiteVisitor + internal sealed class CallSiteJsonFormatter: CallSiteVisitor { internal static CallSiteJsonFormatter Instance = new CallSiteJsonFormatter(); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs index a017bfdd900a9..fdca10e769620 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Tracing; using System.Linq.Expressions; using Microsoft.Extensions.DependencyInjection.ServiceLookup; @@ -29,6 +30,8 @@ private DependencyInjectionEventSource() : base(EventSourceSettings.EtwSelfDescr // - A stop event's event id must be next one after its start event. // - Avoid renaming methods or parameters marked with EventAttribute. EventSource uses these to form the event object. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Parameters to this method are primitive and are trimmer safe.")] [Event(1, Level = EventLevel.Verbose)] private void CallSiteBuilt(string serviceType, string callSite, int chunkIndex, int chunkCount) { @@ -104,7 +107,7 @@ public static void ExpressionTreeGenerated(this DependencyInjectionEventSource s } } - private class NodeCountingVisitor : ExpressionVisitor + private sealed class NodeCountingVisitor : ExpressionVisitor { public int NodeCount { get; private set; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopePool.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopePool.cs new file mode 100644 index 0000000000000..b077306e5a5a9 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopePool.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using Microsoft.Extensions.DependencyInjection.ServiceLookup; + +namespace Microsoft.Extensions.DependencyInjection +{ + internal class ScopePool + { + // Modest number to re-use. We only really care about reuse for short lived scopes + private const int MaxQueueSize = 128; + + private int _count; + private readonly ConcurrentQueue _queue = new(); + + public State Rent() + { + if (_queue.TryDequeue(out State state)) + { + Interlocked.Decrement(ref _count); + return state; + } + return new State(this); + } + + public bool Return(State state) + { + if (Interlocked.Increment(ref _count) > MaxQueueSize) + { + Interlocked.Decrement(ref _count); + return false; + } + + state.Clear(); + _queue.Enqueue(state); + return true; + } + + public class State + { + private readonly ScopePool _pool; + + public IDictionary ResolvedServices { get; } + public List Disposables { get; set; } + + public State(ScopePool pool = null) + { + _pool = pool; + // When pool is null, we're in the global scope which doesn't need pooling. + // To reduce lock contention for singletons upon resolve we use a concurrent dictionary. + ResolvedServices = pool == null ? new ConcurrentDictionary() : new Dictionary(); + } + + internal void Clear() + { + // This should only get called from the pool + Debug.Assert(_pool != null); + // REVIEW: Should we trim excess here as well? + ResolvedServices.Clear(); + Disposables?.Clear(); + } + + public bool Return() + { + return _pool?.Return(this) ?? false; + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteChain.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteChain.cs index 04ef765574116..80182dab6cce9 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteChain.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteChain.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class CallSiteChain + internal sealed class CallSiteChain { private readonly Dictionary _callSiteChain; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index d7e6fb0e720ad..2e8e9a889bb34 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class CallSiteFactory + internal sealed class CallSiteFactory { private const int DefaultSlot = 0; private readonly ServiceDescriptor[] _descriptors; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs index fd5c896278bbd..36de790a24efd 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs @@ -90,7 +90,7 @@ protected override object VisitScopeCache(ServiceCallSite callSite, RuntimeResol private object VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) { bool lockTaken = false; - IDictionary resolvedServices = serviceProviderEngine.ResolvedServices; + object sync = serviceProviderEngine.Sync; // Taking locks only once allows us to fork resolution process // on another thread without causing the deadlock because we @@ -98,7 +98,7 @@ private object VisitCache(ServiceCallSite callSite, RuntimeResolverContext conte // releasing the lock if ((context.AcquiredLocks & lockType) == 0) { - Monitor.Enter(resolvedServices, ref lockTaken); + Monitor.Enter(sync, ref lockTaken); } try @@ -109,7 +109,7 @@ private object VisitCache(ServiceCallSite callSite, RuntimeResolverContext conte { if (lockTaken) { - Monitor.Exit(resolvedServices); + Monitor.Exit(sync); } } } @@ -123,7 +123,7 @@ private object ResolveService(ServiceCallSite callSite, RuntimeResolverContext c // For scoped: takes a dictionary as both a resolution lock and a dictionary access lock. Debug.Assert( (lockType == RuntimeResolverLock.Root && resolvedServices is ConcurrentDictionary) || - (lockType == RuntimeResolverLock.Scope && Monitor.IsEntered(resolvedServices))); + (lockType == RuntimeResolverLock.Scope && Monitor.IsEntered(serviceProviderEngine.Sync))); if (resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved)) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs index 243cf1c638c72..f836ba78b41b3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteValidator.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class CallSiteValidator: CallSiteVisitor + internal sealed class CallSiteValidator: CallSiteVisitor { // Keys are services being resolved via GetService, values - first scoped service in their call site tree private readonly ConcurrentDictionary _scopedServices = new ConcurrentDictionary(); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs index bd09485c617f6..206d2691a878f 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ConstantCallSite : ServiceCallSite + internal sealed class ConstantCallSite : ServiceCallSite { private readonly Type _serviceType; internal object DefaultValue { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstructorCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstructorCallSite.cs index d25bec5bdd8eb..b44608c0841b5 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstructorCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstructorCallSite.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ConstructorCallSite : ServiceCallSite + internal sealed class ConstructorCallSite : ServiceCallSite { internal ConstructorInfo ConstructorInfo { get; } internal ServiceCallSite[] ParameterCallSites { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs index fbc40fe43b590..20c2faf44709b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine + internal sealed class DynamicServiceProviderEngine : CompiledServiceProviderEngine { public DynamicServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index 12ea7e7405f6e..4ad093a9f6ce0 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ExpressionResolverBuilder : CallSiteVisitor + internal sealed class ExpressionResolverBuilder : CallSiteVisitor { internal static readonly MethodInfo InvokeFactoryMethodInfo = GetMethodInfo, IServiceProvider>>((a, b) => a.Invoke(b)); internal static readonly MethodInfo CaptureDisposableMethodInfo = GetMethodInfo>((a, b) => a.CaptureDisposable(b)); @@ -26,9 +26,18 @@ internal class ExpressionResolverBuilder : CallSiteVisitor private static readonly ParameterExpression ScopeParameter = Expression.Parameter(typeof(ServiceProviderEngineScope)); private static readonly ParameterExpression ResolvedServices = Expression.Variable(typeof(IDictionary), ScopeParameter.Name + "resolvedServices"); + private static readonly ParameterExpression Sync = Expression.Variable(typeof(object), ScopeParameter.Name + "sync"); private static readonly BinaryExpression ResolvedServicesVariableAssignment = Expression.Assign(ResolvedServices, - Expression.Property(ScopeParameter, nameof(ServiceProviderEngineScope.ResolvedServices))); + Expression.Property( + ScopeParameter, + typeof(ServiceProviderEngineScope).GetProperty(nameof(ServiceProviderEngineScope.ResolvedServices), BindingFlags.Instance | BindingFlags.NonPublic))); + + private static readonly BinaryExpression SyncVariableAssignment = + Expression.Assign(Sync, + Expression.Property( + ScopeParameter, + typeof(ServiceProviderEngineScope).GetProperty(nameof(ServiceProviderEngineScope.Sync), BindingFlags.Instance | BindingFlags.NonPublic))); private static readonly ParameterExpression CaptureDisposableParameter = Expression.Parameter(typeof(object)); private static readonly LambdaExpression CaptureDisposable = Expression.Lambda( @@ -94,8 +103,9 @@ private Expression> BuildExpression(Ser { return Expression.Lambda>( Expression.Block( - new[] { ResolvedServices }, + new[] { ResolvedServices, Sync }, ResolvedServicesVariableAssignment, + SyncVariableAssignment, BuildScopedExpression(callSite)), ScopeParameter); } @@ -211,7 +221,6 @@ protected override Expression VisitScopeCache(ServiceCallSite callSite, object c // Move off the main stack private Expression BuildScopedExpression(ServiceCallSite callSite) { - ConstantExpression keyExpression = Expression.Constant( callSite.Cache.Key, typeof(ServiceCacheKey)); @@ -255,9 +264,10 @@ private Expression BuildScopedExpression(ServiceCallSite callSite) // The C# compiler would copy the lock object to guard against mutation. // We don't, since we know the lock object is readonly. ParameterExpression lockWasTaken = Expression.Variable(typeof(bool), "lockWasTaken"); + ParameterExpression sync = Sync; - MethodCallExpression monitorEnter = Expression.Call(MonitorEnterMethodInfo, resolvedServices, lockWasTaken); - MethodCallExpression monitorExit = Expression.Call(MonitorExitMethodInfo, resolvedServices); + MethodCallExpression monitorEnter = Expression.Call(MonitorEnterMethodInfo, sync, lockWasTaken); + MethodCallExpression monitorExit = Expression.Call(MonitorExitMethodInfo, sync); BlockExpression tryBody = Expression.Block(monitorEnter, blockExpression); ConditionalExpression finallyBody = Expression.IfThen(lockWasTaken, monitorExit); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs index 7a81fa7f59b3e..eb6ea1e4ecb61 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ExpressionsServiceProviderEngine : ServiceProviderEngine + internal sealed class ExpressionsServiceProviderEngine : ServiceProviderEngine { private readonly ExpressionResolverBuilder _expressionResolverBuilder; public ExpressionsServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/FactoryCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/FactoryCallSite.cs index cd88c4b405f89..59e5267974ac5 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/FactoryCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/FactoryCallSite.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class FactoryCallSite : ServiceCallSite + internal sealed class FactoryCallSite : ServiceCallSite { public Func Factory { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs index 89be681080565..7e9b0f7f89954 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class IEnumerableCallSite : ServiceCallSite + internal sealed class IEnumerableCallSite : ServiceCallSite { internal Type ItemType { get; } internal ServiceCallSite[] ServiceCallSites { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs index c2da69e7dcb0f..25066d90e220c 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs @@ -14,13 +14,16 @@ internal sealed class ILEmitResolverBuilder : CallSiteVisitor)); + LocalBuilder syncLocal = context.Generator.DeclareLocal(typeof(object)); LocalBuilder lockTakenLocal = context.Generator.DeclareLocal(typeof(bool)); LocalBuilder resultLocal = context.Generator.DeclareLocal(typeof(object)); @@ -339,8 +343,15 @@ private ILEmitResolverBuilderRuntimeContext GenerateMethodBody(ServiceCallSite c // Store resolved services Stloc(context.Generator, resolvedServicesLocal.LocalIndex); - // Load resolvedServices - Ldloc(context.Generator, resolvedServicesLocal.LocalIndex); + // scope + context.Generator.Emit(OpCodes.Ldarg_1); + // .Sync + context.Generator.Emit(OpCodes.Callvirt, ScopeLockGetter); + // Store syncLocal + Stloc(context.Generator, syncLocal.LocalIndex); + + // Load syncLocal + Ldloc(context.Generator, syncLocal.LocalIndex); // Load address of lockTaken context.Generator.Emit(OpCodes.Ldloca_S, lockTakenLocal.LocalIndex); // Monitor.Enter @@ -388,8 +399,8 @@ private ILEmitResolverBuilderRuntimeContext GenerateMethodBody(ServiceCallSite c Ldloc(context.Generator, lockTakenLocal.LocalIndex); // return if not context.Generator.Emit(OpCodes.Brfalse, returnLabel); - // Load resolvedServices - Ldloc(context.Generator, resolvedServicesLocal.LocalIndex); + // Load syncLocal + Ldloc(context.Generator, syncLocal.LocalIndex); // Monitor.Exit context.Generator.Emit(OpCodes.Call, ExpressionResolverBuilder.MonitorExitMethodInfo); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilderContext.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilderContext.cs index def3457fe82b7..33cde16bb0091 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilderContext.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilderContext.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ILEmitResolverBuilderContext + internal sealed class ILEmitResolverBuilderContext { public ILGenerator Generator { get; set; } public List Constants { get; set; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs index da854208dfdf9..ca8a1ad1a124e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ILEmitServiceProviderEngine : ServiceProviderEngine + internal sealed class ILEmitServiceProviderEngine : ServiceProviderEngine { private readonly ILEmitResolverBuilder _expressionResolverBuilder; public ILEmitServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs index 83d762739ca58..45a41c97fba74 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class RuntimeServiceProviderEngine : ServiceProviderEngine + internal sealed class RuntimeServiceProviderEngine : ServiceProviderEngine { public RuntimeServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderCallSite.cs index 8339c64ecc310..3db2f7f0723e0 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderCallSite.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ServiceProviderCallSite : ServiceCallSite + internal sealed class ServiceProviderCallSite : ServiceCallSite { public ServiceProviderCallSite() : base(ResultCache.None) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs index d6d804c648448..48bab2f5fa510 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs @@ -25,8 +25,11 @@ protected ServiceProviderEngine(IEnumerable serviceDescriptor CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite()); RealizedServices = new ConcurrentDictionary>(); + ScopePool = new ScopePool(); } + internal ScopePool ScopePool { get; } + internal ConcurrentDictionary> RealizedServices { get; } internal CallSiteFactory CallSiteFactory { get; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs index 76988982c8754..656cf9b10c4f1 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs @@ -2,33 +2,34 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Threading.Tasks; using Microsoft.Extensions.Internal; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IAsyncDisposable + internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IAsyncDisposable { // For testing only internal Action _captureDisposableCallback; - private List _disposables; - private bool _disposed; - private readonly object _disposelock = new object(); + private ScopePool.State _state; + + // This lock protects state on the scope, in particular, for the root scope, it protects + // the list of disposable entries only, since ResolvedServices is a concurrent dictionary. + // For other scopes, it protects ResolvedServices and the list of disposables + private readonly object _scopeLock = new object(); public ServiceProviderEngineScope(ServiceProviderEngine engine, bool isRoot = false) { Engine = engine; - - // To reduce lock contention for singletons upon resolve we use a concurrent dictionary. - ResolvedServices = isRoot ? new ConcurrentDictionary() : new Dictionary(); + _state = isRoot ? new ScopePool.State() : engine.ScopePool.Rent(); } - internal IDictionary ResolvedServices { get; } + internal IDictionary ResolvedServices => _state?.ResolvedServices ?? ScopeDisposed(); + + internal object Sync => _scopeLock; public ServiceProviderEngine Engine { get; } @@ -53,7 +54,7 @@ internal object CaptureDisposable(object service) return service; } - lock (_disposelock) + lock (_scopeLock) { if (_disposed) { @@ -66,16 +67,15 @@ internal object CaptureDisposable(object service) // sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool. Task.Run(() => ((IAsyncDisposable)service).DisposeAsync().AsTask()).GetAwaiter().GetResult(); } + ThrowHelper.ThrowObjectDisposedException(); } - if (_disposables == null) - { - _disposables = new List(); - } + _state.Disposables ??= new List(); - _disposables.Add(service); + _state.Disposables.Add(service); } + return service; } @@ -97,6 +97,8 @@ public void Dispose() } } } + + ClearState(); } public ValueTask DisposeAsync() @@ -115,7 +117,7 @@ public ValueTask DisposeAsync() ValueTask vt = asyncDisposable.DisposeAsync(); if (!vt.IsCompletedSuccessfully) { - return Await(i, vt, toDispose); + return Await(this, i, vt, toDispose); } // If its a IValueTaskSource backed ValueTask, @@ -134,9 +136,11 @@ public ValueTask DisposeAsync() } } + ClearState(); + return default; - static async ValueTask Await(int i, ValueTask vt, List toDispose) + static async ValueTask Await(ServiceProviderEngineScope scope, int i, ValueTask vt, List toDispose) { await vt.ConfigureAwait(false); // vt is acting on the disposable at index i, @@ -155,30 +159,52 @@ static async ValueTask Await(int i, ValueTask vt, List toDispose) ((IDisposable)disposable).Dispose(); } } + + scope.ClearState(); + } + } + + private IDictionary ScopeDisposed() + { + ThrowHelper.ThrowObjectDisposedException(); + return null; + } + + private void ClearState() + { + // We lock here since ResolvedServices is always accessed in the scope lock, this means we'll never + // try to return to the pool while somebody is trying to access ResolvedServices. + lock (_scopeLock) + { + // ResolvedServices is never cleared for singletons because there might be a compilation running in background + // trying to get a cached singleton service. If it doesn't find it + // it will try to create a new one which will result in an ObjectDisposedException. + + // Dispose the state, which will end up attempting to return the state pool. + // This will return false if the pool is full or if this state object is the root scope + if (_state.Return()) + { + _state = null; + } } } private List BeginDispose() { - List toDispose; - lock (_disposelock) + lock (_scopeLock) { if (_disposed) { return null; } + // We've transitioned to the disposed state, so future calls to + // CaptureDisposable will immediately dispose the object. + // No further changes to _state.Disposables, are allowed. _disposed = true; - toDispose = _disposables; - _disposables = null; - // Not clearing ResolvedServices here because there might be a compilation running in background - // trying to get a cached singleton service instance and if it won't find - // it it will try to create a new one tripping the Debug.Assert in CaptureDisposable - // and leaking a Disposable object in Release mode + return _state.Disposables; } - - return toDispose; } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs index ffa0bc869213b..fc95d601f5e9f 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ServiceScopeFactoryCallSite : ServiceCallSite + internal sealed class ServiceScopeFactoryCallSite : ServiceCallSite { public ServiceScopeFactoryCallSite() : base(ResultCache.None) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ThrowHelper.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ThrowHelper.cs index 2f9edafbaa045..9ed91ff5148c4 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ThrowHelper.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ThrowHelper.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal class ThrowHelper + internal static class ThrowHelper { [MethodImpl(MethodImplOptions.NoInlining)] internal static void ThrowObjectDisposedException() diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Microsoft.Extensions.DependencyInjection.ExternalContainers.Tests.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Microsoft.Extensions.DependencyInjection.ExternalContainers.Tests.csproj index 5b048b60cdc8d..e74abce985bf8 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Microsoft.Extensions.DependencyInjection.ExternalContainers.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Microsoft.Extensions.DependencyInjection.ExternalContainers.Tests.csproj @@ -4,9 +4,6 @@ $(NetCoreAppCurrent);net461 true $(NoWarn);CS8002 - - true diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs index a4fe5b6b5afd5..ae36f2e1d64bb 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection.Specification.Fakes; using Xunit; @@ -9,13 +11,28 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup public class ServiceProviderEngineScopeTests { [Fact] - public void Dispose_DoesntClearResolvedServices() + public void ResolvedServicesAfterDispose_ThrowsObjectDispose() { - var serviceProviderEngineScope = new ServiceProviderEngineScope(null); + var engine = new FakeEngine(); + var serviceProviderEngineScope = new ServiceProviderEngineScope(engine); serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(typeof(IFakeService), 0), null); serviceProviderEngineScope.Dispose(); - Assert.Single(serviceProviderEngineScope.ResolvedServices); + Assert.Throws(() => serviceProviderEngineScope.ResolvedServices); + } + + private class FakeEngine : ServiceProviderEngine + { + public FakeEngine() : + base(Array.Empty()) + { + } + + protected override Func RealizeService(ServiceCallSite callSite) + { + return scope => null; + } + } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs index 18f6af8cacaf3..cb9f01fbd70f4 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs @@ -90,7 +90,7 @@ public static DependencyContext Load(Assembly assembly) return DependencyContextLoader.Default.Load(assembly); } - private class LibraryMergeEqualityComparer : IEqualityComparer where T : Library + private sealed class LibraryMergeEqualityComparer : IEqualityComparer where T : Library { public bool Equals(T x, T y) { diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextJsonReader.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextJsonReader.cs index 4d025d9076fb3..f356bd15cc844 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextJsonReader.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextJsonReader.cs @@ -804,7 +804,7 @@ private string Pool(string s) return result; } - private class Target + private sealed class Target { public string Name; diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextPaths.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextPaths.cs index 0f77f3f4b7b39..6c1eaff38b9e2 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextPaths.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextPaths.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.DependencyModel { - internal class DependencyContextPaths + internal sealed class DependencyContextPaths { private const string DepsFilesProperty = "APP_CONTEXT_DEPS_FILES"; private const string FxDepsFileProperty = "FX_DEPS_FILE"; diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextStrings.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextStrings.cs index a97bbcd38bc63..22e276d065205 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextStrings.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextStrings.cs @@ -3,7 +3,7 @@ namespace Microsoft.Extensions.DependencyModel { - internal class DependencyContextStrings + internal static class DependencyContextStrings { internal const char VersionSeparator = '/'; diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DirectoryWrapper.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DirectoryWrapper.cs index 02dc754d96151..7fefd9483cdd4 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DirectoryWrapper.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DirectoryWrapper.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.DependencyModel { - internal class DirectoryWrapper: IDirectory + internal sealed class DirectoryWrapper: IDirectory { public bool Exists(string path) { diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs index 0325f9ad7eab2..a26698e0aaef3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/EnvironmentWrapper.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyModel { - internal class EnvironmentWrapper : IEnvironment + internal sealed class EnvironmentWrapper : IEnvironment { public static IEnvironment Default = new EnvironmentWrapper(); diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/FileSystemWrapper.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/FileSystemWrapper.cs index a715780fa45a2..5deda61761df9 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/FileSystemWrapper.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/FileSystemWrapper.cs @@ -3,7 +3,7 @@ namespace Microsoft.Extensions.DependencyModel { - internal class FileSystemWrapper : IFileSystem + internal sealed class FileSystemWrapper : IFileSystem { public static IFileSystem Default { get; } = new FileSystemWrapper(); diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/FileWrapper.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/FileWrapper.cs index 34b903de77349..ef1bf5b2bc532 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/FileWrapper.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/FileWrapper.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyModel { - internal class FileWrapper: IFile + internal sealed class FileWrapper: IFile { public bool Exists(string path) { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/Clock.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/Clock.cs index 67371f56e74e7..aa154395f8b87 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/Clock.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/Clock.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.FileProviders.Physical { - internal class Clock : IClock + internal sealed class Clock : IClock { public static readonly Clock Instance = new Clock(); diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 1a8a349eee096..5bb882e5cccd9 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -226,7 +226,7 @@ public void GetFileInfoReturnsNotFoundFileInfoForRelativePathWithEmptySegmentsTh } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateReadStreamSucceedsOnEmptyFile() { using (var root = new DisposableFileSystem()) @@ -324,7 +324,7 @@ public void GetFileInfoReturnsFileInfoWhenExclusionDisabled() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void TokenIsSameForSamePath() { using (var root = new DisposableFileSystem()) @@ -347,7 +347,7 @@ public void TokenIsSameForSamePath() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokensFiredOnFileChange() { using (var root = new DisposableFileSystem()) @@ -377,7 +377,7 @@ public async Task TokensFiredOnFileChange() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenCallbackInvokedOnFileChange() { using (var root = new DisposableFileSystem()) @@ -413,7 +413,7 @@ public async Task TokenCallbackInvokedOnFileChange() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task WatcherWithPolling_ReturnsTrueForFileChangedWhenFileSystemWatcherDoesNotRaiseEvents() { using (var root = new DisposableFileSystem()) @@ -443,7 +443,7 @@ public async Task WatcherWithPolling_ReturnsTrueForFileChangedWhenFileSystemWatc } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task WatcherWithPolling_ReturnsTrueForFileRemovedWhenFileSystemWatcherDoesNotRaiseEvents() { using (var root = new DisposableFileSystem()) @@ -475,7 +475,7 @@ public async Task WatcherWithPolling_ReturnsTrueForFileRemovedWhenFileSystemWatc } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokensFiredOnFileDeleted() { using (var root = new DisposableFileSystem()) @@ -786,7 +786,7 @@ public void GetDirectoryContentsReturnsFilesWhenExclusionDisabled() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task FileChangeTokenNotNotifiedAfterExpiry() { using (var root = new DisposableFileSystem()) @@ -818,7 +818,7 @@ public async Task FileChangeTokenNotNotifiedAfterExpiry() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void TokenIsSameForSamePathCaseInsensitive() { using (var root = new DisposableFileSystem()) @@ -834,7 +834,7 @@ public void TokenIsSameForSamePathCaseInsensitive() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task CorrectTokensFiredForMultipleFiles() { using (var root = new DisposableFileSystem()) @@ -867,7 +867,7 @@ public async Task CorrectTokensFiredForMultipleFiles() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenNotAffectedByExceptions() { using (var root = new DisposableFileSystem()) @@ -925,7 +925,7 @@ public void NoopChangeTokenForFilterThatNavigatesAboveRoot() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void TokenForEmptyFilter() { using (var root = new DisposableFileSystem()) @@ -941,7 +941,7 @@ public void TokenForEmptyFilter() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void TokenForWhitespaceFilters() { using (var root = new DisposableFileSystem()) @@ -974,7 +974,7 @@ public void NoopChangeTokenForAbsolutePathFilters() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenFiredOnCreation() { using (var root = new DisposableFileSystem()) @@ -999,7 +999,7 @@ public async Task TokenFiredOnCreation() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenFiredOnDeletion() { using (var root = new DisposableFileSystem()) @@ -1024,7 +1024,7 @@ public async Task TokenFiredOnDeletion() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenFiredForFilesUnderPathEndingWithSlash() { using (var root = new DisposableFileSystem()) @@ -1062,7 +1062,7 @@ public async Task TokenFiredForFilesUnderPathEndingWithSlash() } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData("/")] [InlineData("///")] [InlineData("/\\/")] @@ -1151,7 +1151,7 @@ private async Task TokenNotFiredForInvalidPathStartingWithSlash(string slashes) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenFiredForGlobbingPatternsPointingToSubDirectory() { using (var root = new DisposableFileSystem()) @@ -1184,7 +1184,7 @@ public async Task TokenFiredForGlobbingPatternsPointingToSubDirectory() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void TokensWithForwardAndBackwardSlashesAreSame() { using (var root = new DisposableFileSystem()) @@ -1200,7 +1200,7 @@ public void TokensWithForwardAndBackwardSlashesAreSame() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokensFiredForOldAndNewNamesOnRename() { using (var root = new DisposableFileSystem()) @@ -1229,7 +1229,7 @@ public async Task TokensFiredForOldAndNewNamesOnRename() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokensFiredForNewDirectoryContentsOnRename() { var tcsShouldNotFire = new TaskCompletionSource(); @@ -1302,7 +1302,7 @@ void Fail(object state) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokenNotFiredForFileNameStartingWithPeriod() { using (var root = new DisposableFileSystem()) @@ -1327,7 +1327,7 @@ public async Task TokenNotFiredForFileNameStartingWithPeriod() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] // Hidden and system files only make sense on Windows. [PlatformSpecific(TestPlatforms.Windows)] public async Task TokensNotFiredForHiddenAndSystemFiles() @@ -1369,7 +1369,7 @@ public async Task TokensNotFiredForHiddenAndSystemFiles() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task TokensFiredForAllEntriesOnError() { using (var root = new DisposableFileSystem()) @@ -1397,7 +1397,7 @@ public async Task TokensFiredForAllEntriesOnError() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task WildCardToken_RaisesEventsForNewFilesAdded() { // Arrange @@ -1423,7 +1423,7 @@ public async Task WildCardToken_RaisesEventsForNewFilesAdded() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task WildCardToken_RaisesEventsWhenFileSystemWatcherDoesNotFire() { // Arrange @@ -1534,7 +1534,7 @@ public void CreateFileWatcher_CreatesWatcherWithPollingAndActiveFlags() [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task CanDeleteWatchedDirectory(bool useActivePolling) { using (var root = new DisposableFileSystem()) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFilesWatcherTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFilesWatcherTests.cs index 3644f63223ee9..77771f5390adc 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFilesWatcherTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFilesWatcherTests.cs @@ -36,7 +36,7 @@ public void CreateFileChangeToken_DoesNotAllowPathsAboveRoot() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task HandlesOnRenamedEventsThatMatchRootPath() { using (var root = new DisposableFileSystem()) diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Abstractions/DirectoryInfoWrapper.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Abstractions/DirectoryInfoWrapper.cs index 06bab743495c4..884c887aa5556 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Abstractions/DirectoryInfoWrapper.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Abstractions/DirectoryInfoWrapper.cs @@ -94,8 +94,7 @@ public override DirectoryInfoBase GetDirectory(string name) { // This shouldn't happen. The parameter name isn't supposed to contain wild card. throw new InvalidOperationException( - string.Format("More than one sub directories are found under {0} with name {1}.", - _directoryInfo.FullName, name)); + $"More than one sub directories are found under {_directoryInfo.FullName} with name {name}."); } } } diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs index d94f1982e1ea1..55688a8baf4d5 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/InMemoryDirectoryInfo.cs @@ -119,13 +119,11 @@ public override IEnumerable EnumerateFileSystemInfos() private bool IsRootDirectory(string rootDir, string filePath) { - if (!filePath.StartsWith(rootDir, StringComparison.Ordinal) || - filePath.IndexOf(Path.DirectorySeparatorChar, rootDir.Length) != rootDir.Length) - { - return false; - } + int rootDirLength = rootDir.Length; - return true; + return filePath.StartsWith(rootDir, StringComparison.Ordinal) && + (rootDir[rootDirLength - 1] == Path.DirectorySeparatorChar || + filePath.IndexOf(Path.DirectorySeparatorChar, rootDirLength) == rootDirLength); } /// diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/InMemoryFileInfo.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/InMemoryFileInfo.cs index c5b40ecf76a82..90f3f83bb77d7 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/InMemoryFileInfo.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/InMemoryFileInfo.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.FileSystemGlobbing.Internal { - internal class InMemoryFileInfo : FileInfoBase + internal sealed class InMemoryFileInfo : FileInfoBase { private InMemoryDirectoryInfo _parent; diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/MatcherContext.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/MatcherContext.cs index c4c45691b8c79..bd3ad94cfaebc 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/MatcherContext.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/MatcherContext.cs @@ -171,7 +171,7 @@ internal static string CombinePath(string left, string right) } else { - return string.Format("{0}/{1}", left, right); + return $"{left}/{right}"; } } diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/Patterns/PatternBuilder.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/Patterns/PatternBuilder.cs index f750c4142e01c..6b577ee66801a 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/Patterns/PatternBuilder.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Internal/Patterns/PatternBuilder.cs @@ -220,7 +220,7 @@ private static string Portion(string pattern, int beginIndex, int endIndex) return pattern.Substring(beginIndex, endIndex - beginIndex); } - private class LinearPattern : ILinearPattern + private sealed class LinearPattern : ILinearPattern { public LinearPattern(List allSegments) { @@ -240,7 +240,7 @@ public IPatternContext CreatePatternContextForExclude() } } - private class RaggedPattern : IRaggedPattern + private sealed class RaggedPattern : IRaggedPattern { public RaggedPattern(List allSegments, IList segmentsPatternStartsWith, IList segmentsPatternEndsWith, IList> segmentsPatternContains) { diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs index ce56c09bc1a72..d300666a0ae26 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/tests/FunctionalTests.cs @@ -635,9 +635,118 @@ public void StemIncludesAllSegmentsFromPatternStartingAtWildcard_TwoDirectoriesD Assert.Equal(expectedStem, actualStem); } - private List GetFileList() + [Theory] + [InlineData("/", '/')] + public void RootDir_IsPathRoot_WithInMemory_AllOS(string rootDir, char separator) + { + RootDir_IsPathRoot_WithInMemory(rootDir, separator); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData("C:\\", '\\')] + [InlineData("C:/", '/')] + public void RootDir_IsPathRoot_WithInMemory_WindowsOnly(string rootDir, char separator) { - return new List + RootDir_IsPathRoot_WithInMemory(rootDir, separator); + } + + private static void RootDir_IsPathRoot_WithInMemory(string rootDir, char separator) + { + var matcher = new Matcher(); + matcher.AddInclude($"**{separator}*.cs"); + + IEnumerable files = GetFileList(rootDir, separator); + PatternMatchingResult results = matcher.Match(rootDir, files); + + IEnumerable actual = results.Files.Select(match => match.Path); + IEnumerable expected = new string[] + { + "src/project/source1.cs", + "src/project/sub/source2.cs", + "src/project/sub/source3.cs", + "src/project/sub2/source4.cs", + "src/project/sub2/source5.cs", + "src/project/compiler/preprocess/preprocess-source1.cs", + "src/project/compiler/preprocess/sub/preprocess-source2.cs", + "src/project/compiler/preprocess/sub/sub/preprocess-source3.cs", + "src/project/compiler/shared/shared1.cs", + "src/project/compiler/shared/sub/shared2.cs", + "src/project/compiler/shared/sub/sub/sharedsub.cs", + "src/project2/source1.cs", + "src/project2/sub/source2.cs", + "src/project2/sub/source3.cs", + "src/project2/sub2/source4.cs", + "src/project2/sub2/source5.cs", + "src/project2/compiler/preprocess/preprocess-source1.cs", + "src/project2/compiler/preprocess/sub/preprocess-source2.cs", + "src/project2/compiler/preprocess/sub/sub/preprocess-source3.cs", + "src/project2/compiler/shared/shared1.cs", + "src/project2/compiler/shared/sub/shared2.cs", + "src/project2/compiler/shared/sub/sub/sharedsub.cs", + "lib/source6.cs", + "lib/sub3/source7.cs", + "lib/sub4/source8.cs", + }; + + Assert.Equal( + expected.OrderBy(e => e), + actual.OrderBy(e => e), + StringComparer.OrdinalIgnoreCase); + } + + [Theory] + [InlineData("/src/project", '/')] + [InlineData("/src/project/", '/')] + public void RootDir_IsAbsolutePath_WithInMemory_AllOS(string rootDir, char separator) + { + RootDir_IsAbsolutePath_WithInMemory(rootDir, separator); + } + + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] + [InlineData("C:\\src\\project", '\\')] + [InlineData("C:\\src\\project\\", '\\')] + [InlineData("C:/src/project", '/')] + [InlineData("C:/src/project/", '/')] + public void RootDir_IsAbsolutePath_WithInMemory_WindowsOnly(string rootDir, char separator) + { + RootDir_IsAbsolutePath_WithInMemory(rootDir, separator); + } + + private static void RootDir_IsAbsolutePath_WithInMemory(string rootDir, char separator) + { + var matcher = new Matcher(); + matcher.AddInclude($"**{separator}*.cs"); + + IEnumerable files = GetFileList(Path.GetPathRoot(rootDir), separator); + PatternMatchingResult results = matcher.Match(rootDir, files); + + IEnumerable actual = results.Files.Select(match => match.Path); + IEnumerable expected = new string[] + { + "source1.cs", + "sub/source2.cs", + "sub/source3.cs", + "sub2/source4.cs", + "sub2/source5.cs", + "compiler/preprocess/preprocess-source1.cs", + "compiler/preprocess/sub/preprocess-source2.cs", + "compiler/preprocess/sub/sub/preprocess-source3.cs", + "compiler/shared/shared1.cs", + "compiler/shared/sub/shared2.cs", + "compiler/shared/sub/sub/sharedsub.cs" + }; + + Assert.Equal( + expected.OrderBy(e => e), + actual.OrderBy(e => e), + StringComparer.OrdinalIgnoreCase); + } + + private static IEnumerable GetFileList(string rootDir = "", char directorySeparator = '/') + { + var files = new List { "root/test.0", "root/dir1/test.1", @@ -693,6 +802,8 @@ private List GetFileList() ".hidden/file1.hid", ".hidden/sub/file2.hid" }; + + return files.Select(x => (rootDir + x).Replace('/', directorySeparator)); } private DisposableFileSystem CreateContext() @@ -713,5 +824,45 @@ private void ExecuteAndVerify(Matcher matcher, string directoryPath, params stri AssertExtensions.CollectionEqual(expected, actual, StringComparer.OrdinalIgnoreCase); } + + [Fact] // https://github.com/dotnet/runtime/issues/44767 + public void VerifyAbsolutePaths_HasMatches() + { + var fileMatcher = new Matcher(); + fileMatcher.AddInclude("**/*"); + + if (PlatformDetection.IsWindows) + { + // Windows-like absolute paths are not supported on Unix. + string fakeWindowsPath = "C:\\This\\is\\a\\nested\\windows-like\\path\\somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeWindowsPath), fakeWindowsPath).HasMatches); + } + + // Unix-like absolute paths are treated as relative paths on Windows. + string fakeUnixPath = "/This/is/a/nested/unix-like/path/somefile.cs"; + Assert.True(fileMatcher.Match(Path.GetPathRoot(fakeUnixPath), fakeUnixPath).HasMatches); + } + + [Fact] // https://github.com/dotnet/runtime/issues/36415 + [ActiveIssue("https://github.com/dotnet/runtime/issues/50648")] + public void VerifyInMemoryDirectoryInfo_IsNotEmpty() + { + IEnumerable files = new[] { @"pagefile.sys" }; + InMemoryDirectoryInfo directoryInfo; + IEnumerable fileSystemInfos; + + if (PlatformDetection.IsWindows) + { + directoryInfo = new InMemoryDirectoryInfo(@"C:\", files); + fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + + Assert.Equal(1, fileSystemInfos.Count()); + } + + directoryInfo = new InMemoryDirectoryInfo("/", files); + fileSystemInfos = directoryInfo.EnumerateFileSystemInfos(); + + Assert.Equal(1, fileSystemInfos.Count()); + } } } diff --git a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs index 440444ef5073f..03df5ba943627 100644 --- a/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs +++ b/src/libraries/Microsoft.Extensions.HostFactoryResolver/src/HostFactoryResolver.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Hosting { - internal class HostFactoryResolver + internal sealed class HostFactoryResolver { private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj b/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj new file mode 100644 index 0000000000000..2fc99da91d90c --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/Microsoft.Extensions.Hosting.Systemd.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(NetCoreAppCurrent) + true + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs b/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs new file mode 100644 index 0000000000000..128858a35c338 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting.Systemd/tests/UseSystemdTests.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting.Internal; +using Xunit; + +namespace Microsoft.Extensions.Hosting +{ + public class UseSystemdTests + { + [Fact] + public void DefaultsToOffOutsideOfService() + { + var host = new HostBuilder() + .UseSystemd() + .Build(); + + using (host) + { + var lifetime = host.Services.GetRequiredService(); + Assert.IsType(lifetime); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/NuGet.config b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/NuGet.config new file mode 100644 index 0000000000000..a66b7f9b01324 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/NuGet.config @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/test/Microsoft.Extensions.Hosting.WindowsServices.Tests.csproj b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/test/Microsoft.Extensions.Hosting.WindowsServices.Tests.csproj new file mode 100644 index 0000000000000..67b1cb31ee521 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/test/Microsoft.Extensions.Hosting.WindowsServices.Tests.csproj @@ -0,0 +1,12 @@ + + + + $(NetCoreAppCurrent);net461 + true + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/test/UseWindowsServiceTests.cs b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/test/UseWindowsServiceTests.cs new file mode 100644 index 0000000000000..7e24685466d91 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/test/UseWindowsServiceTests.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting.Internal; +using Xunit; + +namespace Microsoft.Extensions.Hosting +{ + public class UseWindowsServiceTests + { + [Fact] + public void DefaultsToOffOutsideOfService() + { + var host = new HostBuilder() + .UseWindowsService() + .Build(); + + using (host) + { + var lifetime = host.Services.GetRequiredService(); + Assert.IsType(lifetime); + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs index 63c5913808ed3..1f92e812f2fc8 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs @@ -39,6 +39,7 @@ public static partial class HostingHostBuilderExtensions { public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureAppConfiguration(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureContainer(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } + public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureDefaults(this Microsoft.Extensions.Hosting.IHostBuilder builder, string[] args) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureLogging(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureLogging) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureServices(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs index 7859c873f8dc0..874abe23e4008 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.EventLog; namespace Microsoft.Extensions.Hosting { @@ -55,82 +52,8 @@ public static IHostBuilder CreateDefaultBuilder() => /// The initialized . public static IHostBuilder CreateDefaultBuilder(string[] args) { - var builder = new HostBuilder(); - - builder.UseContentRoot(Directory.GetCurrentDirectory()); - builder.ConfigureHostConfiguration(config => - { - config.AddEnvironmentVariables(prefix: "DOTNET_"); - if (args != null) - { - config.AddCommandLine(args); - } - }); - - builder.ConfigureAppConfiguration((hostingContext, config) => - { - IHostEnvironment env = hostingContext.HostingEnvironment; - - bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true); - - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange); - - if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) - { - var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); - if (appAssembly != null) - { - config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange); - } - } - - config.AddEnvironmentVariables(); - - if (args != null) - { - config.AddCommandLine(args); - } - }) - .ConfigureLogging((hostingContext, logging) => - { - bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - // IMPORTANT: This needs to be added *before* configuration is loaded, this lets - // the defaults be overridden by the configuration. - if (isWindows) - { - // Default the EventLogLoggerProvider to warning or above - logging.AddFilter(level => level >= LogLevel.Warning); - } - - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - logging.AddConsole(); - logging.AddDebug(); - logging.AddEventSourceLogger(); - - if (isWindows) - { - // Add the EventLogLoggerProvider on windows machines - logging.AddEventLog(); - } - - logging.Configure(options => - { - options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId - | ActivityTrackingOptions.TraceId - | ActivityTrackingOptions.ParentId; - }); - - }) - .UseDefaultServiceProvider((context, options) => - { - bool isDevelopment = context.HostingEnvironment.IsDevelopment(); - options.ValidateScopes = isDevelopment; - options.ValidateOnBuild = isDevelopment; - }); - - return builder; + HostBuilder builder = new(); + return builder.ConfigureDefaults(args); } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs index ecbaebcf9faa6..1cc812f6f8f19 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs @@ -30,6 +30,7 @@ public class HostBuilder : IHostBuilder private HostBuilderContext _hostBuilderContext; private HostingEnvironment _hostingEnvironment; private IServiceProvider _appServices; + private PhysicalFileProvider _defaultProvider; /// /// A central location for sharing state between components during the host building process. @@ -162,7 +163,7 @@ private void CreateHostingEnvironment() _hostingEnvironment.ApplicationName = Assembly.GetEntryAssembly()?.GetName().Name; } - _hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath); + _hostingEnvironment.ContentRootFileProvider = _defaultProvider = new PhysicalFileProvider(_hostingEnvironment.ContentRootPath); } private string ResolveContentRootPath(string contentRootPath, string basePath) @@ -219,6 +220,8 @@ private void CreateServiceProvider() services.AddSingleton(_ => { return new Internal.Host(_appServices, + _hostingEnvironment, + _defaultProvider, _appServices.GetRequiredService(), _appServices.GetRequiredService>(), _appServices.GetRequiredService(), diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index ca7e84abff8a0..28e32a4e92e45 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -3,12 +3,16 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.EventLog; namespace Microsoft.Extensions.Hosting { @@ -45,7 +49,7 @@ public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, string configBuilder.AddInMemoryCollection(new[] { new KeyValuePair(HostDefaults.ContentRootKey, - contentRoot ?? throw new ArgumentNullException(nameof(contentRoot))) + contentRoot ?? throw new ArgumentNullException(nameof(contentRoot))) }); }); } @@ -134,6 +138,105 @@ public static IHostBuilder ConfigureContainer(this IHostBuild return hostBuilder.ConfigureContainer((context, builder) => configureDelegate(builder)); } + /// + /// Configures an existing instance with pre-configured defaults. + /// + /// + /// The following defaults are applied to the : + /// + /// set the to the result of + /// load host from "DOTNET_" prefixed environment variables + /// load host from supplied command line args + /// load app from 'appsettings.json' and 'appsettings.[].json' + /// load app from User Secrets when is 'Development' using the entry assembly + /// load app from environment variables + /// load app from supplied command line args + /// configure the to log to the console, debug, and event source output + /// enables scope validation on the dependency injection container when is 'Development' + /// + /// + /// The existing builder to configure. + /// The command line args. + /// The same instance of the for chaining. + public static IHostBuilder ConfigureDefaults(this IHostBuilder builder, string[] args) + { + builder.UseContentRoot(Directory.GetCurrentDirectory()); + builder.ConfigureHostConfiguration(config => + { + config.AddEnvironmentVariables(prefix: "DOTNET_"); + if (args is { Length: > 0 }) + { + config.AddCommandLine(args); + } + }); + + builder.ConfigureAppConfiguration((hostingContext, config) => + { + IHostEnvironment env = hostingContext.HostingEnvironment; + + bool reloadOnChange = hostingContext.Configuration.GetValue("hostBuilder:reloadConfigOnChange", defaultValue: true); + + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: reloadOnChange) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: reloadOnChange); + + if (env.IsDevelopment() && env.ApplicationName is { Length: > 0 }) + { + var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); + if (appAssembly is not null) + { + config.AddUserSecrets(appAssembly, optional: true, reloadOnChange: reloadOnChange); + } + } + + config.AddEnvironmentVariables(); + + if (args is { Length: > 0 }) + { + config.AddCommandLine(args); + } + }) + .ConfigureLogging((hostingContext, logging) => + { + bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + // IMPORTANT: This needs to be added *before* configuration is loaded, this lets + // the defaults be overridden by the configuration. + if (isWindows) + { + // Default the EventLogLoggerProvider to warning or above + logging.AddFilter(level => level >= LogLevel.Warning); + } + + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + logging.AddEventSourceLogger(); + + if (isWindows) + { + // Add the EventLogLoggerProvider on windows machines + logging.AddEventLog(); + } + + logging.Configure(options => + { + options.ActivityTrackingOptions = + ActivityTrackingOptions.SpanId | + ActivityTrackingOptions.TraceId | + ActivityTrackingOptions.ParentId; + }); + + }) + .UseDefaultServiceProvider((context, options) => + { + bool isDevelopment = context.HostingEnvironment.IsDevelopment(); + options.ValidateScopes = isDevelopment; + options.ValidateOnBuild = isDevelopment; + }); + + return builder; + } + /// /// Listens for Ctrl+C or SIGTERM and calls to start the shutdown process. /// This will unblock extensions like RunAsync and WaitForShutdownAsync. diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConfigureContainerAdapter.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConfigureContainerAdapter.cs index ae974a0538f31..13f7fc3f112b5 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConfigureContainerAdapter.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConfigureContainerAdapter.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Hosting.Internal { - internal class ConfigureContainerAdapter : IConfigureContainerAdapter + internal sealed class ConfigureContainerAdapter : IConfigureContainerAdapter { private Action _action; diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs index 04b1c1b003525..ca78d42a05bc6 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs @@ -7,24 +7,35 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.Extensions.Hosting.Internal { - internal class Host : IHost, IAsyncDisposable + internal sealed class Host : IHost, IAsyncDisposable { private readonly ILogger _logger; private readonly IHostLifetime _hostLifetime; private readonly ApplicationLifetime _applicationLifetime; private readonly HostOptions _options; + private readonly IHostEnvironment _hostEnvironment; + private readonly PhysicalFileProvider _defaultProvider; private IEnumerable _hostedServices; - public Host(IServiceProvider services, IHostApplicationLifetime applicationLifetime, ILogger logger, - IHostLifetime hostLifetime, IOptions options) + public Host(IServiceProvider services, + IHostEnvironment hostEnvironment, + PhysicalFileProvider defaultProvider, + IHostApplicationLifetime applicationLifetime, + ILogger logger, + IHostLifetime hostLifetime, + IOptions options) { Services = services ?? throw new ArgumentNullException(nameof(services)); _applicationLifetime = (applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime))) as ApplicationLifetime; + _hostEnvironment = hostEnvironment; + _defaultProvider = defaultProvider; + if (_applicationLifetime is null) { throw new ArgumentException("Replacing IHostApplicationLifetime is not supported.", nameof(applicationLifetime)); @@ -131,14 +142,34 @@ public async Task StopAsync(CancellationToken cancellationToken = default) public async ValueTask DisposeAsync() { - switch (Services) + // The user didn't change the ContentRootFileProvider instance, we can dispose it + if (ReferenceEquals(_hostEnvironment.ContentRootFileProvider, _defaultProvider)) + { + // Dispose the content provider + await DisposeAsync(_hostEnvironment.ContentRootFileProvider).ConfigureAwait(false); + } + else { - case IAsyncDisposable asyncDisposable: - await asyncDisposable.DisposeAsync().ConfigureAwait(false); - break; - case IDisposable disposable: - disposable.Dispose(); - break; + // In the rare case that the user replaced the ContentRootFileProvider, dispose it and the one + // we originally created + await DisposeAsync(_hostEnvironment.ContentRootFileProvider).ConfigureAwait(false); + await DisposeAsync(_defaultProvider).ConfigureAwait(false); + } + + // Dispose the service provider + await DisposeAsync(Services).ConfigureAwait(false); + + static async ValueTask DisposeAsync(object o) + { + switch (o) + { + case IAsyncDisposable asyncDisposable: + await asyncDisposable.DisposeAsync().ConfigureAwait(false); + break; + case IDisposable disposable: + disposable.Dispose(); + break; + } } } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ServiceFactoryAdapter.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ServiceFactoryAdapter.cs index 130f5e4b2dc0a..fc738ef0bb5e9 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ServiceFactoryAdapter.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ServiceFactoryAdapter.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Hosting.Internal { - internal class ServiceFactoryAdapter : IServiceFactoryAdapter + internal sealed class ServiceFactoryAdapter : IServiceFactoryAdapter { private IServiceProviderFactory _serviceProviderFactory; private readonly Func _contextResolver; diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs b/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs index f6b2b31955440..2bfc901a918cf 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/ValidationHostedService.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.DependencyInjection { - internal class ValidationHostedService : IHostedService + internal sealed class ValidationHostedService : IHostedService { private readonly IDictionary _validators; diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs index 9701860396de4..c71d1ea5f0af8 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/ValidatorOptions.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.DependencyInjection { - internal class ValidatorOptions + internal sealed class ValidatorOptions { // Maps each options type to a method that forces its evaluation, e.g. IOptionsMonitor.Get(name) public IDictionary Validators { get; } = new Dictionary(); diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs index e4b793849acf8..5a8379f9fd287 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Hosting.Fakes; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.Extensions.Hosting.Tests @@ -115,7 +116,7 @@ public void CanConfigureAppConfigurationAndRetrieveFromDI() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CanConfigureAppConfigurationFromFile() { var hostBuilder = new HostBuilder() @@ -532,7 +533,21 @@ public void BuilderPropertiesAreAvailableInBuilderAndContext() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void DisposingHostDisposesContentFileProvider() + { + var host = new HostBuilder() + .Build(); + + var env = host.Services.GetRequiredService(); + var fileProvider = new FakeFileProvider(); + env.ContentRootFileProvider = fileProvider; + + host.Dispose(); + Assert.True(fileProvider.Disposed); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void HostServicesSameServiceProviderAsInHostBuilder() { var hostBuilder = Host.CreateDefaultBuilder(); @@ -544,6 +559,28 @@ public void HostServicesSameServiceProviderAsInHostBuilder() Assert.Same(appServicesFromHostBuilder, host.Services); } + [Fact] + public void HostBuilderConfigureDefaultsInterleavesMissingConfigValues() + { + IHostBuilder hostBuilder = new HostBuilder(); + hostBuilder.ConfigureDefaults(args: null); + + using var host = hostBuilder.Build(); + var env = host.Services.GetRequiredService(); + + var expectedContentRootPath = Directory.GetCurrentDirectory(); + Assert.Equal(expectedContentRootPath, env.ContentRootPath); + } + + private class FakeFileProvider : IFileProvider, IDisposable + { + public bool Disposed { get; private set; } + public void Dispose() => Disposed = true; + public IDirectoryContents GetDirectoryContents(string subpath) => throw new NotImplementedException(); + public IFileInfo GetFileInfo(string subpath) => throw new NotImplementedException(); + public IChangeToken Watch(string filter) => throw new NotImplementedException(); + } + private class ServiceC { public ServiceC(ServiceD serviceD) { } diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs index 2136a23ef2bd9..303e89b8e15e7 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs @@ -33,7 +33,7 @@ public async Task StopAsyncWithCancellation() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_IncludesContentRootByDefault() { var expected = Directory.GetCurrentDirectory(); @@ -46,7 +46,7 @@ public void CreateDefaultBuilder_IncludesContentRootByDefault() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_IncludesCommandLineArguments() { var expected = Directory.GetParent(Directory.GetCurrentDirectory()).FullName; // It must exist @@ -57,7 +57,7 @@ public void CreateDefaultBuilder_IncludesCommandLineArguments() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_RegistersEventSourceLogger() { var listener = new TestEventListener(); @@ -74,7 +74,7 @@ public void CreateDefaultBuilder_RegistersEventSourceLogger() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_EnablesActivityTracking() { var parentActivity = new Activity("ParentActivity"); @@ -113,7 +113,7 @@ public void CreateDefaultBuilder_EnablesActivityTracking() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_EnablesScopeValidation() { using var host = Host.CreateDefaultBuilder() @@ -128,7 +128,7 @@ public void CreateDefaultBuilder_EnablesScopeValidation() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_EnablesValidateOnBuild() { var hostBuilder = Host.CreateDefaultBuilder() @@ -142,7 +142,8 @@ public void CreateDefaultBuilder_EnablesValidateOnBuild() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/48696")] public async Task CreateDefaultBuilder_ConfigJsonDoesNotReload() { var reloadFlagConfig = new Dictionary() {{ "hostbuilder:reloadConfigOnChange", "false" }}; @@ -176,7 +177,7 @@ string SaveRandomConfig() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task CreateDefaultBuilder_ConfigJsonDoesReload() { var reloadFlagConfig = new Dictionary() { { "hostbuilder:reloadConfigOnChange", "true" } }; @@ -219,7 +220,7 @@ string SaveRandomConfig() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task CreateDefaultBuilder_SecretsDoesReload() { var secretId = Assembly.GetExecutingAssembly().GetName().Name; @@ -265,7 +266,7 @@ string SaveRandomSecret() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void CreateDefaultBuilder_RespectShutdownTimeout() { var notDefaultTimeoutSeconds = 99; diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs index e8aaff44181e2..ddcce5ce2c47b 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs @@ -25,7 +25,7 @@ public void ValidateOnStart_NullOptionsBuilder_Throws() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_ConfigureAndValidateThenCallValidateOnStart_ValidatesFailure() { var hostBuilder = CreateHostBuilder(services => @@ -48,7 +48,7 @@ public async Task ValidateOnStart_ConfigureAndValidateThenCallValidateOnStart_Va } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_CallFirstThenConfigureAndValidate_ValidatesFailure() { var hostBuilder = CreateHostBuilder(services => @@ -71,7 +71,7 @@ public async Task ValidateOnStart_CallFirstThenConfigureAndValidate_ValidatesFai } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_ErrorMessageSpecified_FailsWithCustomError() { var hostBuilder = CreateHostBuilder(services => @@ -101,7 +101,7 @@ internal class FakeSettings } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_NamedOptions_ValidatesFailureOnStart() { var hostBuilder = CreateHostBuilder(services => @@ -129,7 +129,7 @@ public async Task ValidateOnStart_NamedOptions_ValidatesFailureOnStart() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] private async Task ValidateOnStart_AddOptionsMultipleTimesForSameType_LastOneGetsTriggered() { bool firstOptionsBuilderTriggered = false; @@ -175,7 +175,7 @@ private async Task ValidateOnStart_AddOptionsMultipleTimesForSameType_LastOneGet } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] private async Task ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStartsWithNoFailure() { bool validateCalled = false; @@ -202,7 +202,7 @@ private async Task ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStar } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] private async Task ValidateOnStart_AddLazyValidation_SkipsValidationWhenHostStarts() { bool validateCalled = false; @@ -235,7 +235,7 @@ private async Task ValidateOnStart_AddLazyValidation_SkipsValidationWhenHostStar } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_AddBothLazyAndEagerValidationOnDifferentTypes_ValidatesWhenHostStartsOnlyForEagerValidations() { bool validateCalledForNested = false; @@ -278,7 +278,7 @@ public async Task ValidateOnStart_AddBothLazyAndEagerValidationOnDifferentTypes_ } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_MultipleErrorsInOneValidationCall_ValidatesFailureWithMultipleErrors() { var hostBuilder = CreateHostBuilder(services => @@ -306,7 +306,7 @@ public async Task ValidateOnStart_MultipleErrorsInOneValidationCall_ValidatesFai } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public async Task ValidateOnStart_MultipleErrorsInOneValidationCallUsingCustomErrors_FailuresContainCustomErrors() { var hostBuilder = CreateHostBuilder(services => diff --git a/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs b/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs index c99ee1f2250cd..c5f57d03453e7 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/ActiveHandlerTrackingEntry.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Http { // Thread-safety: We treat this class as immutable except for the timer. Creating a new object // for the 'expiry' pool simplifies the threading requirements significantly. - internal class ActiveHandlerTrackingEntry + internal sealed class ActiveHandlerTrackingEntry { private static readonly TimerCallback _timerCallback = (s) => ((ActiveHandlerTrackingEntry)s).Timer_Tick(); private readonly object _lock; diff --git a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs index c15bce2ba57b9..3b8f2324289b1 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DefaultHttpMessageHandlerBuilder.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Http { - internal class DefaultHttpMessageHandlerBuilder : HttpMessageHandlerBuilder + internal sealed class DefaultHttpMessageHandlerBuilder : HttpMessageHandlerBuilder { public DefaultHttpMessageHandlerBuilder(IServiceProvider services) { diff --git a/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs b/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs index 1b0f442d6091b..02215b040042f 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DefaultTypedHttpClientFactory.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Http { - internal class DefaultTypedHttpClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient> : + internal sealed class DefaultTypedHttpClientFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient> : ITypedHttpClientFactory { private readonly Cache _cache; diff --git a/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/DefaultHttpClientBuilder.cs b/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/DefaultHttpClientBuilder.cs index 9fb05bf99b400..8e1e3daf9096b 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/DefaultHttpClientBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/DefaultHttpClientBuilder.cs @@ -3,7 +3,7 @@ namespace Microsoft.Extensions.DependencyInjection { - internal class DefaultHttpClientBuilder : IHttpClientBuilder + internal sealed class DefaultHttpClientBuilder : IHttpClientBuilder { public DefaultHttpClientBuilder(IServiceCollection services, string name) { diff --git a/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientMappingRegistry.cs b/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientMappingRegistry.cs index 3737fd996eb8a..663b821a9cb5a 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientMappingRegistry.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientMappingRegistry.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.DependencyInjection // // See: https://github.com/dotnet/extensions/issues/519 // See: https://github.com/dotnet/extensions/issues/960 - internal class HttpClientMappingRegistry + internal sealed class HttpClientMappingRegistry { public Dictionary NamedClientRegistrations { get; } = new Dictionary(); } diff --git a/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs b/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs index 65537b0f96b74..b5c53f810a26c 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/ExpiredHandlerTrackingEntry.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Http { // Thread-safety: This class is immutable - internal class ExpiredHandlerTrackingEntry + internal sealed class ExpiredHandlerTrackingEntry { private readonly WeakReference _livenessTracker; diff --git a/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs b/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs index d591e59347b1c..d7537ca82a607 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/LifetimeTrackingHttpMessageHandler.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Http // This a marker used to check if the underlying handler should be disposed. HttpClients // share a reference to an instance of this class, and when it goes out of scope the inner handler // is eligible to be disposed. - internal class LifetimeTrackingHttpMessageHandler : DelegatingHandler + internal sealed class LifetimeTrackingHttpMessageHandler : DelegatingHandler { public LifetimeTrackingHttpMessageHandler(HttpMessageHandler innerHandler) : base(innerHandler) diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs index 54f917943e37c..dc92d3f659dfa 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/HttpHeadersLogValue.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Http.Logging { - internal class HttpHeadersLogValue : IReadOnlyList> + internal sealed class HttpHeadersLogValue : IReadOnlyList> { private readonly Kind _kind; private readonly Func _shouldRedactHeaderValue; diff --git a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandlerBuilderFilter.cs b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandlerBuilderFilter.cs index 1fcbd29f128b7..ebefa2047bb47 100644 --- a/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandlerBuilderFilter.cs +++ b/src/libraries/Microsoft.Extensions.Http/src/Logging/LoggingHttpMessageHandlerBuilderFilter.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Http { // Internal so we can change the requirements without breaking changes. - internal class LoggingHttpMessageHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter + internal sealed class LoggingHttpMessageHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter { private readonly ILoggerFactory _loggerFactory; private readonly IOptionsMonitor _optionsMonitor; diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs index e2d403bc7b38c..a1051db9b0189 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs @@ -94,6 +94,7 @@ public static partial class LoggerFactoryExtensions public static partial class LoggerMessage { public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } @@ -102,11 +103,17 @@ public static partial class LoggerMessage public static System.Func DefineScope(string formatString) { throw null; } public static System.Func DefineScope(string formatString) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString) { throw null; } + public static System.Action Define(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, string formatString, bool skipEnabledCheck) { throw null; } } public partial class Logger : Microsoft.Extensions.Logging.ILogger, Microsoft.Extensions.Logging.ILogger { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs index af09dfbd54425..aed4014fdc6ba 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LogValuesFormatter.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Logging /// /// Formatter to convert the named format items like {NamedformatItem} to format. /// - internal class LogValuesFormatter + internal sealed class LogValuesFormatter { private const string NullValue = "(null)"; private static readonly char[] FormatDelimiters = {',', ':'}; @@ -128,6 +128,34 @@ private static int FindIndexOfAny(string format, char[] chars, int startIndex, i } public string Format(object?[]? values) + { + object?[]? formattedValues = values; + + if (values != null) + { + for (int i = 0; i < values.Length; i++) + { + object formattedValue = FormatArgument(values[i]); + // If the formatted value is changed, we allocate and copy items to a new array to avoid mutating the array passed in to this method + if (!ReferenceEquals(formattedValue, values[i])) + { + formattedValues = new object[values.Length]; + Array.Copy(values, formattedValues, i); + formattedValues[i++] = formattedValue; + for (; i < values.Length; i++) + { + formattedValues[i] = FormatArgument(values[i]); + } + break; + } + } + } + + return string.Format(CultureInfo.InvariantCulture, _format, formattedValues ?? Array.Empty()); + } + + // NOTE: This method mutates the items in the array if needed to avoid extra allocations, and should only be used when caller expects this to happen + internal string FormatWithOverwrite(object?[]? values) { if (values != null) { diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerExternalScopeProvider.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerExternalScopeProvider.cs index 0afed527c11bc..53744eb2e76ec 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerExternalScopeProvider.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerExternalScopeProvider.cs @@ -44,7 +44,7 @@ public IDisposable Push(object? state) return newScope; } - private class Scope : IDisposable + private sealed class Scope : IDisposable { private readonly LoggerExternalScopeProvider _provider; private bool _isDisposed; diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs index 7007782a7a2a2..ee40fc116bc38 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/src/LoggerMessage.cs @@ -128,14 +128,35 @@ public static Func DefineScopeThe named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 0); + void Log(ILogger logger, Exception? exception) + { + logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); + } + + if (skipEnabledCheck) + { + return Log; + } + return (logger, exception) => { if (logger.IsEnabled(logLevel)) { - logger.Log(logLevel, eventId, new LogValues(formatter), exception, LogValues.Callback); + Log(logger, exception); } }; } @@ -149,6 +170,18 @@ public static Func DefineScopeThe named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 1); @@ -157,6 +190,11 @@ void Log(ILogger logger, T1 arg1, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, exception) => { if (logger.IsEnabled(logLevel)) @@ -176,6 +214,19 @@ void Log(ILogger logger, T1 arg1, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 2); @@ -184,6 +235,11 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, exception) => { if (logger.IsEnabled(logLevel)) @@ -204,6 +260,20 @@ void Log(ILogger logger, T1 arg1, T2 arg2, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 3); @@ -212,6 +282,11 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, exception) => { if (logger.IsEnabled(logLevel)) @@ -233,6 +308,21 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, Exception? exception) /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 4); @@ -241,6 +331,11 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4), exception, LogValues.Callback); } + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, arg4, exception) => { if (logger.IsEnabled(logLevel)) @@ -263,14 +358,40 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The type of the fifth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 5); + void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, Exception? exception) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); + } + + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, arg4, arg5, exception) => { if (logger.IsEnabled(logLevel)) { - logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5), exception, LogValues.Callback); + Log(logger, arg1, arg2, arg3, arg4, arg5, exception); } }; } @@ -289,14 +410,41 @@ void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, Exception? exceptio /// The named format string /// A delegate which when invoked creates a log message. public static Action Define(LogLevel logLevel, EventId eventId, string formatString) + => Define(logLevel, eventId, formatString, skipEnabledCheck: false); + + /// + /// Creates a delegate which can be invoked for logging a message. + /// + /// The type of the first parameter passed to the named format string. + /// The type of the second parameter passed to the named format string. + /// The type of the third parameter passed to the named format string. + /// The type of the fourth parameter passed to the named format string. + /// The type of the fifth parameter passed to the named format string. + /// The type of the sixth parameter passed to the named format string. + /// The + /// The event id + /// The named format string + /// Skips the check if the logging category is enabled. + /// A delegate which when invoked creates a log message. + public static Action Define(LogLevel logLevel, EventId eventId, string formatString, bool skipEnabledCheck) { LogValuesFormatter formatter = CreateLogValuesFormatter(formatString, expectedNamedParameterCount: 6); + void Log(ILogger logger, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, Exception? exception) + { + logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); + } + + if (skipEnabledCheck) + { + return Log; + } + return (logger, arg1, arg2, arg3, arg4, arg5, arg6, exception) => { if (logger.IsEnabled(logLevel)) { - logger.Log(logLevel, eventId, new LogValues(formatter, arg1, arg2, arg3, arg4, arg5, arg6), exception, LogValues.Callback); + Log(logger, arg1, arg2, arg3, arg4, arg5, arg6, exception); } }; } @@ -552,7 +700,7 @@ public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, private object?[] ToArray() => new object?[] { _value0, _value1, _value2, _value3 }; - public override string ToString() => _formatter.Format(ToArray()); + public override string ToString() => _formatter.FormatWithOverwrite(ToArray()); public IEnumerator> GetEnumerator() { @@ -617,7 +765,7 @@ public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, private object?[] ToArray() => new object?[] { _value0, _value1, _value2, _value3, _value4 }; - public override string ToString() => _formatter.Format(ToArray()); + public override string ToString() => _formatter.FormatWithOverwrite(ToArray()); public IEnumerator> GetEnumerator() { @@ -686,7 +834,7 @@ public LogValues(LogValuesFormatter formatter, T0 value0, T1 value1, T2 value2, private object?[] ToArray() => new object?[] { _value0, _value1, _value2, _value3, _value4, _value5 }; - public override string ToString() => _formatter.Format(ToArray()); + public override string ToString() => _formatter.FormatWithOverwrite(ToArray()); public IEnumerator> GetEnumerator() { diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerFilterConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerFilterConfigureOptions.cs index e706da9a2d0dc..bc7c220080d9d 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerFilterConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerFilterConfigureOptions.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging { - internal class LoggerFilterConfigureOptions : IConfigureOptions + internal sealed class LoggerFilterConfigureOptions : IConfigureOptions { private const string LogLevelKey = "LogLevel"; private const string DefaultCategory = "Default"; diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfiguration.cs b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfiguration.cs index e4ffdb82d5c7a..7acf6596011b1 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfiguration.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfiguration.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Logging.Configuration { - internal class LoggerProviderConfiguration : ILoggerProviderConfiguration + internal sealed class LoggerProviderConfiguration : ILoggerProviderConfiguration { public LoggerProviderConfiguration(ILoggerProviderConfigurationFactory providerConfigurationFactory) { diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigurationFactory.cs b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigurationFactory.cs index 25a1c9fc1baa8..258b3a403d22c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigurationFactory.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigurationFactory.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Configuration { - internal class LoggerProviderConfigurationFactory : ILoggerProviderConfigurationFactory + internal sealed class LoggerProviderConfigurationFactory : ILoggerProviderConfigurationFactory { private readonly IEnumerable _configurations; diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigureOptions.cs index bb6e00d25e75e..1510cc53770e0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggerProviderConfigureOptions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging.Configuration /// /// Loads settings for into type. /// - internal class LoggerProviderConfigureOptions : ConfigureFromConfigurationOptions where TOptions : class + internal sealed class LoggerProviderConfigureOptions : ConfigureFromConfigurationOptions where TOptions : class { public LoggerProviderConfigureOptions(ILoggerProviderConfiguration providerConfiguration) : base(providerConfiguration.Configuration) diff --git a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggingConfiguration.cs b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggingConfiguration.cs index d4e66315af9f9..75b518d96f345 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggingConfiguration.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Configuration/src/LoggingConfiguration.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Logging.Configuration { - internal class LoggingConfiguration + internal sealed class LoggingConfiguration { public IConfiguration Configuration { get; } diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiLogConsole.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiLogConsole.cs index 9943da694c284..6e2a8c7143ddd 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiLogConsole.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiLogConsole.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging.Console /// /// For consoles which understand the ANSI escape code sequences to represent color /// - internal class AnsiLogConsole : IConsole + internal sealed class AnsiLogConsole : IConsole { private readonly TextWriter _textWriter; diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParser.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParser.cs index bf2ee5f99c901..cf6c22f67f37a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParser.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParser.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Console { - internal class AnsiParser + internal sealed class AnsiParser { private readonly Action _onParseWrite; public AnsiParser(Action onParseWrite) diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.cs index 2b540ec86cee3..25c3218a0895d 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging.Console { [UnsupportedOSPlatform("browser")] - internal class AnsiParsingLogConsole : IConsole + internal sealed class AnsiParsingLogConsole : IConsole { private readonly TextWriter _textWriter; private readonly AnsiParser _parser; diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs index daa903da318b0..906f703a8d679 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.Console { [UnsupportedOSPlatform("browser")] - internal class ConsoleLogger : ILogger + internal sealed class ConsoleLogger : ILogger { private readonly string _name; private readonly ConsoleLoggerProcessor _queueProcessor; diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerExtensions.cs index 9d12ac5e111f4..372933b97e3d0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLoggerExtensions.cs @@ -157,7 +157,7 @@ private static ILoggingBuilder AddFormatterWithName(this ILoggingBuilder builder } [UnsupportedOSPlatform("browser")] - internal class ConsoleLoggerFormatterConfigureOptions : ConfigureFromConfigurationOptions + internal sealed class ConsoleLoggerFormatterConfigureOptions : ConfigureFromConfigurationOptions where TOptions : ConsoleFormatterOptions where TFormatter : ConsoleFormatter { @@ -168,7 +168,7 @@ public ConsoleLoggerFormatterConfigureOptions(ILoggerProviderConfiguration : ConfigurationChangeTokenSource + internal sealed class ConsoleLoggerFormatterOptionsChangeTokenSource : ConfigurationChangeTokenSource where TOptions : ConsoleFormatterOptions where TFormatter : ConsoleFormatter { diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/FormatterOptionsMonitor.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/FormatterOptionsMonitor.cs index 00adb44b4b61f..16f1d5572893c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/FormatterOptionsMonitor.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/FormatterOptionsMonitor.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.Console { - internal class FormatterOptionsMonitor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> : + internal sealed class FormatterOptionsMonitor<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> : IOptionsMonitor where TOptions : ConsoleFormatterOptions { diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs index 793294ce2a187..b92de57e31064 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs @@ -13,7 +13,7 @@ namespace Microsoft.Extensions.Logging.Console { - internal class JsonConsoleFormatter : ConsoleFormatter, IDisposable + internal sealed class JsonConsoleFormatter : ConsoleFormatter, IDisposable { private IDisposable _optionsReloadToken; @@ -109,11 +109,11 @@ private void WriteScopeInformation(Utf8JsonWriter writer, IExternalScopeProvider writer.WriteStartArray("Scopes"); scopeProvider.ForEachScope((scope, state) => { - if (scope is IEnumerable> scopes) + if (scope is IEnumerable> scopeItems) { state.WriteStartObject(); state.WriteString("Message", scope.ToString()); - foreach (KeyValuePair item in scopes) + foreach (KeyValuePair item in scopeItems) { WriteItem(state, item); } diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs index 1c790a45e730d..73a7514278f0d 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging.Console { - internal class SimpleConsoleFormatter : ConsoleFormatter, IDisposable + internal sealed class SimpleConsoleFormatter : ConsoleFormatter, IDisposable { private const string LoglevelPadding = ": "; private static readonly string _messagePadding = new string(' ', GetLogLevelString(LogLevel.Information).Length + LoglevelPadding.Length); diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs index 1916dd2fa0498..7a3e6dabb925c 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.Console { - internal class SystemdConsoleFormatter : ConsoleFormatter, IDisposable + internal sealed class SystemdConsoleFormatter : ConsoleFormatter, IDisposable { private IDisposable _optionsReloadToken; diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerExtensionsTests.cs b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerExtensionsTests.cs index 739978be7d069..15fcbf7220d47 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerExtensionsTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerExtensionsTests.cs @@ -17,6 +17,7 @@ namespace Microsoft.Extensions.Logging.Test { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49110 ", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class ConsoleLoggerExtensionsTests { [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs index ebe9985a245d2..94b3781ca4d49 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/ConsoleLoggerTest.cs @@ -1167,6 +1167,7 @@ public void ConsoleLoggerOptions_InvalidFormat_Throws() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49110 ", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ConsoleLoggerOptions_DisableColors_IsReadFromLoggingConfiguration() { var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[] { new KeyValuePair("Console:DisableColors", "true") }).Build(); @@ -1198,6 +1199,7 @@ public void ConsoleLoggerOptions_TimeStampFormat_IsReloaded() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49110 ", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ConsoleLoggerOptions_TimeStampFormat_IsReadFromLoggingConfiguration() { var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[] { new KeyValuePair("Console:TimeStampFormat", "yyyyMMddHHmmss") }).Build(); @@ -1243,6 +1245,7 @@ public void ConsoleLoggerOptions_IncludeScopes_IsAppliedToLoggers() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49110 ", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ConsoleLoggerOptions_LogAsErrorLevel_IsReadFromLoggingConfiguration() { var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[] { new KeyValuePair("Console:LogToStandardErrorThreshold", "Warning") }).Build(); @@ -1288,6 +1291,7 @@ public void ConsoleLoggerOptions_UseUtcTimestamp_IsAppliedToLoggers() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49110 ", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ConsoleLoggerOptions_IncludeScopes_IsReadFromLoggingConfiguration() { var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[] { new KeyValuePair("Console:IncludeScopes", "true") }).Build(); diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs index e4df53adbc499..f58e10b840354 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/tests/Microsoft.Extensions.Logging.Console.Tests/JsonConsoleFormatterTests.cs @@ -406,8 +406,8 @@ public static TheoryData SpecialCaseValues // Dynamic object serialized as string { new { a = 1, b = 2 }, "\"{ a = 1, b = 2 }\"" }, - // null serialized as special string - { null, "\"(null)\"" } + // null should not be serialized as special string in the state value, only in message + { null, "null" } }; return data; } diff --git a/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.cs b/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.cs index 9f7c2a5b941cf..4190843501dd2 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.Debug /// /// A logger that writes messages in the debug output window only when a debugger is attached. /// - internal partial class DebugLogger : ILogger + internal sealed partial class DebugLogger : ILogger { private readonly string _name; diff --git a/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.debug.cs b/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.debug.cs index 928bfe3ba00b1..860a2f8ccf3da 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.debug.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Debug/src/DebugLogger.debug.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Logging.Debug { - internal partial class DebugLogger + internal sealed partial class DebugLogger { private void DebugWriteLine(string message, string name) { diff --git a/src/libraries/Microsoft.Extensions.Logging.EventLog/src/EventLogLogger.cs b/src/libraries/Microsoft.Extensions.Logging.EventLog/src/EventLogLogger.cs index cd83dff8aae91..6460fb61f0a79 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventLog/src/EventLogLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging.EventLog/src/EventLogLogger.cs @@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Logging.EventLog /// /// A logger that writes messages to Windows Event Log. /// - internal class EventLogLogger : ILogger + internal sealed class EventLogLogger : ILogger { private readonly string _name; private readonly EventLogSettings _settings; diff --git a/src/libraries/Microsoft.Extensions.Logging.EventLog/src/WindowsEventLog.cs b/src/libraries/Microsoft.Extensions.Logging.EventLog/src/WindowsEventLog.cs index b148714f55289..d2b8c924470e2 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventLog/src/WindowsEventLog.cs +++ b/src/libraries/Microsoft.Extensions.Logging.EventLog/src/WindowsEventLog.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.EventLog { [SupportedOSPlatform("windows")] - internal class WindowsEventLog : IEventLog + internal sealed class WindowsEventLog : IEventLog { // https://msdn.microsoft.com/EN-US/library/windows/desktop/aa363679.aspx private const int MaximumMessageSize = 31839; diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptions.cs index ab5c6468c684c..b38fb89f78984 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptions.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging { - internal class EventLogFiltersConfigureOptions : IConfigureOptions + internal sealed class EventLogFiltersConfigureOptions : IConfigureOptions { private readonly LoggingEventSource _eventSource; diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptionsChangeSource.cs b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptionsChangeSource.cs index f5a6673f89683..687c3b5a73b60 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptionsChangeSource.cs +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventLogFiltersConfigureOptionsChangeSource.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging { - internal class EventLogFiltersConfigureOptionsChangeSource: IOptionsChangeTokenSource + internal sealed class EventLogFiltersConfigureOptionsChangeSource: IOptionsChangeTokenSource { private readonly LoggingEventSource _eventSource; diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventSourceLogger.cs b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventSourceLogger.cs index ddff58e260032..75f4481c453b4 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventSourceLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/EventSourceLogger.cs @@ -20,7 +20,7 @@ namespace Microsoft.Extensions.Logging.EventSource /// On Windows platforms EventSource will deliver messages using Event Tracing for Windows (ETW) events. /// On Linux EventSource will use LTTng (http://lttng.org) to deliver messages. /// - internal class EventSourceLogger : ILogger + internal sealed class EventSourceLogger : ILogger { private static int _activityIds; private readonly LoggingEventSource _eventSource; @@ -149,7 +149,7 @@ public IDisposable BeginScope(TState state) /// ActivityScope is just a IDisposable that knows how to send the ActivityStop event when it is /// desposed. It is part of the BeginScope() support. /// - private class ActivityScope : IDisposable + private sealed class ActivityScope : IDisposable { private readonly string _categoryName; private readonly int _activityID; diff --git a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/ExceptionInfo.cs b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/ExceptionInfo.cs index 8f8b06b4aa375..8628f6c1958c2 100644 --- a/src/libraries/Microsoft.Extensions.Logging.EventSource/src/ExceptionInfo.cs +++ b/src/libraries/Microsoft.Extensions.Logging.EventSource/src/ExceptionInfo.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.EventSource /// Represents information about exceptions that is captured by EventSourceLogger /// [System.Diagnostics.Tracing.EventData(Name ="ExceptionInfo")] - internal class ExceptionInfo + internal sealed class ExceptionInfo { public static ExceptionInfo Empty { get; } = new ExceptionInfo(); diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceLogger.cs b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceLogger.cs index e3e81d42cf976..8bda3a5080f54 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceLogger.cs +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceLogger.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Logging.TraceSource { - internal class TraceSourceLogger : ILogger + internal sealed class TraceSourceLogger : ILogger { private readonly DiagnosticsTraceSource _traceSource; diff --git a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceScope.cs b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceScope.cs index 229c84c3d72ed..80ecc8844e821 100644 --- a/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceScope.cs +++ b/src/libraries/Microsoft.Extensions.Logging.TraceSource/src/TraceSourceScope.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Logging.TraceSource /// /// Provides an IDisposable that represents a logical operation scope based on System.Diagnostics LogicalOperationStack /// - internal class TraceSourceScope : IDisposable + internal sealed class TraceSourceScope : IDisposable { // To detect redundant calls private bool _isDisposed; diff --git a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs index fafdc5319da6b..bb2d201cca320 100644 --- a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs +++ b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs @@ -23,6 +23,8 @@ public enum ActivityTrackingOptions ParentId = 4, TraceState = 8, TraceFlags = 16, + Tags = 32, + Baggage = 64 } public static partial class FilterLoggingBuilderExtensions { diff --git a/src/libraries/Microsoft.Extensions.Logging/src/ActivityTrackingOptions.cs b/src/libraries/Microsoft.Extensions.Logging/src/ActivityTrackingOptions.cs index 044bf8664d18f..062c022dbc01a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/ActivityTrackingOptions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/ActivityTrackingOptions.cs @@ -16,28 +16,38 @@ public enum ActivityTrackingOptions None = 0x0000, /// - /// Span Id wil be included in the logging. + /// Span Id will be included in the logging. /// SpanId = 0x0001, /// - /// Trace Id wil be included in the logging. + /// Trace Id will be included in the logging. /// TraceId = 0x0002, /// - /// Parent Id wil be included in the logging. + /// Parent Id will be included in the logging. /// ParentId = 0x0004, /// - /// Trace State wil be included in the logging. + /// Trace State will be included in the logging. /// TraceState = 0x0008, /// - /// Trace flags wil be included in the logging. + /// Trace flags will be included in the logging. /// - TraceFlags = 0x0010 + TraceFlags = 0x0010, + + /// + /// Tags will be included in the logging. + /// + Tags = 0x0020, + + /// + /// Items of baggage will be included in the logging. + /// + Baggage = 0x0040 } } diff --git a/src/libraries/Microsoft.Extensions.Logging/src/DefaultLoggerLevelConfigureOptions.cs b/src/libraries/Microsoft.Extensions.Logging/src/DefaultLoggerLevelConfigureOptions.cs index 16c69be547b50..a6872f800826e 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/DefaultLoggerLevelConfigureOptions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/DefaultLoggerLevelConfigureOptions.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Logging { - internal class DefaultLoggerLevelConfigureOptions : ConfigureOptions + internal sealed class DefaultLoggerLevelConfigureOptions : ConfigureOptions { public DefaultLoggerLevelConfigureOptions(LogLevel level) : base(options => options.MinLevel = level) { diff --git a/src/libraries/Microsoft.Extensions.Logging/src/Logger.cs b/src/libraries/Microsoft.Extensions.Logging/src/Logger.cs index 364acf05523e9..58352b878b748 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/Logger.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/Logger.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging { - internal class Logger : ILogger + internal sealed class Logger : ILogger { public LoggerInformation[] Loggers { get; set; } public MessageLogger[] MessageLoggers { get; set; } @@ -158,7 +158,7 @@ private static void ThrowLoggingError(List exceptions) message: "An error occurred while writing to logger(s).", innerExceptions: exceptions); } - private class Scope : IDisposable + private sealed class Scope : IDisposable { private bool _isDisposed; diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs index 2961d333b40c1..f9bac5595a1f3 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs @@ -66,7 +66,9 @@ public LoggerFactory(IEnumerable providers, IOptionsMonitor /// Default implementation of /// - internal class LoggerFactoryScopeProvider : IExternalScopeProvider + internal sealed class LoggerFactoryScopeProvider : IExternalScopeProvider { private readonly AsyncLocal _currentScope = new AsyncLocal(); private readonly ActivityTrackingOptions _activityTrackingOption; @@ -48,12 +47,47 @@ void Report(Scope current) } callback(activityLogScope, state); + + // Tags and baggage are opt-in and thus we assume that most of the time it will not be used. + if ((_activityTrackingOption & ActivityTrackingOptions.Tags) != 0 + && activity.TagObjects.GetEnumerator().MoveNext()) + { + // As TagObjects is a IEnumerable> this can be used directly as a scope. + // We do this to safe the allocation of a wrapper object. + callback(activity.TagObjects, state); + } + + if ((_activityTrackingOption & ActivityTrackingOptions.Baggage) != 0) + { + // Only access activity.Baggage as every call leads to an allocation + IEnumerable> baggage = activity.Baggage; + if (baggage.GetEnumerator().MoveNext()) + { + // For the baggage a wrapper object is necessary because we need to be able to overwrite ToString(). + // In contrast to the TagsObject, Baggage doesn't have one underlining type where we can do this overwrite. + ActivityBaggageLogScopeWrapper scope = GetOrCreateActivityBaggageLogScopeWrapper(activity, baggage); + callback(scope, state); + } + } } } Report(_currentScope.Value); } + private static ActivityBaggageLogScopeWrapper GetOrCreateActivityBaggageLogScopeWrapper(Activity activity, IEnumerable> items) + { + const string additionalItemsBaggagePropertyKey = "__ActivityBaggageItemsLogScope__"; + var activityBaggageLogScopeWrapper = activity.GetCustomProperty(additionalItemsBaggagePropertyKey) as ActivityBaggageLogScopeWrapper; + if (activityBaggageLogScopeWrapper == null) + { + activityBaggageLogScopeWrapper = new ActivityBaggageLogScopeWrapper(items); + activity.SetCustomProperty(additionalItemsBaggagePropertyKey, activityBaggageLogScopeWrapper); + } + + return activityBaggageLogScopeWrapper; + } + public IDisposable Push(object state) { Scope parent = _currentScope.Value; @@ -63,7 +97,7 @@ public IDisposable Push(object state) return newScope; } - private class Scope : IDisposable + private sealed class Scope : IDisposable { private readonly LoggerFactoryScopeProvider _provider; private bool _isDisposed; @@ -94,7 +128,7 @@ public void Dispose() } } - private class ActivityLogScope : IReadOnlyList> + private sealed class ActivityLogScope : IReadOnlyList> { private string _cachedToString; private const int MaxItems = 5; @@ -185,7 +219,59 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } } + + private sealed class ActivityBaggageLogScopeWrapper : IEnumerable> + { + private readonly IEnumerable> _items; + + private StringBuilder? _stringBuilder; + + public ActivityBaggageLogScopeWrapper (IEnumerable> items) + { + _items = items; + } + + public IEnumerator> GetEnumerator() + { + return _items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _items.GetEnumerator(); + } + + public override string ToString() + { + lock (this) + { + IEnumerator> enumerator = _items.GetEnumerator(); + if (!enumerator.MoveNext()) + { + return string.Empty; + } + + _stringBuilder ??= new StringBuilder(); + _stringBuilder.Append(enumerator.Current.Key); + _stringBuilder.Append(':'); + _stringBuilder.Append(enumerator.Current.Value); + + while (enumerator.MoveNext()) + { + _stringBuilder.Append(", "); + _stringBuilder.Append(enumerator.Current.Key); + _stringBuilder.Append(':'); + _stringBuilder.Append(enumerator.Current.Value); + } + + string result = _stringBuilder.ToString(); + _stringBuilder.Clear(); + return result; + } + } + } } + internal static class ActivityExtensions { public static string GetSpanId(this Activity activity) diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFilterOptions.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFilterOptions.cs index 68066e0c5e272..3c8165e4d2371 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFilterOptions.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFilterOptions.cs @@ -28,6 +28,9 @@ public LoggerFilterOptions() { } /// /// Gets the collection of used for filtering log messages. /// - public IList Rules { get; } = new List(); + public IList Rules => RulesInternal; + + // Concrete representation of the rule list + internal List RulesInternal { get; } = new List(); } } diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerRuleSelector.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerRuleSelector.cs index c2876ed1b6e57..a24e9838d0b53 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerRuleSelector.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerRuleSelector.cs @@ -22,7 +22,7 @@ public static void Select(LoggerFilterOptions options, Type providerType, string string providerAlias = ProviderAliasUtilities.GetAlias(providerType); LoggerFilterRule current = null; - foreach (LoggerFilterRule rule in options.Rules) + foreach (LoggerFilterRule rule in options.RulesInternal) { if (IsBetter(rule, current, providerType.FullName, category) || (!string.IsNullOrEmpty(providerAlias) && IsBetter(rule, current, providerAlias, category))) diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggingBuilder.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggingBuilder.cs index 3d450cfa33c75..fd992e4c3f3b9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggingBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggingBuilder.cs @@ -5,7 +5,7 @@ namespace Microsoft.Extensions.Logging { - internal class LoggingBuilder : ILoggingBuilder + internal sealed class LoggingBuilder : ILoggingBuilder { public LoggingBuilder(IServiceCollection services) { diff --git a/src/libraries/Microsoft.Extensions.Logging/src/StaticFilterOptionsMonitor.cs b/src/libraries/Microsoft.Extensions.Logging/src/StaticFilterOptionsMonitor.cs index 88ec849ce831a..5c3715dbfd54d 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/StaticFilterOptionsMonitor.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/StaticFilterOptionsMonitor.cs @@ -6,7 +6,7 @@ namespace Microsoft.Extensions.Logging { - internal class StaticFilterOptionsMonitor : IOptionsMonitor + internal sealed class StaticFilterOptionsMonitor : IOptionsMonitor { public StaticFilterOptionsMonitor(LoggerFilterOptions currentValue) { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs index 65355751b7a81..c20ede7d712b9 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/FormattedLogValuesTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Linq; using Xunit; @@ -94,6 +95,29 @@ public void LogValues_With_UnbalancedBraces(string format) }); } + [Fact] + public void LogValues_WithNullAndEnumerable_IsNotMutatingParameter() + { + string format = "TestMessage {Param1} {Param2} {Param3} {Param4}"; + int param1 = 1; + string param2 = null; + int[] param3 = new[] { 1, 2, 3, 4 }; + string param4 = "string"; + + var logValues = new FormattedLogValues(format, param1, param2, param3, param4); + logValues.ToString(); + + var state = logValues.ToArray(); + Assert.Equal(new[] + { + new KeyValuePair("Param1", param1), + new KeyValuePair("Param2", param2), + new KeyValuePair("Param3", param3), + new KeyValuePair("Param4", param4), + new KeyValuePair("{OriginalFormat}", format), + }, state); + } + [Fact] public void CachedFormattersAreCapped() { diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerFactoryTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerFactoryTest.cs index eb0ef289f1057..e31d7e8c26c13 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerFactoryTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerFactoryTest.cs @@ -3,7 +3,6 @@ using System; using System.Text; -using System.Globalization; using System.Diagnostics; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; @@ -82,6 +81,7 @@ private static ILoggerProvider CreateProvider() return disposableProvider.Object; } + [Fact] public void Dispose_ThrowException_SwallowsException() { @@ -181,8 +181,10 @@ public void TestActivityIds(ActivityTrackingOptions options) var logger = loggerFactory.CreateLogger("Logger"); - Activity a = new Activity("ScopeActivity"); - a.Start(); + Activity activity = new Activity("ScopeActivity"); + activity.AddBaggage("baggageTestKey1", "baggageTestValue"); + activity.AddTag("tagTestKey", "tagTestValue"); + activity.Start(); string activity1String = GetActivityLogString(options); string activity2String; @@ -199,10 +201,11 @@ public void TestActivityIds(ActivityTrackingOptions options) } b.Stop(); } - a.Stop(); + activity.Stop(); Assert.Equal(activity1String, loggerProvider.LogText[1]); Assert.Equal(activity2String, loggerProvider.LogText[4]); + Assert.Equal(7, loggerProvider.LogText.Count); // Ensure that Baggage and Tags aren't added. } [Fact] @@ -213,6 +216,195 @@ public void TestInvalidActivityTrackingOptions() ); } + [Fact] + public void TestActivityTrackingOptions_ShouldAddBaggageItemsAsNewScope_WhenBaggageOptionIsSet() + { + var loggerProvider = new ExternalScopeLoggerProvider(); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .Configure(o => o.ActivityTrackingOptions = ActivityTrackingOptions.Baggage) + .AddProvider(loggerProvider); + }); + + var logger = loggerFactory.CreateLogger("Logger"); + + Activity activity = new Activity("ScopeActivity"); + activity.AddBaggage("testKey1", null); + activity.AddBaggage("testKey2", string.Empty); + activity.AddBaggage("testKey3", "testValue"); + activity.Start(); + + logger.LogInformation("Message1"); + + activity.Stop(); + + foreach (string s in loggerProvider.LogText) + { + System.Console.WriteLine(s); + } + + Assert.Equal("Message1", loggerProvider.LogText[0]); + Assert.Equal("testKey3:testValue, testKey2:, testKey1:", loggerProvider.LogText[2]); + } + + [Fact] + public void TestActivityTrackingOptions_ShouldAddTagsAsNewScope_WhenTagsOptionIsSet() + { + var loggerProvider = new ExternalScopeLoggerProvider(); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .Configure(o => o.ActivityTrackingOptions = ActivityTrackingOptions.TraceId | ActivityTrackingOptions.Tags) + .AddProvider(loggerProvider); + }); + + var logger = loggerFactory.CreateLogger("Logger"); + + Activity activity = new Activity("ScopeActivity"); + activity.AddTag("testKey1", null); + activity.AddTag("testKey2", string.Empty); + activity.AddTag("testKey3", "testValue"); + activity.AddTag("testKey4", new Dummy()); + activity.Start(); + + logger.LogInformation("Message1"); + + activity.Stop(); + + Assert.Equal("Message1", loggerProvider.LogText[0]); + Assert.Equal("testKey1:, testKey2:, testKey3:testValue, testKey4:DummyToString", loggerProvider.LogText[2]); + } + + [Fact] + public void TestActivityTrackingOptions_ShouldAddTagsAndBaggageAsOneScopeAndTraceIdAsOtherScope_WhenTagsBaggageAndTraceIdOptionAreSet() + { + var loggerProvider = new ExternalScopeLoggerProvider(); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .Configure(o => o.ActivityTrackingOptions = ActivityTrackingOptions.TraceId | ActivityTrackingOptions.Baggage | ActivityTrackingOptions.Tags) + .AddProvider(loggerProvider); + }); + + var logger = loggerFactory.CreateLogger("Logger"); + + Activity activity = new Activity("ScopeActivity"); + activity.AddTag("testTagKey1", "testTagValue"); + activity.AddBaggage("testBaggageKey1", "testBaggageValue"); + activity.Start(); + logger.LogInformation("Message1"); + string traceIdActivityLogString = GetActivityLogString(ActivityTrackingOptions.TraceId); + activity.Stop(); + + Assert.Equal("Message1", loggerProvider.LogText[0]); + Assert.Equal(traceIdActivityLogString, loggerProvider.LogText[1]); + Assert.Equal("testTagKey1:testTagValue", loggerProvider.LogText[2]); + Assert.Equal("testBaggageKey1:testBaggageValue", loggerProvider.LogText[3]); + } + + [Fact] + public void TestActivityTrackingOptions_ShouldAddNewTagAndBaggageItemsAtRuntime_WhenTagsAndBaggageOptionAreSetAndWithNestedScopes() + { + var loggerProvider = new ExternalScopeLoggerProvider(); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .Configure(o => o.ActivityTrackingOptions = ActivityTrackingOptions.Baggage | ActivityTrackingOptions.Tags) + .AddProvider(loggerProvider); + }); + + var logger = loggerFactory.CreateLogger("Logger"); + + Activity activity = new Activity("ScopeActivity"); + activity.Start(); + + // Add baggage and tag items before the first log entry. + activity.AddTag("MyTagKey1", "1"); + activity.AddBaggage("MyBaggageKey1", "1"); + + // Log a message, this should create any cached objects. + logger.LogInformation("Message1"); + + // Start the first scope, add some more items and log. + using (logger.BeginScope("Scope1")) + { + activity.AddTag("MyTagKey2", "2"); + activity.AddBaggage("MyBaggageKey2", "2"); + logger.LogInformation("Message2"); + + // Add two additional scopes and also replace some tag and baggage items. + using (logger.BeginScope("Scope2")) + { + activity.AddTag("MyTagKey3", "3"); + activity.AddBaggage("MyBaggageKey3", "3"); + + using (logger.BeginScope("Scope3")) + { + activity.SetTag("MyTagKey3", "4"); + activity.SetBaggage("MyBaggageKey3", "4"); + logger.LogInformation("Message3"); + } + } + + // Along with this message we expect all baggage and tags items + // as well as the Scope1 but not the Scope2 and Scope3. + logger.LogInformation("Message4"); + + activity.Stop(); + } + + Assert.Equal("Message1", loggerProvider.LogText[0]); + Assert.Equal("MyTagKey1:1", loggerProvider.LogText[2]); + Assert.Equal("MyBaggageKey1:1", loggerProvider.LogText[3]); + + Assert.Equal("Message2", loggerProvider.LogText[4]); + Assert.Equal("MyTagKey1:1, MyTagKey2:2", loggerProvider.LogText[6]); + Assert.Equal("MyBaggageKey2:2, MyBaggageKey1:1", loggerProvider.LogText[7]); + Assert.Equal("Scope1", loggerProvider.LogText[8]); + + Assert.Equal("Message3", loggerProvider.LogText[9]); + Assert.Equal("MyTagKey1:1, MyTagKey2:2, MyTagKey3:4", loggerProvider.LogText[11]); + Assert.Equal("MyBaggageKey3:4, MyBaggageKey2:2, MyBaggageKey1:1", loggerProvider.LogText[12]); + Assert.Equal("Scope1", loggerProvider.LogText[13]); + Assert.Equal("Scope2", loggerProvider.LogText[14]); + Assert.Equal("Scope3", loggerProvider.LogText[15]); + + Assert.Equal("Message4", loggerProvider.LogText[16]); + Assert.Equal("MyTagKey1:1, MyTagKey2:2, MyTagKey3:4", loggerProvider.LogText[18]); + Assert.Equal("MyBaggageKey3:4, MyBaggageKey2:2, MyBaggageKey1:1", loggerProvider.LogText[19]); + Assert.Equal("Scope1", loggerProvider.LogText[20]); + } + + [Fact] + public void TestActivityTrackingOptions_ShouldNotAddAdditionalScope_WhenTagsBaggageOptionAreSetButTagsAndBaggageAreEmpty() + { + var loggerProvider = new ExternalScopeLoggerProvider(); + + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .Configure(o => o.ActivityTrackingOptions = ActivityTrackingOptions.TraceId | ActivityTrackingOptions.Baggage | ActivityTrackingOptions.Tags) + .AddProvider(loggerProvider); + }); + + var logger = loggerFactory.CreateLogger("Logger"); + + Activity activity = new Activity("ScopeActivity"); + activity.Start(); + logger.LogInformation("Message1"); + string traceIdActivityLogString = GetActivityLogString(ActivityTrackingOptions.TraceId); + activity.Stop(); + + Assert.Equal("Message1", loggerProvider.LogText[0]); + Assert.Equal(traceIdActivityLogString, loggerProvider.LogText[1]); + Assert.Equal(2, loggerProvider.LogText.Count); // Ensure that the additional scopes for tags and baggage aren't added. + } + [Fact] public void CallsSetScopeProvider_OnSupportedProviders() { @@ -367,6 +559,10 @@ public ILogger CreateLogger(string categoryName) public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { LogText.Add(formatter(state, exception)); + + // Notice that other ILoggers maybe not call "ToString()" on the scope but enumerate it and this isn't covered by this implementation. + // E.g. the SimpleConsoleFormatter calls "ToString()" like it's done here but the "JsonConsoleFormatter" enumerates a scope + // if the Scope is of type IEnumerable>. ScopeProvider.ForEachScope((scope, builder) => builder.Add(scope.ToString()), LogText); } @@ -381,6 +577,14 @@ public IDisposable BeginScope(TState state) return null; } } + + private class Dummy + { + public override string ToString() + { + return "DummyToString"; + } + } } internal static class ActivityExtensions diff --git a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs index 6b4e70a4c005d..49e2a17c54fd8 100644 --- a/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs +++ b/src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerMessageTest.cs @@ -193,6 +193,34 @@ public void LogMessages(Delegate messageDelegate, int argumentCount) Assert.Equal(expectedToString, actualLogValues.ToString()); } + [Fact] + public void LogMessage_WithNullParameter_DoesNotMutateArgument() + { + // Arrange + string format = "TestMessage {param1} {param2} {param3}"; + string param1 = "foo"; + string param2 = null; + int param3 = 10; + var testSink = new TestSink(); + var testLogger = new TestLogger("testlogger", testSink, enabled: true); + + // Act + testLogger.LogInformation(format, param1, param2, param3); + + // Assert + Assert.Single(testSink.Writes); + var write = testSink.Writes.First(); + var actualLogValues = Assert.IsAssignableFrom>>(write.State); + AssertLogValues(new[] + { + new KeyValuePair("param1", param1), + new KeyValuePair("param2", param2), + new KeyValuePair("param3", param3), + new KeyValuePair("{OriginalFormat}", format) + }, + actualLogValues.ToArray()); + } + [Fact] public void DefineMessage_WithNoParameters_ThrowsException_WhenFormatString_HasNamedParameters() { diff --git a/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs b/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs index 35c383df37c63..f2dcad253fc1c 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs +++ b/src/libraries/Microsoft.Extensions.Options/src/OptionsFactory.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Microsoft.Extensions.Options { @@ -15,9 +16,9 @@ public class OptionsFactory<[DynamicallyAccessedMembers(Options.DynamicallyAcces IOptionsFactory where TOptions : class { - private readonly IEnumerable> _setups; - private readonly IEnumerable> _postConfigures; - private readonly IEnumerable> _validations; + private readonly IConfigureOptions[] _setups; + private readonly IPostConfigureOptions[] _postConfigures; + private readonly IValidateOptions[] _validations; /// /// Initializes a new instance with the specified options configurations. @@ -35,9 +36,9 @@ public OptionsFactory(IEnumerable> setups, IEnumerab /// The validations to run. public OptionsFactory(IEnumerable> setups, IEnumerable> postConfigures, IEnumerable> validations) { - _setups = setups; - _postConfigures = postConfigures; - _validations = validations; + _setups = setups as IConfigureOptions[] ?? setups.ToArray(); + _postConfigures = postConfigures as IPostConfigureOptions[] ?? postConfigures.ToArray(); + _validations = validations as IValidateOptions[] ?? validations.ToArray(); } /// diff --git a/src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs b/src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs index 7c43e2864cdf6..06e5156a83604 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs +++ b/src/libraries/Microsoft.Extensions.Options/src/OptionsMonitor.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Microsoft.Extensions.Primitives; namespace Microsoft.Extensions.Options @@ -19,7 +20,6 @@ public class OptionsMonitor<[DynamicallyAccessedMembers(Options.DynamicallyAcces { private readonly IOptionsMonitorCache _cache; private readonly IOptionsFactory _factory; - private readonly IEnumerable> _sources; private readonly List _registrations = new List(); internal event Action _onChange; @@ -32,10 +32,9 @@ public class OptionsMonitor<[DynamicallyAccessedMembers(Options.DynamicallyAcces public OptionsMonitor(IOptionsFactory factory, IEnumerable> sources, IOptionsMonitorCache cache) { _factory = factory; - _sources = sources; _cache = cache; - foreach (IOptionsChangeTokenSource source in _sources) + foreach (IOptionsChangeTokenSource source in (sources as IOptionsChangeTokenSource[] ?? sources.ToArray())) { IDisposable registration = ChangeToken.OnChange( () => source.GetChangeToken(), @@ -100,7 +99,7 @@ public void Dispose() _registrations.Clear(); } - internal class ChangeTrackerDisposable : IDisposable + internal sealed class ChangeTrackerDisposable : IDisposable { private readonly Action _listener; private readonly OptionsMonitor _monitor; diff --git a/src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs b/src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs index d9ff1e75b035b..cfba973590744 100644 --- a/src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Options/src/OptionsServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ public static IServiceCollection AddOptions(this IServiceCollection services) throw new ArgumentNullException(nameof(services)); } - services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>))); + services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(UnnamedOptionsManager<>))); services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>))); services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptionsMonitor<>), typeof(OptionsMonitor<>))); services.TryAdd(ServiceDescriptor.Transient(typeof(IOptionsFactory<>), typeof(OptionsFactory<>))); diff --git a/src/libraries/Microsoft.Extensions.Options/src/UnnamedOptionsManager.cs b/src/libraries/Microsoft.Extensions.Options/src/UnnamedOptionsManager.cs new file mode 100644 index 0000000000000..93dafb73f6bd7 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.Options/src/UnnamedOptionsManager.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Threading; + +namespace Microsoft.Extensions.Options +{ + internal sealed class UnnamedOptionsManager<[DynamicallyAccessedMembers(Options.DynamicallyAccessedMembers)] TOptions> : + IOptions + where TOptions : class + { + private readonly IOptionsFactory _factory; + private volatile object _syncObj; + private volatile TOptions _value; + + public UnnamedOptionsManager(IOptionsFactory factory) => _factory = factory; + + public TOptions Value + { + get + { + if (_value is TOptions value) + { + return value; + } + + lock (_syncObj ?? Interlocked.CompareExchange(ref _syncObj, new object(), null) ?? _syncObj) + { + return _value ??= _factory.Create(Options.DefaultName); + } + } + } + } +} diff --git a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs index 9e44a897d2332..98e0f35d0bc21 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsBuilderTest.cs @@ -12,6 +12,7 @@ namespace Microsoft.Extensions.Options.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class OptionsBuilderTest { [Fact] @@ -667,7 +668,7 @@ public void CanValidateMixDataAnnotations() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void ValidateOnStart_CallValidateDataAnnotations_ValidationSuccessful() { var services = new ServiceCollection(); @@ -694,7 +695,7 @@ public void ValidateOnStart_CallValidateDataAnnotations_ValidationSuccessful() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void ValidateOnStart_CallValidateAndValidateDataAnnotations_FailuresCaughtFromBothValidateAndValidateDataAnnotations() { var services = new ServiceCollection(); @@ -724,7 +725,7 @@ public void ValidateOnStart_CallValidateAndValidateDataAnnotations_FailuresCaugh } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void ValidateOnStart_CallValidateOnStartFirst_ValidatesFailuresCorrectly() { var services = new ServiceCollection(); @@ -754,7 +755,7 @@ public void ValidateOnStart_CallValidateOnStartFirst_ValidatesFailuresCorrectly( } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34580", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void ValidateOnStart_ConfigureBasedOnDataAnnotationRestrictions_ValidationSuccessful() { var services = new ServiceCollection(); diff --git a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs index 5c09fe939752b..061388035ebc9 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsFactoryTests.cs @@ -7,6 +7,7 @@ namespace Microsoft.Extensions.Options.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class OptionsFactoryTest { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsMonitorTest.cs b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsMonitorTest.cs index 858b90dccd7ee..f253d1efd251a 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsMonitorTest.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsMonitorTest.cs @@ -11,6 +11,7 @@ namespace Microsoft.Extensions.Options.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class OptionsMonitorTest { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsSnapshotTest.cs b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsSnapshotTest.cs index adb63a5c6913c..0dfa2c2095c02 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsSnapshotTest.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsSnapshotTest.cs @@ -10,6 +10,7 @@ namespace Microsoft.Extensions.Options.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class OptionsSnapshotTest { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsTest.cs b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsTest.cs index bb76bb9d7fc21..7f5ed8fc052a7 100644 --- a/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsTest.cs +++ b/src/libraries/Microsoft.Extensions.Options/tests/Microsoft.Extensions.Options.Tests/OptionsTest.cs @@ -12,6 +12,7 @@ namespace Microsoft.Extensions.Options.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class OptionsTest { [Fact] diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/CancellationChangeToken.cs b/src/libraries/Microsoft.Extensions.Primitives/src/CancellationChangeToken.cs index 2e6ffb529cf05..f66878f9e8eb9 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/CancellationChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.Primitives/src/CancellationChangeToken.cs @@ -71,7 +71,7 @@ public IDisposable RegisterChangeCallback(Action callback, object state) return NullDisposable.Instance; } - private class NullDisposable : IDisposable + private sealed class NullDisposable : IDisposable { public static readonly NullDisposable Instance = new NullDisposable(); diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs b/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs index 25298b7de4aa8..b5df90f760537 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.Primitives/src/ChangeToken.cs @@ -53,7 +53,7 @@ public static IDisposable OnChange(Func changeTokenProduce return new ChangeTokenRegistration(changeTokenProducer, changeTokenConsumer, state); } - private class ChangeTokenRegistration : IDisposable + private sealed class ChangeTokenRegistration : IDisposable { private readonly Func _changeTokenProducer; private readonly Action _changeTokenConsumer; @@ -145,7 +145,7 @@ public void Dispose() Interlocked.Exchange(ref _disposable, _disposedSentinel).Dispose(); } - private class NoopDisposable : IDisposable + private sealed class NoopDisposable : IDisposable { public void Dispose() { diff --git a/src/libraries/Microsoft.Extensions.Primitives/src/StringValues.cs b/src/libraries/Microsoft.Extensions.Primitives/src/StringValues.cs index 1c88bfbd05a9e..25c586d9a9e97 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/src/StringValues.cs +++ b/src/libraries/Microsoft.Extensions.Primitives/src/StringValues.cs @@ -419,7 +419,7 @@ IEnumerator IEnumerable.GetEnumerator() /// Indicates whether the specified contains no string values. /// /// The to test. - /// true if value contains a single null string or empty array; otherwise, false. + /// true if value contains a single null or empty string or an empty array; otherwise, false. public static bool IsNullOrEmpty(StringValues value) { object data = value._values; diff --git a/src/libraries/Microsoft.Extensions.Primitives/tests/CompositeChangeTokenTest.cs b/src/libraries/Microsoft.Extensions.Primitives/tests/CompositeChangeTokenTest.cs index 07bb99b3ffc3f..549cfcc6138e3 100644 --- a/src/libraries/Microsoft.Extensions.Primitives/tests/CompositeChangeTokenTest.cs +++ b/src/libraries/Microsoft.Extensions.Primitives/tests/CompositeChangeTokenTest.cs @@ -103,6 +103,7 @@ public void ActiveChangeCallbacks_IsFalse_IfNoTokenHasActiveChangeCallbacks() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public async Task RegisteredCallbackGetsInvokedExactlyOnce_WhenMultipleConcurrentChangeEventsOccur() { // Arrange diff --git a/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx b/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx index d5c13cece767c..f84023f233d3f 100644 --- a/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.IO.Redist/src/Resources/Strings.resx @@ -114,6 +114,9 @@ The path is empty. - Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + Drive name must be a root directory (i.e. 'C:\') or a drive letter ('C'). + + + Non-negative number required. diff --git a/src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln b/src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln new file mode 100644 index 0000000000000..a31d754662018 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/Microsoft.NETCore.Platforms.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31004.24 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NETCore.Platforms", "src\Microsoft.NETCore.Platforms.csproj", "{BFFF96CC-06AA-4291-9F93-3E77F23DBB11}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NETCore.Platforms.Tests", "tests\Microsoft.NETCore.Platforms.Tests.csproj", "{0C60F372-5C73-4BFA-9B91-5659C88F9750}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFFF96CC-06AA-4291-9F93-3E77F23DBB11}.Release|Any CPU.Build.0 = Release|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C60F372-5C73-4BFA-9B91-5659C88F9750}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E946A528-C3E7-48EC-AD6D-AE84ED2B11AC} + EndGlobalSection +EndGlobal diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj b/src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj deleted file mode 100644 index e24d21ae013bb..0000000000000 --- a/src/libraries/Microsoft.NETCore.Platforms/pkg/Microsoft.NETCore.Platforms.pkgproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - true - - false - Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages. - - - - - - lib/netstandard1.0 - - - - - diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs b/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs new file mode 100644 index 0000000000000..a0f4aaad29bc0 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + /// + /// Used to enable app-local assembly unification. + /// + internal static class AssemblyResolver + { + static AssemblyResolver() + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + } + + /// + /// Call to enable the assembly resolver for the current AppDomain. + /// + public static void Enable() + { + // intentionally empty. This is just meant to ensure the static constructor + // has run. + } + + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // apply any existing policy + AssemblyName referenceName = new AssemblyName(AppDomain.CurrentDomain.ApplyPolicy(args.Name)); + + string fileName = referenceName.Name + ".dll"; + string assemblyPath = null; + string probingPath = null; + Assembly assm = null; + + // look next to requesting assembly + assemblyPath = args.RequestingAssembly?.Location; + if (!string.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look next to the executing assembly + assemblyPath = Assembly.GetExecutingAssembly().Location; + if (!string.IsNullOrEmpty(assemblyPath)) + { + probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName); + + Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + } + + // look in AppDomain base directory + probingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + Debug.WriteLine($"Considering {probingPath} based on BaseDirectory"); + if (Probe(probingPath, referenceName.Version, out assm)) + { + return assm; + } + + // look in current directory + Debug.WriteLine($"Considering {fileName}"); + if (Probe(fileName, referenceName.Version, out assm)) + { + return assm; + } + + return null; + } + + /// + /// Considers a path to load for satisfying an assembly ref and loads it + /// if the file exists and version is sufficient. + /// + /// Path to consider for load + /// Minimum version to consider + /// loaded assembly + /// true if assembly was loaded + private static bool Probe(string filePath, Version minimumVersion, out Assembly assembly) + { + if (File.Exists(filePath)) + { + AssemblyName name = AssemblyName.GetAssemblyName(filePath); + + if (name.Version >= minimumVersion) + { + assembly = Assembly.Load(name); + return true; + } + } + + assembly = null; + return false; + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs new file mode 100644 index 0000000000000..29af5f186cfb4 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.Desktop.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public partial class BuildTask + { + static BuildTask() + { + AssemblyResolver.Enable(); + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs new file mode 100644 index 0000000000000..fa7dd7edabda9 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/BuildTask.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public abstract partial class BuildTask : ITask + { + private Log _log; + + internal Log Log + { + get { return _log ?? (_log = new Log(new TaskLoggingHelper(this))); } + } + + public BuildTask() + { + } + + public IBuildEngine BuildEngine + { + get; + set; + } + + public ITaskHost HostObject + { + get; + set; + } + + public abstract bool Execute(); + } + + internal class Log : ILog + { + private readonly TaskLoggingHelper _logger; + public Log(TaskLoggingHelper logger) + { + _logger = logger; + } + + public void LogError(string message, params object[] messageArgs) + { + _logger.LogError(message, messageArgs); + } + + public void LogErrorFromException(Exception exception, bool showStackTrace) + { + _logger.LogErrorFromException(exception, showStackTrace); + } + + public void LogMessage(string message, params object[] messageArgs) + { + _logger.LogMessage(message, messageArgs); + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + _logger.LogMessage((MessageImportance)importance, message, messageArgs); + } + + public void LogWarning(string message, params object[] messageArgs) + { + _logger.LogWarning(message, messageArgs); + } + + public bool HasLoggedErrors { get { return _logger.HasLoggedErrors; } } + } + + public enum LogImportance + { + Low = MessageImportance.Low, + Normal = MessageImportance.Normal, + High = MessageImportance.High + } + + + public interface ILog + { + // + // Summary: + // Logs an error with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogError(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(string message, params object[] messageArgs); + + // + // Summary: + // Logs a message with the specified string and importance. + // + // Parameters: + // importance: + // One of the enumeration values that specifies the importance of the message. + // + // message: + // The message. + // + // messageArgs: + // The arguments for formatting the message. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogMessage(LogImportance importance, string message, params object[] messageArgs); + + // + // Summary: + // Logs a warning with the specified message. + // + // Parameters: + // message: + // The message. + // + // messageArgs: + // Optional arguments for formatting the message string. + // + // Exceptions: + // T:System.ArgumentNullException: + // message is null. + void LogWarning(string message, params object[] messageArgs); + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props b/src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props new file mode 100644 index 0000000000000..ea706165b9f58 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Directory.Build.props @@ -0,0 +1,6 @@ + + + false + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs b/src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs new file mode 100644 index 0000000000000..128b56082e1fe --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Extensions.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public static class Extensions + { + public static string GetString(this ITaskItem taskItem, string metadataName) + { + var metadataValue = taskItem.GetMetadata(metadataName)?.Trim(); + return string.IsNullOrEmpty(metadataValue) ? null : metadataValue; + } + + public static bool GetBoolean(this ITaskItem taskItem, string metadataName, bool defaultValue = false) + { + bool result = false; + var metadataValue = taskItem.GetMetadata(metadataName); + if (!bool.TryParse(metadataValue, out result)) + { + result = defaultValue; + } + return result; + } + + public static IEnumerable GetStrings(this ITaskItem taskItem, string metadataName) + { + var metadataValue = taskItem.GetMetadata(metadataName)?.Trim(); + if (!string.IsNullOrEmpty(metadataValue)) + { + return metadataValue.Split(';').Where(v => !string.IsNullOrEmpty(v.Trim())).ToArray(); + } + + return Enumerable.Empty(); + } + + public static IEnumerable NullAsEmpty(this IEnumerable source) + { + return source ?? Enumerable.Empty(); + } + + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs b/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs new file mode 100644 index 0000000000000..cbee78b71a6b0 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/GenerateRuntimeGraph.cs @@ -0,0 +1,392 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using Microsoft.Build.Framework; +using Newtonsoft.Json; +using NuGet.RuntimeModel; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + public class GenerateRuntimeGraph : BuildTask + { + + /// + /// A set of RuntimeGroups that can be used to generate a runtime graph + /// Identity: the base string for the RID, without version architecture, or qualifiers. + /// Parent: the base string for the parent of this RID. This RID will be imported by the baseRID, architecture-specific, + /// and qualifier-specific RIDs (with the latter two appending appropriate architecture and qualifiers). + /// Versions: A list of strings delimited by semi-colons that represent the versions for this RID. + /// TreatVersionsAsCompatible: Default is true. When true, version-specific RIDs will import the previous + /// version-specific RID in the Versions list, with the first version importing the version-less RID. + /// When false all version-specific RIDs will import the version-less RID (bypassing previous version-specific RIDs) + /// OmitVersionDelimiter: Default is false. When true no characters will separate the base RID and version (EG: win7). + /// When false a '.' will separate the base RID and version (EG: osx.10.12). + /// ApplyVersionsToParent: Default is false. When true, version-specific RIDs will import version-specific Parent RIDs + /// similar to is done for architecture and qualifier (see Parent above). + /// Architectures: A list of strings delimited by semi-colons that represent the architectures for this RID. + /// AdditionalQualifiers: A list of strings delimited by semi-colons that represent the additional qualifiers for this RID. + /// Additional qualifers do not stack, each only applies to the qualifier-less RIDs (so as not to cause combinatorial + /// exponential growth of RIDs). + /// + /// The following options can be used under special circumstances but break the normal precedence rules we try to establish + /// by generating the RID graph from common logic. These options make it possible to create a RID fallback chain that doesn't + /// match the rest of the RIDs and therefore is hard for developers/package authors to reason about. + /// Only use these options for cases where you know what you are doing and have carefully reviewed the resulting RID fallbacks + /// using the CompatibliltyMap. + /// OmitRIDs: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup that should + /// be omitted from the RuntimeGraph. These RIDs will not be referenced nor defined. + /// OmitRIDDefinitions: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup + /// that should be omitted from the RuntimeGraph. These RIDs will not be defined by this RuntimeGroup, but will be + /// referenced: useful in case some other RuntimeGroup (or runtime.json template) defines them. + /// OmitRIDReferences: A list of strings delimited by semi-colons that represent RIDs calculated from this RuntimeGroup + /// that should be omitted from the RuntimeGraph. These RIDs will be defined but not referenced by this RuntimeGroup. + /// + public ITaskItem[] RuntimeGroups + { + get; + set; + } + + /// + /// Optional source Runtime.json to use as a starting point when merging additional RuntimeGroups + /// + public string SourceRuntimeJson + { + get; + set; + } + + /// + /// Where to write the final runtime.json + /// + public string RuntimeJson + { + get; + set; + } + + /// + /// Optionally, other runtime.jsons which may contain imported RIDs + /// + public string[] ExternalRuntimeJsons + { + get; + set; + } + + /// + /// When defined, specifies the file to write compatibility precedence for each RID in the graph. + /// + public string CompatibilityMap + { + get; + set; + } + + + /// + /// True to write the generated runtime.json to RuntimeJson and compatibility map to CompatibilityMap, otherwise files are read and diffed + /// with generated versions and an error is emitted if they differ. + /// Setting UpdateRuntimeFiles will overwrite files even when the file is marked ReadOnly. + /// + public bool UpdateRuntimeFiles + { + get; + set; + } + + /// + /// When defined, specifies the file to write a DGML representation of the runtime graph. + /// + public string RuntimeDirectedGraph + { + get; + set; + } + + public override bool Execute() + { + if (RuntimeGroups != null && RuntimeGroups.Any() && RuntimeJson == null) + { + Log.LogError($"{nameof(RuntimeJson)} argument must be specified when {nameof(RuntimeGroups)} is specified."); + return false; + } + + RuntimeGraph runtimeGraph; + if (!string.IsNullOrEmpty(SourceRuntimeJson)) + { + if (!File.Exists(SourceRuntimeJson)) + { + Log.LogError($"{nameof(SourceRuntimeJson)} did not exist at {SourceRuntimeJson}."); + return false; + } + + runtimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(SourceRuntimeJson); + } + else + { + runtimeGraph = new RuntimeGraph(); + } + + foreach (var runtimeGroup in RuntimeGroups.NullAsEmpty().Select(i => new RuntimeGroup(i))) + { + runtimeGraph = SafeMerge(runtimeGraph, runtimeGroup); + } + + Dictionary externalRids = new Dictionary(); + if (ExternalRuntimeJsons != null) + { + foreach (var externalRuntimeJson in ExternalRuntimeJsons) + { + RuntimeGraph externalRuntimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(externalRuntimeJson); + + foreach (var runtime in externalRuntimeGraph.Runtimes.Keys) + { + // don't check for duplicates, we merely care what is external + externalRids.Add(runtime, externalRuntimeJson); + } + } + } + + ValidateImports(runtimeGraph, externalRids); + + if (!string.IsNullOrEmpty(RuntimeJson)) + { + if (UpdateRuntimeFiles) + { + EnsureWritable(RuntimeJson); + WriteRuntimeGraph(RuntimeJson, runtimeGraph); + + } + else + { + // validate that existing file matches generated file + if (!File.Exists(RuntimeJson)) + { + Log.LogError($"{nameof(RuntimeJson)} did not exist at {RuntimeJson} and {nameof(UpdateRuntimeFiles)} was not specified."); + } + else + { + var existingRuntimeGraph = JsonRuntimeFormat.ReadRuntimeGraph(RuntimeJson); + + if (!existingRuntimeGraph.Equals(runtimeGraph)) + { + Log.LogError($"The generated {nameof(RuntimeJson)} differs from {RuntimeJson} and {nameof(UpdateRuntimeFiles)} was not specified. Please specify {nameof(UpdateRuntimeFiles)}=true to commit the changes."); + } + } + } + } + + if (!string.IsNullOrEmpty(CompatibilityMap)) + { + var compatibilityMap = GetCompatibilityMap(runtimeGraph); + if (UpdateRuntimeFiles) + { + EnsureWritable(CompatibilityMap); + WriteCompatibilityMap(compatibilityMap, CompatibilityMap); + } + else + { + // validate that existing file matches generated file + if (!File.Exists(CompatibilityMap)) + { + Log.LogError($"{nameof(CompatibilityMap)} did not exist at {CompatibilityMap} and {nameof(UpdateRuntimeFiles)} was not specified."); + } + else + { + var existingCompatibilityMap = ReadCompatibilityMap(CompatibilityMap); + + if (!CompatibilityMapEquals(existingCompatibilityMap, compatibilityMap)) + { + Log.LogError($"The generated {nameof(CompatibilityMap)} differs from {CompatibilityMap} and {nameof(UpdateRuntimeFiles)} was not specified. Please specify {nameof(UpdateRuntimeFiles)}=true to commit the changes."); + } + } + } + } + + if (!string.IsNullOrEmpty(RuntimeDirectedGraph)) + { + WriteRuntimeGraph(runtimeGraph, RuntimeDirectedGraph); + } + + return !Log.HasLoggedErrors; + } + + private void EnsureWritable(string file) + { + if (File.Exists(file)) + { + var existingAttributes = File.GetAttributes(file); + + if ((existingAttributes & FileAttributes.ReadOnly) != 0) + { + File.SetAttributes(file, existingAttributes &= ~FileAttributes.ReadOnly); + } + } + } + + public static void WriteRuntimeGraph(string filePath, RuntimeGraph runtimeGraph) + { + using (var fileStream = new FileStream(filePath, FileMode.Create)) + using (var textWriter = new StreamWriter(fileStream)) + using (var jsonWriter = new JsonTextWriter(textWriter)) + using (var writer = new JsonObjectWriter(jsonWriter)) + { + jsonWriter.Formatting = Formatting.Indented; + + // workaround https://github.com/NuGet/Home/issues/9532 + writer.WriteObjectStart(); + + JsonRuntimeFormat.WriteRuntimeGraph(writer, runtimeGraph); + + writer.WriteObjectEnd(); + } + } + + private RuntimeGraph SafeMerge(RuntimeGraph existingGraph, RuntimeGroup runtimeGroup) + { + var runtimeGraph = runtimeGroup.GetRuntimeGraph(); + + foreach (var existingRuntimeDescription in existingGraph.Runtimes.Values) + { + RuntimeDescription newRuntimeDescription; + + if (runtimeGraph.Runtimes.TryGetValue(existingRuntimeDescription.RuntimeIdentifier, out newRuntimeDescription)) + { + // overlapping RID, ensure that the imports match (same ordering and content) + if (!existingRuntimeDescription.InheritedRuntimes.SequenceEqual(newRuntimeDescription.InheritedRuntimes)) + { + Log.LogError($"RuntimeGroup {runtimeGroup.BaseRID} defines RID {newRuntimeDescription.RuntimeIdentifier} with imports {string.Join(";", newRuntimeDescription.InheritedRuntimes)} which differ from existing imports {string.Join(";", existingRuntimeDescription.InheritedRuntimes)}. You may avoid this by specifying {nameof(RuntimeGroup.OmitRIDDefinitions)} metadata with {newRuntimeDescription.RuntimeIdentifier}."); + } + } + } + + return RuntimeGraph.Merge(existingGraph, runtimeGraph); + } + + private void ValidateImports(RuntimeGraph runtimeGraph, IDictionary externalRIDs) + { + foreach (var runtimeDescription in runtimeGraph.Runtimes.Values) + { + string externalRuntimeJson; + + if (externalRIDs.TryGetValue(runtimeDescription.RuntimeIdentifier, out externalRuntimeJson)) + { + Log.LogError($"Runtime {runtimeDescription.RuntimeIdentifier} is defined in both this RuntimeGraph and {externalRuntimeJson}."); + } + + foreach (var import in runtimeDescription.InheritedRuntimes) + { + if (!runtimeGraph.Runtimes.ContainsKey(import) && !externalRIDs.ContainsKey(import)) + { + Log.LogError($"Runtime {runtimeDescription.RuntimeIdentifier} imports {import} which is not defined."); + } + } + } + } + + private static IDictionary> GetCompatibilityMap(RuntimeGraph graph) + { + Dictionary> compatibilityMap = new Dictionary>(); + + foreach (var rid in graph.Runtimes.Keys.OrderBy(rid => rid, StringComparer.Ordinal)) + { + compatibilityMap.Add(rid, graph.ExpandRuntime(rid)); + } + + return compatibilityMap; + } + + private static IDictionary> ReadCompatibilityMap(string mapFile) + { + var serializer = new JsonSerializer(); + using (var file = File.OpenText(mapFile)) + using (var jsonTextReader = new JsonTextReader(file)) + { + return serializer.Deserialize>>(jsonTextReader); + } + } + + private static void WriteCompatibilityMap(IDictionary> compatibilityMap, string mapFile) + { + var serializer = new JsonSerializer() + { + Formatting = Formatting.Indented, + StringEscapeHandling = StringEscapeHandling.EscapeNonAscii + }; + + string directory = Path.GetDirectoryName(mapFile); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + using (var file = File.CreateText(mapFile)) + { + serializer.Serialize(file, compatibilityMap); + } + } + + private static bool CompatibilityMapEquals(IDictionary> left, IDictionary> right) + { + if (left.Count != right.Count) + { + return false; + } + + foreach (var leftPair in left) + { + IEnumerable rightValue; + + if (!right.TryGetValue(leftPair.Key, out rightValue)) + { + return false; + } + + if (!rightValue.SequenceEqual(leftPair.Value)) + { + return false; + } + } + + return true; + } + + private static XNamespace s_dgmlns = @"http://schemas.microsoft.com/vs/2009/dgml"; + private static void WriteRuntimeGraph(RuntimeGraph graph, string dependencyGraphFilePath) + { + + var doc = new XDocument(new XElement(s_dgmlns + "DirectedGraph")); + var nodesElement = new XElement(s_dgmlns + "Nodes"); + var linksElement = new XElement(s_dgmlns + "Links"); + doc.Root.Add(nodesElement); + doc.Root.Add(linksElement); + + var nodeIds = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var runtimeDescription in graph.Runtimes.Values) + { + nodesElement.Add(new XElement(s_dgmlns + "Node", + new XAttribute("Id", runtimeDescription.RuntimeIdentifier))); + + foreach (var import in runtimeDescription.InheritedRuntimes) + { + linksElement.Add(new XElement(s_dgmlns + "Link", + new XAttribute("Source", runtimeDescription.RuntimeIdentifier), + new XAttribute("Target", import))); + } + } + + using (var file = File.Create(dependencyGraphFilePath)) + { + doc.Save(file); + } + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index 987d53fcabe74..3c00187ddb18e 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -1,9 +1,42 @@ - + - netstandard1.0 - true - - false + $(NetCoreAppToolCurrent);net472 + $(MSBuildProjectName) + true + Microsoft.NETCore.Platforms.BuildTasks + + false + true + false + true + $(NoWarn);NU5128 + Provides runtime information required to resolve target framework, platform, and runtime specific implementations of .NETCore packages. + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/RID.cs b/src/libraries/Microsoft.NETCore.Platforms/src/RID.cs new file mode 100644 index 0000000000000..357790fbfdd54 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/RID.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + internal class RID + { + public string BaseRID { get; set; } + public string VersionDelimiter { get; set; } + public string Version { get; set; } + public string ArchitectureDelimiter { get; set; } + public string Architecture { get; set; } + public string QualifierDelimiter { get; set; } + public string Qualifier { get; set; } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(BaseRID); + + if (HasVersion()) + { + builder.Append(VersionDelimiter); + builder.Append(Version); + } + + if (HasArchitecture()) + { + builder.Append(ArchitectureDelimiter); + builder.Append(Architecture); + } + + if (HasQualifier()) + { + builder.Append(QualifierDelimiter); + builder.Append(Qualifier); + } + + return builder.ToString(); + } + + public bool HasVersion() + { + return Version != null; + } + + public bool HasArchitecture() + { + return Architecture != null; + } + + public bool HasQualifier() + { + return Qualifier != null; + } + } + +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs b/src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs new file mode 100644 index 0000000000000..2ddd60022c426 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/src/RuntimeGroup.cs @@ -0,0 +1,264 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +using NuGet.RuntimeModel; + +namespace Microsoft.NETCore.Platforms.BuildTasks +{ + + internal class RuntimeGroup + { + private const string rootRID = "any"; + private const char VersionDelimiter = '.'; + private const char ArchitectureDelimiter = '-'; + private const char QualifierDelimiter = '-'; + + public RuntimeGroup(ITaskItem item) + { + BaseRID = item.ItemSpec; + Parent = item.GetString(nameof(Parent)); + Versions = item.GetStrings(nameof(Versions)); + TreatVersionsAsCompatible = item.GetBoolean(nameof(TreatVersionsAsCompatible), true); + OmitVersionDelimiter = item.GetBoolean(nameof(OmitVersionDelimiter)); + ApplyVersionsToParent = item.GetBoolean(nameof(ApplyVersionsToParent)); + Architectures = item.GetStrings(nameof(Architectures)); + AdditionalQualifiers = item.GetStrings(nameof(AdditionalQualifiers)); + OmitRIDs = new HashSet(item.GetStrings(nameof(OmitRIDs))); + OmitRIDDefinitions = new HashSet(item.GetStrings(nameof(OmitRIDDefinitions))); + OmitRIDReferences = new HashSet(item.GetStrings(nameof(OmitRIDReferences))); + } + + public string BaseRID { get; } + public string Parent { get; } + public IEnumerable Versions { get; } + public bool TreatVersionsAsCompatible { get; } + public bool OmitVersionDelimiter { get; } + public bool ApplyVersionsToParent { get; } + public IEnumerable Architectures { get; } + public IEnumerable AdditionalQualifiers { get; } + public ICollection OmitRIDs { get; } + public ICollection OmitRIDDefinitions { get; } + public ICollection OmitRIDReferences { get; } + + private class RIDMapping + { + public RIDMapping(RID runtimeIdentifier) + { + RuntimeIdentifier = runtimeIdentifier; + Imports = Enumerable.Empty(); + } + + public RIDMapping(RID runtimeIdentifier, IEnumerable imports) + { + RuntimeIdentifier = runtimeIdentifier; + Imports = imports; + } + + public RID RuntimeIdentifier { get; } + + public IEnumerable Imports { get; } + } + + private RID CreateRuntime(string baseRid, string version = null, string architecture = null, string qualifier = null) + { + return new RID() + { + BaseRID = baseRid, + VersionDelimiter = OmitVersionDelimiter ? string.Empty : VersionDelimiter.ToString(), + Version = version, + ArchitectureDelimiter = ArchitectureDelimiter.ToString(), + Architecture = architecture, + QualifierDelimiter = QualifierDelimiter.ToString(), + Qualifier = qualifier + }; + } + + private IEnumerable GetRIDMappings() + { + // base => + // Parent + yield return Parent == null ? + new RIDMapping(CreateRuntime(BaseRID)) : + new RIDMapping(CreateRuntime(BaseRID), new[] { CreateRuntime(Parent) }); + + foreach (var architecture in Architectures) + { + // base + arch => + // base, + // parent + arch + var imports = new List() + { + CreateRuntime(BaseRID) + }; + + if (!IsNullOrRoot(Parent)) + { + imports.Add(CreateRuntime(Parent, architecture: architecture)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture), imports); + } + + string lastVersion = null; + foreach (var version in Versions) + { + // base + version => + // base + lastVersion, + // parent + version (optionally) + var imports = new List() + { + CreateRuntime(BaseRID, version: lastVersion) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version), imports); + + foreach (var architecture in Architectures) + { + // base + version + architecture => + // base + version, + // base + lastVersion + architecture, + // parent + version + architecture (optionally) + var archImports = new List() + { + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, architecture: architecture) + }; + + if (ApplyVersionsToParent) + { + archImports.Add(CreateRuntime(Parent, version: version, architecture: architecture)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture), archImports); + } + + if (TreatVersionsAsCompatible) + { + lastVersion = version; + } + } + + foreach (var qualifier in AdditionalQualifiers) + { + // base + qual => + // base, + // parent + qual + yield return new RIDMapping(CreateRuntime(BaseRID, qualifier: qualifier), + new[] + { + CreateRuntime(BaseRID), + IsNullOrRoot(Parent) ? CreateRuntime(qualifier) : CreateRuntime(Parent, qualifier:qualifier) + }); + + foreach (var architecture in Architectures) + { + // base + arch + qualifier => + // base + qualifier, + // base + arch + // parent + arch + qualifier + var imports = new List() + { + CreateRuntime(BaseRID, qualifier: qualifier), + CreateRuntime(BaseRID, architecture: architecture) + }; + + if (!IsNullOrRoot(Parent)) + { + imports.Add(CreateRuntime(Parent, architecture: architecture, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, architecture: architecture, qualifier: qualifier), imports); + } + + lastVersion = null; + foreach (var version in Versions) + { + // base + version + qualifier => + // base + version, + // base + lastVersion + qualifier + // parent + version + qualifier (optionally) + var imports = new List() + { + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, qualifier: qualifier) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, qualifier: qualifier), imports); + + foreach (var architecture in Architectures) + { + // base + version + architecture + qualifier => + // base + version + qualifier, + // base + version + architecture, + // base + version, + // base + lastVersion + architecture + qualifier, + // parent + version + architecture + qualifier (optionally) + var archImports = new List() + { + CreateRuntime(BaseRID, version: version, qualifier: qualifier), + CreateRuntime(BaseRID, version: version, architecture: architecture), + CreateRuntime(BaseRID, version: version), + CreateRuntime(BaseRID, version: lastVersion, architecture: architecture, qualifier: qualifier) + }; + + if (ApplyVersionsToParent) + { + imports.Add(CreateRuntime(Parent, version: version, architecture: architecture, qualifier: qualifier)); + } + + yield return new RIDMapping(CreateRuntime(BaseRID, version: version, architecture: architecture, qualifier: qualifier), archImports); + } + + if (TreatVersionsAsCompatible) + { + lastVersion = version; + } + } + } + } + + private bool IsNullOrRoot(string rid) + { + return rid == null || rid == rootRID; + } + + + public IEnumerable GetRuntimeDescriptions() + { + foreach (var mapping in GetRIDMappings()) + { + var rid = mapping.RuntimeIdentifier.ToString(); + + if (OmitRIDs.Contains(rid) || OmitRIDDefinitions.Contains(rid)) + { + continue; + } + + var imports = mapping.Imports + .Select(i => i.ToString()) + .Where(i => !OmitRIDs.Contains(i) && !OmitRIDReferences.Contains(i)) + .ToArray(); + + yield return new RuntimeDescription(rid, imports); + } + } + + public RuntimeGraph GetRuntimeGraph() + { + return new RuntimeGraph(GetRuntimeDescriptions()); + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/_._ b/src/libraries/Microsoft.NETCore.Platforms/src/_._ new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.compatibility.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json similarity index 92% rename from src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.compatibility.json rename to src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json index b4b5f6a6ec2ee..a740ab7324fbc 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.compatibility.json +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.compatibility.json @@ -3014,6 +3014,352 @@ "any", "base" ], + "iossimulator": [ + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator-arm64": [ + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator-x64": [ + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator-x86": [ + "iossimulator-x86", + "iossimulator", + "ios-x86", + "ios", + "unix-x86", + "unix", + "any", + "base" + ], + "iossimulator.10": [ + "iossimulator.10", + "iossimulator.9", + "iossimulator.8", + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator.10-arm64": [ + "iossimulator.10-arm64", + "iossimulator.10", + "iossimulator.9-arm64", + "iossimulator.9", + "iossimulator.8-arm64", + "iossimulator.8", + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator.10-x64": [ + "iossimulator.10-x64", + "iossimulator.10", + "iossimulator.9-x64", + "iossimulator.9", + "iossimulator.8-x64", + "iossimulator.8", + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator.10-x86": [ + "iossimulator.10-x86", + "iossimulator.10", + "iossimulator.9-x86", + "iossimulator.9", + "iossimulator.8-x86", + "iossimulator.8", + "iossimulator-x86", + "iossimulator", + "ios-x86", + "ios", + "unix-x86", + "unix", + "any", + "base" + ], + "iossimulator.11": [ + "iossimulator.11", + "iossimulator.10", + "iossimulator.9", + "iossimulator.8", + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator.11-arm64": [ + "iossimulator.11-arm64", + "iossimulator.11", + "iossimulator.10-arm64", + "iossimulator.10", + "iossimulator.9-arm64", + "iossimulator.9", + "iossimulator.8-arm64", + "iossimulator.8", + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator.11-x64": [ + "iossimulator.11-x64", + "iossimulator.11", + "iossimulator.10-x64", + "iossimulator.10", + "iossimulator.9-x64", + "iossimulator.9", + "iossimulator.8-x64", + "iossimulator.8", + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator.12": [ + "iossimulator.12", + "iossimulator.11", + "iossimulator.10", + "iossimulator.9", + "iossimulator.8", + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator.12-arm64": [ + "iossimulator.12-arm64", + "iossimulator.12", + "iossimulator.11-arm64", + "iossimulator.11", + "iossimulator.10-arm64", + "iossimulator.10", + "iossimulator.9-arm64", + "iossimulator.9", + "iossimulator.8-arm64", + "iossimulator.8", + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator.12-x64": [ + "iossimulator.12-x64", + "iossimulator.12", + "iossimulator.11-x64", + "iossimulator.11", + "iossimulator.10-x64", + "iossimulator.10", + "iossimulator.9-x64", + "iossimulator.9", + "iossimulator.8-x64", + "iossimulator.8", + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator.13": [ + "iossimulator.13", + "iossimulator.12", + "iossimulator.11", + "iossimulator.10", + "iossimulator.9", + "iossimulator.8", + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator.13-arm64": [ + "iossimulator.13-arm64", + "iossimulator.13", + "iossimulator.12-arm64", + "iossimulator.12", + "iossimulator.11-arm64", + "iossimulator.11", + "iossimulator.10-arm64", + "iossimulator.10", + "iossimulator.9-arm64", + "iossimulator.9", + "iossimulator.8-arm64", + "iossimulator.8", + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator.13-x64": [ + "iossimulator.13-x64", + "iossimulator.13", + "iossimulator.12-x64", + "iossimulator.12", + "iossimulator.11-x64", + "iossimulator.11", + "iossimulator.10-x64", + "iossimulator.10", + "iossimulator.9-x64", + "iossimulator.9", + "iossimulator.8-x64", + "iossimulator.8", + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator.8": [ + "iossimulator.8", + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator.8-arm64": [ + "iossimulator.8-arm64", + "iossimulator.8", + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator.8-x64": [ + "iossimulator.8-x64", + "iossimulator.8", + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator.8-x86": [ + "iossimulator.8-x86", + "iossimulator.8", + "iossimulator-x86", + "iossimulator", + "ios-x86", + "ios", + "unix-x86", + "unix", + "any", + "base" + ], + "iossimulator.9": [ + "iossimulator.9", + "iossimulator.8", + "iossimulator", + "ios", + "unix", + "any", + "base" + ], + "iossimulator.9-arm64": [ + "iossimulator.9-arm64", + "iossimulator.9", + "iossimulator.8-arm64", + "iossimulator.8", + "iossimulator-arm64", + "iossimulator", + "ios-arm64", + "ios", + "unix-arm64", + "unix", + "any", + "base" + ], + "iossimulator.9-x64": [ + "iossimulator.9-x64", + "iossimulator.9", + "iossimulator.8-x64", + "iossimulator.8", + "iossimulator-x64", + "iossimulator", + "ios-x64", + "ios", + "unix-x64", + "unix", + "any", + "base" + ], + "iossimulator.9-x86": [ + "iossimulator.9-x86", + "iossimulator.9", + "iossimulator.8-x86", + "iossimulator.8", + "iossimulator-x86", + "iossimulator", + "ios-x86", + "ios", + "unix-x86", + "unix", + "any", + "base" + ], "linux": [ "linux", "unix", @@ -5735,6 +6081,191 @@ "any", "base" ], + "tvossimulator": [ + "tvossimulator", + "tvos", + "unix", + "any", + "base" + ], + "tvossimulator-arm64": [ + "tvossimulator-arm64", + "tvossimulator", + "tvos-arm64", + "tvos", + "unix-arm64", + "unix", + "any", + "base" + ], + "tvossimulator-x64": [ + "tvossimulator-x64", + "tvossimulator", + "tvos-x64", + "tvos", + "unix-x64", + "unix", + "any", + "base" + ], + "tvossimulator.10": [ + "tvossimulator.10", + "tvossimulator", + "tvos", + "unix", + "any", + "base" + ], + "tvossimulator.10-arm64": [ + "tvossimulator.10-arm64", + "tvossimulator.10", + "tvossimulator-arm64", + "tvossimulator", + "tvos-arm64", + "tvos", + "unix-arm64", + "unix", + "any", + "base" + ], + "tvossimulator.10-x64": [ + "tvossimulator.10-x64", + "tvossimulator.10", + "tvossimulator-x64", + "tvossimulator", + "tvos-x64", + "tvos", + "unix-x64", + "unix", + "any", + "base" + ], + "tvossimulator.11": [ + "tvossimulator.11", + "tvossimulator.10", + "tvossimulator", + "tvos", + "unix", + "any", + "base" + ], + "tvossimulator.11-arm64": [ + "tvossimulator.11-arm64", + "tvossimulator.11", + "tvossimulator.10-arm64", + "tvossimulator.10", + "tvossimulator-arm64", + "tvossimulator", + "tvos-arm64", + "tvos", + "unix-arm64", + "unix", + "any", + "base" + ], + "tvossimulator.11-x64": [ + "tvossimulator.11-x64", + "tvossimulator.11", + "tvossimulator.10-x64", + "tvossimulator.10", + "tvossimulator-x64", + "tvossimulator", + "tvos-x64", + "tvos", + "unix-x64", + "unix", + "any", + "base" + ], + "tvossimulator.12": [ + "tvossimulator.12", + "tvossimulator.11", + "tvossimulator.10", + "tvossimulator", + "tvos", + "unix", + "any", + "base" + ], + "tvossimulator.12-arm64": [ + "tvossimulator.12-arm64", + "tvossimulator.12", + "tvossimulator.11-arm64", + "tvossimulator.11", + "tvossimulator.10-arm64", + "tvossimulator.10", + "tvossimulator-arm64", + "tvossimulator", + "tvos-arm64", + "tvos", + "unix-arm64", + "unix", + "any", + "base" + ], + "tvossimulator.12-x64": [ + "tvossimulator.12-x64", + "tvossimulator.12", + "tvossimulator.11-x64", + "tvossimulator.11", + "tvossimulator.10-x64", + "tvossimulator.10", + "tvossimulator-x64", + "tvossimulator", + "tvos-x64", + "tvos", + "unix-x64", + "unix", + "any", + "base" + ], + "tvossimulator.13": [ + "tvossimulator.13", + "tvossimulator.12", + "tvossimulator.11", + "tvossimulator.10", + "tvossimulator", + "tvos", + "unix", + "any", + "base" + ], + "tvossimulator.13-arm64": [ + "tvossimulator.13-arm64", + "tvossimulator.13", + "tvossimulator.12-arm64", + "tvossimulator.12", + "tvossimulator.11-arm64", + "tvossimulator.11", + "tvossimulator.10-arm64", + "tvossimulator.10", + "tvossimulator-arm64", + "tvossimulator", + "tvos-arm64", + "tvos", + "unix-arm64", + "unix", + "any", + "base" + ], + "tvossimulator.13-x64": [ + "tvossimulator.13-x64", + "tvossimulator.13", + "tvossimulator.12-x64", + "tvossimulator.12", + "tvossimulator.11-x64", + "tvossimulator.11", + "tvossimulator.10-x64", + "tvossimulator.10", + "tvossimulator-x64", + "tvossimulator", + "tvos-x64", + "tvos", + "unix-x64", + "unix", + "any", + "base" + ], "ubuntu": [ "ubuntu", "debian", @@ -7446,4 +7977,4 @@ "any", "base" ] -} +} \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.json b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json similarity index 92% rename from src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.json rename to src/libraries/Microsoft.NETCore.Platforms/src/runtime.json index b490944226e5b..9ce5b6033f61e 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtime.json +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtime.json @@ -1252,6 +1252,149 @@ "ios.8-x86" ] }, + "iossimulator": { + "#import": [ + "ios" + ] + }, + "iossimulator-arm64": { + "#import": [ + "iossimulator", + "ios-arm64" + ] + }, + "iossimulator-x64": { + "#import": [ + "iossimulator", + "ios-x64" + ] + }, + "iossimulator-x86": { + "#import": [ + "iossimulator", + "ios-x86" + ] + }, + "iossimulator.10": { + "#import": [ + "iossimulator.9" + ] + }, + "iossimulator.10-arm64": { + "#import": [ + "iossimulator.10", + "iossimulator.9-arm64" + ] + }, + "iossimulator.10-x64": { + "#import": [ + "iossimulator.10", + "iossimulator.9-x64" + ] + }, + "iossimulator.10-x86": { + "#import": [ + "iossimulator.10", + "iossimulator.9-x86" + ] + }, + "iossimulator.11": { + "#import": [ + "iossimulator.10" + ] + }, + "iossimulator.11-arm64": { + "#import": [ + "iossimulator.11", + "iossimulator.10-arm64" + ] + }, + "iossimulator.11-x64": { + "#import": [ + "iossimulator.11", + "iossimulator.10-x64" + ] + }, + "iossimulator.12": { + "#import": [ + "iossimulator.11" + ] + }, + "iossimulator.12-arm64": { + "#import": [ + "iossimulator.12", + "iossimulator.11-arm64" + ] + }, + "iossimulator.12-x64": { + "#import": [ + "iossimulator.12", + "iossimulator.11-x64" + ] + }, + "iossimulator.13": { + "#import": [ + "iossimulator.12" + ] + }, + "iossimulator.13-arm64": { + "#import": [ + "iossimulator.13", + "iossimulator.12-arm64" + ] + }, + "iossimulator.13-x64": { + "#import": [ + "iossimulator.13", + "iossimulator.12-x64" + ] + }, + "iossimulator.8": { + "#import": [ + "iossimulator" + ] + }, + "iossimulator.8-arm64": { + "#import": [ + "iossimulator.8", + "iossimulator-arm64" + ] + }, + "iossimulator.8-x64": { + "#import": [ + "iossimulator.8", + "iossimulator-x64" + ] + }, + "iossimulator.8-x86": { + "#import": [ + "iossimulator.8", + "iossimulator-x86" + ] + }, + "iossimulator.9": { + "#import": [ + "iossimulator.8" + ] + }, + "iossimulator.9-arm64": { + "#import": [ + "iossimulator.9", + "iossimulator.8-arm64" + ] + }, + "iossimulator.9-x64": { + "#import": [ + "iossimulator.9", + "iossimulator.8-x64" + ] + }, + "iossimulator.9-x86": { + "#import": [ + "iossimulator.9", + "iossimulator.8-x86" + ] + }, "linux": { "#import": [ "unix" @@ -2431,6 +2574,91 @@ "tvos.12-x64" ] }, + "tvossimulator": { + "#import": [ + "tvos" + ] + }, + "tvossimulator-arm64": { + "#import": [ + "tvossimulator", + "tvos-arm64" + ] + }, + "tvossimulator-x64": { + "#import": [ + "tvossimulator", + "tvos-x64" + ] + }, + "tvossimulator.10": { + "#import": [ + "tvossimulator" + ] + }, + "tvossimulator.10-arm64": { + "#import": [ + "tvossimulator.10", + "tvossimulator-arm64" + ] + }, + "tvossimulator.10-x64": { + "#import": [ + "tvossimulator.10", + "tvossimulator-x64" + ] + }, + "tvossimulator.11": { + "#import": [ + "tvossimulator.10" + ] + }, + "tvossimulator.11-arm64": { + "#import": [ + "tvossimulator.11", + "tvossimulator.10-arm64" + ] + }, + "tvossimulator.11-x64": { + "#import": [ + "tvossimulator.11", + "tvossimulator.10-x64" + ] + }, + "tvossimulator.12": { + "#import": [ + "tvossimulator.11" + ] + }, + "tvossimulator.12-arm64": { + "#import": [ + "tvossimulator.12", + "tvossimulator.11-arm64" + ] + }, + "tvossimulator.12-x64": { + "#import": [ + "tvossimulator.12", + "tvossimulator.11-x64" + ] + }, + "tvossimulator.13": { + "#import": [ + "tvossimulator.12" + ] + }, + "tvossimulator.13-arm64": { + "#import": [ + "tvossimulator.13", + "tvossimulator.12-arm64" + ] + }, + "tvossimulator.13-x64": { + "#import": [ + "tvossimulator.13", + "tvossimulator.12-x64" + ] + }, "ubuntu": { "#import": [ "debian" @@ -3259,4 +3487,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtimeGroups.props b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props similarity index 86% rename from src/libraries/Microsoft.NETCore.Platforms/pkg/runtimeGroups.props rename to src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props index d0ea5972ea845..75f41db11693e 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/pkg/runtimeGroups.props +++ b/src/libraries/Microsoft.NETCore.Platforms/src/runtimeGroups.props @@ -91,6 +91,17 @@ 8;9;10;11;12;13 + + ios + x86 + 8;9;10 + + + ios + arm64;x64 + 8;9;10;11;12;13 + + ubuntu.14.04 @@ -210,6 +221,12 @@ 10;11;12;13 + + tvos + arm64;x64 + 10;11;12;13 + + debian x64;x86;arm @@ -245,15 +262,22 @@ - + + <_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' == 'core'">$(NetCoreAppToolCurrent) + <_generateRuntimeGraphTargetFramework Condition="'$(MSBuildRuntimeType)' != 'core'">net472 + <_generateRuntimeGraphTask>$([MSBuild]::NormalizePath('$(BaseOutputPath)', '$(_generateRuntimeGraphTargetFramework)-$(Configuration)', '$(AssemblyName).dll')) + + + + - + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/AssemblyInfo.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs similarity index 57% rename from src/libraries/System.Security.Cryptography.X509Certificates/tests/AssemblyInfo.cs rename to src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs index 3a74f33d7aa32..baad52408c410 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/AssemblyInfo.cs +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/AssemblyInfo.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using Xunit; -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/37669", TestPlatforms.Browser)] +[assembly: SkipOnMono("MSBuild is not supported on Browser", TestPlatforms.Browser)] \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs new file mode 100644 index 0000000000000..652ca7f87ec33 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/GenerateRuntimeGraphTests.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NETCore.Platforms.BuildTasks.Tests +{ + public class GenerateRuntimeGraphTests + { + private Log _log; + private TestBuildEngine _engine; + + public GenerateRuntimeGraphTests(ITestOutputHelper output) + { + _log = new Log(output); + _engine = new TestBuildEngine(_log); + } + + [Fact] + public void CanCreateRuntimeGraph() + { + string runtimeFile = "runtime.json"; + + Project runtimeGroupProps = new Project("runtimeGroups.props"); + + ITaskItem[] runtimeGroups = runtimeGroupProps.GetItems("RuntimeGroupWithQualifiers") + .Select(i => CreateItem(i)).ToArray(); + + Assert.NotEmpty(runtimeGroups); + + // will generate and compare to existing file. + GenerateRuntimeGraph task = new GenerateRuntimeGraph() + { + BuildEngine = _engine, + RuntimeGroups = runtimeGroups, + RuntimeJson = runtimeFile + }; + task.Execute(); + + _log.AssertNoErrorsOrWarnings(); + } + + private static ITaskItem CreateItem(ProjectItem projectItem) + { + TaskItem item = new TaskItem(projectItem.EvaluatedInclude); + foreach(var metadatum in projectItem.Metadata) + { + item.SetMetadata(metadatum.Name, metadatum.EvaluatedValue); + } + return item; + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs new file mode 100644 index 0000000000000..d1e7368ec0882 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Log.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NETCore.Platforms.BuildTasks.Tests +{ + internal class Log : ILog + { + private readonly ITestOutputHelper _output; + + public Log(ITestOutputHelper output) + { + _output = output; + Reset(); + } + + public int ErrorsLogged { get; set; } + public int WarningsLogged { get; set; } + + public void LogError(string message, params object[] messageArgs) + { + ErrorsLogged++; + _output.WriteLine("Error: " + message, messageArgs); + } + + public void LogMessage(string message, params object[] messageArgs) + { + _output.WriteLine(message, messageArgs); + } + + public void LogMessage(LogImportance importance, string message, params object[] messageArgs) + { + _output.WriteLine(message, messageArgs); + } + + public void LogWarning(string message, params object[] messageArgs) + { + WarningsLogged++; + _output.WriteLine("Warning: " + message, messageArgs); + } + + public void Reset() + { + ErrorsLogged = 0; + WarningsLogged = 0; + } + + public void AssertNoErrorsOrWarnings() + { + Assert.Equal(0, ErrorsLogged); + Assert.Equal(0, WarningsLogged); + } + } +} diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj new file mode 100644 index 0000000000000..cb254a5cb5070 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj @@ -0,0 +1,24 @@ + + + $(NetCoreAppCurrent);net472 + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs b/src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs new file mode 100644 index 0000000000000..67dfefbe8c8c9 --- /dev/null +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/TestBuildEngine.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Framework; +using System; +using System.Collections; + +namespace Microsoft.NETCore.Platforms.BuildTasks.Tests +{ + public class TestBuildEngine : IBuildEngine + { + private ILog _log; + + public TestBuildEngine(ILog log) + { + ColumnNumberOfTaskNode = 0; + ContinueOnError = true; + LineNumberOfTaskNode = 0; + ProjectFileOfTaskNode = "test"; + _log = log; + } + + public int ColumnNumberOfTaskNode { get; set; } + + public bool ContinueOnError { get; set; } + + public int LineNumberOfTaskNode { get; set; } + + public string ProjectFileOfTaskNode { get; set; } + + public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs) + { + throw new NotImplementedException(); + } + + public void LogCustomEvent(CustomBuildEventArgs e) + { + _log.LogMessage(e.Message); + } + + public void LogErrorEvent(BuildErrorEventArgs e) + { + _log.LogError(e.Message); + } + + public void LogMessageEvent(BuildMessageEventArgs e) + { + _log.LogMessage((LogImportance)e.Importance, e.Message); + } + + public void LogWarningEvent(BuildWarningEventArgs e) + { + _log.LogWarning(e.Message); + } + } +} diff --git a/src/libraries/Microsoft.VisualBasic.Core/src/ILLink/ILLinkTrim.xml b/src/libraries/Microsoft.VisualBasic.Core/src/ILLink/ILLink.Descriptors.xml similarity index 100% rename from src/libraries/Microsoft.VisualBasic.Core/src/ILLink/ILLinkTrim.xml rename to src/libraries/Microsoft.VisualBasic.Core/src/ILLink/ILLink.Descriptors.xml diff --git a/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj index 98b58060d2d66..3724c9fb49baa 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj +++ b/src/libraries/Microsoft.VisualBasic.Core/src/Microsoft.VisualBasic.Core.vbproj @@ -1,6 +1,5 @@ - {a32671b6-5470-4f9c-9cd8-4094b9ab0799} true None On @@ -11,7 +10,7 @@ 42025 $(DefineConstants),LATEBINDING=True $(DefineConstants),TARGET_WINDOWS=True - $(NoWarn),CA1810,CA2200 + $(NoWarn),CA1052,CA1810,CA2200 $(NoWarn);CA1823 Microsoft.VisualBasic.Core diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/FileSystemTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/FileSystemTests.cs index 46862efd1d6dd..49a0d5ff2352a 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/FileSystemTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/FileSystemTests.cs @@ -41,6 +41,7 @@ public void ChDir() // public static void ChDrive(string Drive){ throw null; } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void CloseAllFiles() { var fileName1 = GetTestFilePath(); diff --git a/src/libraries/Microsoft.VisualBasic.Core/tests/VBMathTests.cs b/src/libraries/Microsoft.VisualBasic.Core/tests/VBMathTests.cs index 7a9d2bd05aaf1..22c0d88bb6697 100644 --- a/src/libraries/Microsoft.VisualBasic.Core/tests/VBMathTests.cs +++ b/src/libraries/Microsoft.VisualBasic.Core/tests/VBMathTests.cs @@ -171,9 +171,6 @@ public void Randomize_SetsExpectedState() { ResetSeed(); - if (!BitConverter.IsLittleEndian) - throw new NotImplementedException("big endian tests"); - VBMath.Randomize(-2E30); Assert.Equal(-0.0297851562f, VBMath.Rnd(0.0f)); VBMath.Randomize(-0.003356); diff --git a/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs b/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs index 9ef02aaef46a1..a2b1644108098 100644 --- a/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs +++ b/src/libraries/Microsoft.Win32.Primitives/src/System/ComponentModel/Win32Exception.cs @@ -87,7 +87,7 @@ public override string ToString() string className = GetType().ToString(); StringBuilder s = new StringBuilder(className); string nativeErrorString = NativeErrorCode < 0 - ? string.Format(CultureInfo.InvariantCulture, "0x{0:X8}", NativeErrorCode) + ? $"0x{NativeErrorCode:X8}" : NativeErrorCode.ToString(CultureInfo.InvariantCulture); if (HResult == E_FAIL) { diff --git a/src/libraries/Microsoft.Win32.SystemEvents/Directory.Build.props b/src/libraries/Microsoft.Win32.SystemEvents/Directory.Build.props index 1f1da63db954b..5e287db1f7dbf 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/Directory.Build.props +++ b/src/libraries/Microsoft.Win32.SystemEvents/Directory.Build.props @@ -2,7 +2,6 @@ Open - true true Provides access to Windows system event notifications. diff --git a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs index 32313c61a5e37..374070553e433 100644 --- a/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs +++ b/src/libraries/Microsoft.Win32.SystemEvents/src/Microsoft/Win32/SystemEvents.cs @@ -668,9 +668,7 @@ private unsafe void Initialize() IntPtr hInstance = Interop.Kernel32.GetModuleHandle(null); - s_className = string.Format( - ".NET-BroadcastEventWindow.{0:x}.0", - AppDomain.CurrentDomain.GetHashCode()); + s_className = $".NET-BroadcastEventWindow.{AppDomain.CurrentDomain.GetHashCode():x}.0"; fixed (char* className = s_className) { @@ -1312,7 +1310,7 @@ private void WindowThreadProc() } // A class that helps fire events on the right thread. - private class SystemEventInvokeInfo + private sealed class SystemEventInvokeInfo { private readonly SynchronizationContext _syncContext; // the context that we'll use to fire against. private readonly Delegate _delegate; // the delegate we'll fire. This is a weak ref so we don't hold object in memory. diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs b/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs index 322da79bf88ad..e88f16a1c4d3b 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs +++ b/src/libraries/Microsoft.XmlSerializer.Generator/src/Sgen.cs @@ -14,7 +14,7 @@ namespace Microsoft.XmlSerializer.Generator { - internal class Sgen + internal sealed class Sgen { public static int Main(string[] args) { @@ -453,7 +453,7 @@ private static Assembly LoadAssembly(string assemblyName, bool throwOnFail) private void WriteHeader() { // do not localize Copyright header - Console.WriteLine(string.Format(CultureInfo.CurrentCulture, ".NET Xml Serialization Generation Utility, Version {0}]", ThisAssembly.InformationalVersion)); + Console.WriteLine($".NET Xml Serialization Generation Utility, Version {ThisAssembly.InformationalVersion}]"); } private void WriteHelp() diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj b/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj index 64b66933c183d..10a6631611e07 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj +++ b/src/libraries/Microsoft.XmlSerializer.Generator/tests/Microsoft.XmlSerializer.Generator.Tests.csproj @@ -8,8 +8,8 @@ $(MSBuildThisFileDirectory)..\pkg\build\dotnet-Microsoft.XmlSerializer.Generator.runtimeconfig.json "$(DotNetTool)" - set DOTNET_MULTILEVEL_LOOKUP=0 & $(GeneratorCommand) - export DOTNET_MULTILEVEL_LOOKUP=0 && $(GeneratorCommand) + set DOTNET_MULTILEVEL_LOOKUP=0 & $(GeneratorCommand) + export DOTNET_MULTILEVEL_LOOKUP=0 && $(GeneratorCommand) diff --git a/src/libraries/Native/Unix/CMakeLists.txt b/src/libraries/Native/Unix/CMakeLists.txt index fab7c06dfc3d1..f47135fd8871c 100644 --- a/src/libraries/Native/Unix/CMakeLists.txt +++ b/src/libraries/Native/Unix/CMakeLists.txt @@ -254,13 +254,8 @@ elseif(CLR_CMAKE_TARGET_TVOS) #add_subdirectory(System.Net.Security.Native) # no gssapi on tvOS, see https://developer.apple.com/documentation/gss # System.Security.Cryptography.Native is intentionally disabled on tvOS # it is only used for interacting with OpenSSL which isn't useful there -elseif(CLR_CMAKE_TARGET_ANDROID AND NOT CROSS_ROOTFS) - #add_subdirectory(System.Net.Security.Native) # TODO: reenable - if (NOT "$ENV{ANDROID_OPENSSL_AAR}" STREQUAL "") - message("Using Android OpenSSL") - set(PREFER_OPENSSL_ANDROID 1) - add_subdirectory(System.Security.Cryptography.Native) - endif() +elseif(CLR_CMAKE_TARGET_ANDROID) + add_subdirectory(System.Security.Cryptography.Native.Android) else() add_subdirectory(System.Globalization.Native) add_subdirectory(System.Net.Security.Native) @@ -270,9 +265,3 @@ endif() if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) add_subdirectory(System.Security.Cryptography.Native.Apple) endif() - -# if ANDROID_OPENSSL_AAR is not set - use Android Native Crypto (it's going to replace openssl eventually) -if(CLR_CMAKE_TARGET_ANDROID AND NOT PREFER_OPENSSL_ANDROID) - message("Using Android Native Crypto") - add_subdirectory(System.Security.Cryptography.Native.Android) -endif() diff --git a/src/libraries/Native/Unix/Common/pal_x509_types.h b/src/libraries/Native/Unix/Common/pal_x509_types.h new file mode 100644 index 0000000000000..47602dbbc1207 --- /dev/null +++ b/src/libraries/Native/Unix/Common/pal_x509_types.h @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +// Matches managed X509ChainStatusFlags enum +enum +{ + PAL_X509ChainNoError = 0, + PAL_X509ChainNotTimeValid = 0x00000001, + PAL_X509ChainNotTimeNested = 0x00000002, + PAL_X509ChainRevoked = 0x00000004, + PAL_X509ChainNotSignatureValid = 0x00000008, + PAL_X509ChainNotValidForUsage = 0x00000010, + PAL_X509ChainUntrustedRoot = 0x00000020, + PAL_X509ChainRevocationStatusUnknown = 0x00000040, + PAL_X509ChainCyclic = 0x00000080, + PAL_X509ChainInvalidExtension = 0x00000100, + PAL_X509ChainInvalidPolicyConstraints = 0x00000200, + PAL_X509ChainInvalidBasicConstraints = 0x00000400, + PAL_X509ChainInvalidNameConstraints = 0x00000800, + PAL_X509ChainHasNotSupportedNameConstraint = 0x00001000, + PAL_X509ChainHasNotDefinedNameConstraint = 0x00002000, + PAL_X509ChainHasNotPermittedNameConstraint = 0x00004000, + PAL_X509ChainHasExcludedNameConstraint = 0x00008000, + PAL_X509ChainPartialChain = 0x00010000, + PAL_X509ChainCtlNotTimeValid = 0x00020000, + PAL_X509ChainCtlNotSignatureValid = 0x00040000, + PAL_X509ChainCtlNotValidForUsage = 0x00080000, + PAL_X509ChainOfflineRevocation = 0x01000000, + PAL_X509ChainNoIssuanceChainPolicy = 0x02000000, + PAL_X509ChainExplicitDistrust = 0x04000000, + PAL_X509ChainHasNotSupportedCriticalExtension = 0x08000000, + PAL_X509ChainHasWeakSignature = 0x00100000, +}; +typedef uint32_t PAL_X509ChainStatusFlags; + +// Matches managed X509ContentType enum +enum +{ + PAL_X509Unknown = 0, + PAL_Certificate = 1, + PAL_SerializedCert = 2, + PAL_Pkcs12 = 3, + PAL_SerializedStore = 4, + PAL_Pkcs7 = 5, + PAL_Authenticode = 6, +}; +typedef uint32_t PAL_X509ContentType; diff --git a/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt index b580d34856a80..148197a6b9258 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Globalization.Native/CMakeLists.txt @@ -14,8 +14,10 @@ if(CLR_CMAKE_TARGET_UNIX) add_compile_options(-Wno-unknown-warning-option) if (NOT CLR_CMAKE_TARGET_ANDROID) - execute_process(COMMAND brew --prefix OUTPUT_VARIABLE brew_prefix OUTPUT_STRIP_TRAILING_WHITESPACE) - set(ICU_HOMEBREW_INC_PATH "${brew_prefix}/opt/icu4c/include") + if (CLR_CMAKE_TARGET_OSX) + execute_process(COMMAND brew --prefix OUTPUT_VARIABLE brew_prefix OUTPUT_STRIP_TRAILING_WHITESPACE) + set(ICU_HOMEBREW_INC_PATH "${brew_prefix}/opt/icu4c/include") + endif() find_path(UTYPES_H "unicode/utypes.h" PATHS ${ICU_HOMEBREW_INC_PATH}) if(UTYPES_H STREQUAL UTYPES_H-NOTFOUND) @@ -59,10 +61,14 @@ set(NATIVEGLOBALIZATION_SOURCES pal_localeNumberData.c pal_localeStringData.c pal_normalization.c - pal_timeZoneInfo.c pal_icushim.c ) +# time zone names are filtered out of icu data for the browser and associated functionality is disabled +if (NOT CLR_CMAKE_TARGET_BROWSER) + set(NATIVEGLOBALIZATION_SOURCES ${NATIVEGLOBALIZATION_SOURCES} pal_timeZoneInfo.c) +endif() + if (NOT GEN_SHARED_LIB AND NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS AND NOT CLR_CMAKE_TARGET_ANDROID AND NOT CLR_CMAKE_TARGET_BROWSER) set(NATIVEGLOBALIZATION_SOURCES ${NATIVEGLOBALIZATION_SOURCES} entrypoints.c) endif() @@ -97,7 +103,7 @@ if(CLR_CMAKE_TARGET_UNIX) set_target_properties(System.Globalization.Native-Static PROPERTIES OUTPUT_NAME System.Globalization.Native CLEAN_DIRECT_OUTPUT 1) endif() -install (TARGETS System.Globalization.Native-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.Globalization.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) if(NOT CLR_CMAKE_TARGET_OSX AND NOT CLR_CMAKE_TARGET_MACCATALYST AND NOT CLR_CMAKE_TARGET_IOS AND NOT CLR_CMAKE_TARGET_TVOS AND NOT CLR_CMAKE_TARGET_ANDROID) if (GEN_SHARED_LIB) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c b/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c index fced139e21958..f8ec0e22be77d 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c @@ -50,6 +50,9 @@ static const Entry s_globalizationNative[] = DllImportEntry(GlobalizationNative_IsPredefinedLocale) DllImportEntry(GlobalizationNative_LastIndexOf) DllImportEntry(GlobalizationNative_LoadICU) +#if defined(STATIC_ICU) + DllImportEntry(GlobalizationNative_LoadICUData) +#endif DllImportEntry(GlobalizationNative_NormalizeString) DllImportEntry(GlobalizationNative_StartsWith) DllImportEntry(GlobalizationNative_ToAscii) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.h index 5f0123d8ef521..51e117113de1c 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.h @@ -11,7 +11,7 @@ PALEXPORT int32_t GlobalizationNative_GetICUVersion(void); #if defined(STATIC_ICU) -PALEXPORT int32_t GlobalizationNative_LoadICUData(char* path); +PALEXPORT int32_t GlobalizationNative_LoadICUData(const char* path); PALEXPORT const char* GlobalizationNative_GetICUDTName(const char* culture); diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h index 836ba44ec182f..b70e31fe9c0bb 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h @@ -58,27 +58,33 @@ // (U_ICU_VERSION_MAJOR_NUM < 52) // The following APIs are not supported in the ICU versions less than 52. We need to define them manually. // We have to do runtime check before using the pointers to these APIs. That is why these are listed in the FOR_ALL_OPTIONAL_ICU_FUNCTIONS list. -U_CAPI int32_t U_EXPORT2 ucal_getWindowsTimeZoneID(const UChar* id, int32_t len,UChar* winid, int32_t winidCapacity, UErrorCode* status); U_CAPI int32_t U_EXPORT2 ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* region, UChar* id, int32_t idCapacity, UErrorCode* status); +U_CAPI int32_t U_EXPORT2 ucal_getWindowsTimeZoneID(const UChar* id, int32_t len, UChar* winid, int32_t winidCapacity, UErrorCode* status); #endif // List of all functions from the ICU libraries that are used in the System.Globalization.Native.so #define FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \ PER_FUNCTION_BLOCK(u_charsToUChars, libicuuc, true) \ PER_FUNCTION_BLOCK(u_getVersion, libicuuc, true) \ + PER_FUNCTION_BLOCK(u_strcmp, libicuuc, true) \ + PER_FUNCTION_BLOCK(u_strcpy, libicuuc, true) \ PER_FUNCTION_BLOCK(u_strlen, libicuuc, true) \ PER_FUNCTION_BLOCK(u_strncpy, libicuuc, true) \ PER_FUNCTION_BLOCK(u_tolower, libicuuc, true) \ PER_FUNCTION_BLOCK(u_toupper, libicuuc, true) \ + PER_FUNCTION_BLOCK(u_uastrcpy, libicuuc, true) \ PER_FUNCTION_BLOCK(ucal_add, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_close, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_get, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_getAttribute, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_getKeywordValuesForLocale, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_getLimit, libicui18n, true) \ + PER_FUNCTION_BLOCK(ucal_getNow, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_getTimeZoneDisplayName, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_open, libicui18n, true) \ + PER_FUNCTION_BLOCK(ucal_openTimeZoneIDEnumeration, libicui18n, true) \ PER_FUNCTION_BLOCK(ucal_set, libicui18n, true) \ + PER_FUNCTION_BLOCK(ucal_setMillis, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_close, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_closeElements, libicui18n, true) \ PER_FUNCTION_BLOCK(ucol_getOffset, libicui18n, true) \ @@ -96,6 +102,7 @@ U_CAPI int32_t U_EXPORT2 ucal_getTimeZoneIDForWindowsID(const UChar* winid, int3 PER_FUNCTION_BLOCK(ucol_strcoll, libicui18n, true) \ PER_FUNCTION_BLOCK(udat_close, libicui18n, true) \ PER_FUNCTION_BLOCK(udat_countSymbols, libicui18n, true) \ + PER_FUNCTION_BLOCK(udat_format, libicui18n, true) \ PER_FUNCTION_BLOCK(udat_getSymbols, libicui18n, true) \ PER_FUNCTION_BLOCK(udat_open, libicui18n, true) \ PER_FUNCTION_BLOCK(udat_setCalendar, libicui18n, true) \ @@ -202,21 +209,27 @@ FOR_ALL_ICU_FUNCTIONS // to the functions of the selected version of ICU in the initialization. #define u_charsToUChars(...) u_charsToUChars_ptr(__VA_ARGS__) #define u_getVersion(...) u_getVersion_ptr(__VA_ARGS__) +#define u_strcmp(...) u_strcmp_ptr(__VA_ARGS__) +#define u_strcpy(...) u_strcpy_ptr(__VA_ARGS__) #define u_strlen(...) u_strlen_ptr(__VA_ARGS__) #define u_strncpy(...) u_strncpy_ptr(__VA_ARGS__) #define u_tolower(...) u_tolower_ptr(__VA_ARGS__) #define u_toupper(...) u_toupper_ptr(__VA_ARGS__) +#define u_uastrcpy(...) u_uastrcpy_ptr(__VA_ARGS__) #define ucal_add(...) ucal_add_ptr(__VA_ARGS__) #define ucal_close(...) ucal_close_ptr(__VA_ARGS__) #define ucal_get(...) ucal_get_ptr(__VA_ARGS__) #define ucal_getAttribute(...) ucal_getAttribute_ptr(__VA_ARGS__) #define ucal_getKeywordValuesForLocale(...) ucal_getKeywordValuesForLocale_ptr(__VA_ARGS__) #define ucal_getLimit(...) ucal_getLimit_ptr(__VA_ARGS__) +#define ucal_getNow(...) ucal_getNow_ptr(__VA_ARGS__) #define ucal_getTimeZoneDisplayName(...) ucal_getTimeZoneDisplayName_ptr(__VA_ARGS__) #define ucal_getTimeZoneIDForWindowsID(...) ucal_getTimeZoneIDForWindowsID_ptr(__VA_ARGS__) #define ucal_getWindowsTimeZoneID(...) ucal_getWindowsTimeZoneID_ptr(__VA_ARGS__) #define ucal_open(...) ucal_open_ptr(__VA_ARGS__) +#define ucal_openTimeZoneIDEnumeration(...) ucal_openTimeZoneIDEnumeration_ptr(__VA_ARGS__) #define ucal_set(...) ucal_set_ptr(__VA_ARGS__) +#define ucal_setMillis(...) ucal_setMillis_ptr(__VA_ARGS__) #define ucol_close(...) ucol_close_ptr(__VA_ARGS__) #define ucol_closeElements(...) ucol_closeElements_ptr(__VA_ARGS__) #define ucol_getOffset(...) ucol_getOffset_ptr(__VA_ARGS__) @@ -241,6 +254,7 @@ FOR_ALL_ICU_FUNCTIONS #define ucurr_getName(...) ucurr_getName_ptr(__VA_ARGS__) #define udat_close(...) udat_close_ptr(__VA_ARGS__) #define udat_countSymbols(...) udat_countSymbols_ptr(__VA_ARGS__) +#define udat_format(...) udat_format_ptr(__VA_ARGS__) #define udat_getSymbols(...) udat_getSymbols_ptr(__VA_ARGS__) #define udat_open(...) udat_open_ptr(__VA_ARGS__) #define udat_setCalendar(...) udat_setCalendar_ptr(__VA_ARGS__) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h index 7e05fa35d03ca..fea4c8696f785 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h @@ -25,6 +25,7 @@ typedef struct UBreakIterator UBreakIterator; typedef int8_t UBool; typedef uint16_t UChar; typedef int32_t UChar32; +typedef double UDate; typedef uint8_t UVersionInfo[U_MAX_VERSION_LENGTH]; typedef void* UNumberFormat; @@ -369,6 +370,12 @@ typedef enum UCollationResult { UCOL_LESS = -1 } UCollationResult; +typedef enum USystemTimeZoneType { + UCAL_ZONE_TYPE_ANY, + UCAL_ZONE_TYPE_CANONICAL, + UCAL_ZONE_TYPE_CANONICAL_LOCATION +} USystemTimeZoneType; + enum { UIDNA_ERROR_EMPTY_LABEL = 1, UIDNA_ERROR_LABEL_TOO_LONG = 2, @@ -419,25 +426,36 @@ typedef struct UIDNAInfo { int32_t reservedI3; } UIDNAInfo; +typedef struct UFieldPosition { + int32_t field; + int32_t beginIndex; + int32_t endIndex; +} UFieldPosition; void u_charsToUChars(const char * cs, UChar * us, int32_t length); void u_getVersion(UVersionInfo versionArray); int32_t u_strlen(const UChar * s); +int32_t u_strcmp(const UChar * s1, const UChar * s2); +UChar * u_strcpy(UChar * dst, const UChar * src); UChar * u_strncpy(UChar * dst, const UChar * src, int32_t n); UChar32 u_tolower(UChar32 c); UChar32 u_toupper(UChar32 c); +UChar* u_uastrcpy(UChar * dst, const char * src); void ucal_add(UCalendar * cal, UCalendarDateFields field, int32_t amount, UErrorCode * status); void ucal_close(UCalendar * cal); int32_t ucal_get(const UCalendar * cal, UCalendarDateFields field, UErrorCode * status); int32_t ucal_getAttribute(const UCalendar * cal, UCalendarAttribute attr); UEnumeration * ucal_getKeywordValuesForLocale(const char * key, const char * locale, UBool commonlyUsed, UErrorCode * status); int32_t ucal_getLimit(const UCalendar * cal, UCalendarDateFields field, UCalendarLimitType type, UErrorCode * status); +UDate ucal_getNow(void); int32_t ucal_getTimeZoneDisplayName(const UCalendar * cal, UCalendarDisplayNameType type, const char * locale, UChar * result, int32_t resultLength, UErrorCode * status); -UCalendar * ucal_open(const UChar * zoneID, int32_t len, const char * locale, UCalendarType type, UErrorCode * status); -void ucal_set(UCalendar * cal, UCalendarDateFields field, int32_t value); int32_t ucal_getTimeZoneIDForWindowsID(const UChar * winid, int32_t len, const char * region, UChar * id, int32_t idCapacity, UErrorCode * status); int32_t ucal_getWindowsTimeZoneID(const UChar * id, int32_t len, UChar * winid, int32_t winidCapacity, UErrorCode * status); +UCalendar * ucal_open(const UChar * zoneID, int32_t len, const char * locale, UCalendarType type, UErrorCode * status); +UEnumeration * ucal_openTimeZoneIDEnumeration(USystemTimeZoneType zoneType, const char * region, const int32_t * rawOffset, UErrorCode * ec); +void ucal_set(UCalendar * cal, UCalendarDateFields field, int32_t value); +void ucal_setMillis(UCalendar * cal, UDate dateTime, UErrorCode * status); void ucol_close(UCollator * coll); void ucol_closeElements(UCollationElements * elems); int32_t ucol_getOffset(const UCollationElements *elems); @@ -457,6 +475,7 @@ int32_t ucurr_forLocale(const char * locale, UChar * buff, int32_t buffCapacity, const UChar * ucurr_getName(const UChar * currency, const char * locale, UCurrNameStyle nameStyle, UBool * isChoiceFormat, int32_t * len, UErrorCode * ec); void udat_close(UDateFormat * format); int32_t udat_countSymbols(const UDateFormat * fmt, UDateFormatSymbolType type); +int32_t udat_format(const UDateFormat * format, UDate dateToFormat, UChar * result, int32_t resultLength, UFieldPosition * position, UErrorCode * status); int32_t udat_getSymbols(const UDateFormat * fmt, UDateFormatSymbolType type, int32_t symbolIndex, UChar * result, int32_t resultLength, UErrorCode * status); UDateFormat * udat_open(UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const char * locale, const UChar * tzID, int32_t tzIDLength, const UChar * pattern, int32_t patternLength, UErrorCode * status); void udat_setCalendar(UDateFormat * fmt, const UCalendar * calendarToSet); diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_static.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_static.c index 49dd64df6507c..c81ce59e50aca 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_static.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_static.c @@ -22,10 +22,19 @@ static int32_t isLoaded = 0; static int32_t isDataSet = 0; +static void log_shim_error(const char* format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + static void log_icu_error(const char* name, UErrorCode status) { const char * statusText = u_errorName(status); - fprintf(stderr, "ICU call %s failed with error #%d '%s'.\n", name, status, statusText); + log_shim_error("ICU call %s failed with error #%d '%s'.\n", name, status, statusText); } static void U_CALLCONV icu_trace_data(const void* context, int32_t fnNumber, int32_t level, const char* fmt, va_list args) @@ -89,45 +98,60 @@ static int32_t load_icu_data(void* pData) } } -int32_t GlobalizationNative_LoadICUData(char* path) +int32_t GlobalizationNative_LoadICUData(const char* path) { int32_t ret = -1; char* icu_data; - FILE *fp = fopen (path, "rb"); + FILE *fp = fopen(path, "rb"); if (fp == NULL) { - fprintf (stderr, "Unable to load ICU dat file '%s'.", path); + log_shim_error("Unable to load ICU dat file '%s'.", path); return ret; } - if (fseek (fp, 0L, SEEK_END) != 0) { - fprintf (stderr, "Unable to determine size of the dat file"); + if (fseek(fp, 0L, SEEK_END) != 0) { + fclose(fp); + log_shim_error("Unable to determine size of the dat file"); return ret; } - long bufsize = ftell (fp); + long bufsize = ftell(fp); if (bufsize == -1) { - fprintf (stderr, "Unable to determine size of the ICU dat file."); + fclose(fp); + log_shim_error("Unable to determine size of the ICU dat file."); return ret; } - icu_data = malloc (sizeof (char) * (bufsize + 1)); + icu_data = malloc(sizeof(char) * (bufsize + 1)); + + if (icu_data == NULL) { + fclose(fp); + log_shim_error("Unable to allocate enough to read the ICU dat file"); + return ret; + } - if (fseek (fp, 0L, SEEK_SET) != 0) { - fprintf (stderr, "Unable to seek ICU dat file."); + if (fseek(fp, 0L, SEEK_SET) != 0) { + fclose(fp); + log_shim_error("Unable to seek ICU dat file."); return ret; } - fread (icu_data, sizeof (char), bufsize, fp); - if (ferror ( fp ) != 0 ) { - fprintf (stderr, "Unable to read ICU dat file"); + fread(icu_data, sizeof(char), bufsize, fp); + if (ferror( fp ) != 0 ) { + fclose(fp); + log_shim_error("Unable to read ICU dat file"); return ret; } - fclose (fp); + fclose(fp); + + if (load_icu_data(icu_data) == 0) { + log_shim_error("ICU BAD EXIT %d.", ret); + return ret; + } - return load_icu_data (icu_data); + return GlobalizationNative_LoadICU(); } const char* GlobalizationNative_GetICUDTName(const char* culture) diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c index 09c2c1ca5092b..9173931716c0e 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c @@ -4,41 +4,19 @@ #include #include +#include #include "pal_errors_internal.h" #include "pal_locale_internal.h" #include "pal_timeZoneInfo.h" -/* -Gets the localized display name for the specified time zone. -*/ -ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName, - const UChar* timeZoneId, - TimeZoneDisplayNameType type, - UChar* result, - int32_t resultLength) -{ - UErrorCode err = U_ZERO_ERROR; - char locale[ULOC_FULLNAME_CAPACITY]; - GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err); - - int32_t timeZoneIdLength = -1; // timeZoneId is NULL-terminated - UCalendar* calendar = ucal_open(timeZoneId, timeZoneIdLength, locale, UCAL_DEFAULT, &err); +#define DISPLAY_NAME_LENGTH 256 // arbitrarily large, to be safe +#define TZID_LENGTH 64 // arbitrarily large, to be safe - // TODO (https://github.com/dotnet/runtime/issues/16232): need to support Generic names, but ICU "C" api - // has no public option for this. For now, just use the ICU standard name for both Standard and Generic - // (which is the same behavior on Windows with the mincore TIME_ZONE_INFORMATION APIs). - ucal_getTimeZoneDisplayName( - calendar, - type == TimeZoneDisplayName_DaylightSavings ? UCAL_DST : UCAL_STANDARD, - locale, - result, - resultLength, - &err); - - ucal_close(calendar); - return GetResultCode(err); -} +// For descriptions of the following patterns, see https://unicode-org.github.io/icu/userguide/format_parse/datetime/#date-field-symbol-table +static const UChar GENERIC_PATTERN_UCHAR[] = {'v', 'v', 'v', 'v', '\0'}; // u"vvvv" +static const UChar GENERIC_LOCATION_PATTERN_UCHAR[] = {'V', 'V', 'V', 'V', '\0'}; // u"VVVV" +static const UChar EXEMPLAR_CITY_PATTERN_UCHAR[] = {'V', 'V', 'V', '\0'}; // u"VVV" /* Convert Windows Time Zone Id to IANA Id @@ -80,3 +58,287 @@ int32_t GlobalizationNative_IanaIdToWindowsId(const UChar* ianaId, UChar* window // Failed return 0; } + +/* +Private function to get the standard and daylight names from the ICU Calendar API. +*/ +static void GetTimeZoneDisplayName_FromCalendar(const char* locale, const UChar* timeZoneId, const UDate timestamp, UCalendarDisplayNameType type, UChar* result, int32_t resultLength, UErrorCode* err) +{ + // Examples: "Pacific Standard Time" (standard) + // "Pacific Daylight Time" (daylight) + + // (-1 == timeZoneId is null terminated) + UCalendar* calendar = ucal_open(timeZoneId, -1, locale, UCAL_DEFAULT, err); + if (U_SUCCESS(*err)) + { + ucal_setMillis(calendar, timestamp, err); + if (U_SUCCESS(*err)) + { + ucal_getTimeZoneDisplayName(calendar, type, locale, result, resultLength, err); + } + + ucal_close(calendar); + } +} + +/* +Private function to get the various forms of generic time zone names using patterns with the ICU Date Formatting API. +*/ +static void GetTimeZoneDisplayName_FromPattern(const char* locale, const UChar* timeZoneId, const UDate timestamp, const UChar* pattern, UChar* result, int32_t resultLength, UErrorCode* err) +{ + // (-1 == timeZoneId and pattern are null terminated) + UDateFormat* dateFormatter = udat_open(UDAT_PATTERN, UDAT_PATTERN, locale, timeZoneId, -1, pattern, -1, err); + if (U_SUCCESS(*err)) + { + udat_format(dateFormatter, timestamp, result, resultLength, NULL, err); + udat_close(dateFormatter); + } +} + +/* +Private function to modify the generic display name to better suit our needs. +*/ +static void FixupTimeZoneGenericDisplayName(const char* locale, const UChar* timeZoneId, const UDate timestamp, UChar* genericName, UErrorCode* err) +{ + // By default, some time zones will still give a standard name instead of the generic + // non-location name. + // + // For example, given the following zones and their English results: + // America/Denver => "Mountain Time" + // America/Phoenix => "Mountain Standard Time" + // + // We prefer that all time zones in the same metazone have the same generic name, + // such that they are grouped together when combined with their base offset, location + // and sorted alphabetically. For example: + // + // (UTC-07:00) Mountain Time (Denver) + // (UTC-07:00) Mountain Time (Phoenix) + // + // Without modification, they would show as: + // + // (UTC-07:00) Mountain Standard Time (Phoenix) + // (UTC-07:00) Mountain Time (Denver) + // + // When combined with the rest of the time zones, having them not grouped together + // makes it harder to locate the correct time zone from a list. + // + // The reason we get the standard name is because TR35 (LDML) defines a rule that + // states that metazone generic names should use standard names if there is no DST + // transition within a +/- 184 day range near the timestamp being translated. + // + // See the "Type Fallback" section in: + // https://www.unicode.org/reports/tr35/tr35-dates.html#Using_Time_Zone_Names + // + // This might make sense when attached to an exact timestamp, but doesn't work well + // when using the generic name to pick a time zone from a list. + // Note that this test only happens when the generic name comes from a metazone. + // + // ICU implements this test in TZGNCore::formatGenericNonLocationName in + // https://github.com/unicode-org/icu/blob/master/icu4c/source/i18n/tzgnames.cpp + // (Note the kDstCheckRange 184-day constant.) + // + // The rest of the code below is a workaround for this issue. When the generic + // name and standard name match, we search through the other time zones for one + // having the same base offset and standard name but a shorter generic name. + // That will at least keep them grouped together, though note that if there aren't + // any found that means all of them are using the standard name. + // + // If ICU ever adds an API to get a generic name that doesn't perform the + // 184-day check on metazone names, then test for the existence of that new API + // and use that instead of this workaround. Keep the workaround for when the + // new API is not available. + + // Get the standard name for this time zone. (-1 == timeZoneId is null terminated) + // Note that we leave the calendar open and close it later so we can also get the base offset. + UChar standardName[DISPLAY_NAME_LENGTH]; + UCalendar* calendar = ucal_open(timeZoneId, -1, locale, UCAL_DEFAULT, err); + if (U_FAILURE(*err)) + { + return; + } + + ucal_setMillis(calendar, timestamp, err); + if (U_FAILURE(*err)) + { + ucal_close(calendar); + return; + } + + ucal_getTimeZoneDisplayName(calendar, UCAL_STANDARD, locale, standardName, DISPLAY_NAME_LENGTH, err); + if (U_FAILURE(*err)) + { + ucal_close(calendar); + return; + } + + // Ensure the generic name is the same as the standard name. + if (u_strcmp(genericName, standardName) != 0) + { + ucal_close(calendar); + return; + } + + // Get some details for later comparison. + const int32_t originalGenericNameActualLength = u_strlen(genericName); + const int32_t baseOffset = ucal_get(calendar, UCAL_ZONE_OFFSET, err); + if (U_FAILURE(*err)) + { + ucal_close(calendar); + return; + } + + // Allocate some additional strings for test values. + UChar testTimeZoneId[TZID_LENGTH]; + UChar testDisplayName[DISPLAY_NAME_LENGTH]; + UChar testDisplayName2[DISPLAY_NAME_LENGTH]; + + // Enumerate over all the time zones having the same base offset. + UEnumeration* pEnum = ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL_LOCATION, NULL, &baseOffset, err); + if (U_FAILURE(*err)) + { + uenum_close(pEnum); + ucal_close(calendar); + return; + } + + int count = uenum_count(pEnum, err); + if (U_FAILURE(*err)) + { + uenum_close(pEnum); + ucal_close(calendar); + return; + } + + for (int i = 0; i < count; i++) + { + // Get a time zone id from the enumeration to test with. + int32_t testIdLength; + const char* testId = uenum_next(pEnum, &testIdLength, err); + if (U_FAILURE(*err)) + { + // There shouldn't be a failure in enumeration, but if there was then exit. + uenum_close(pEnum); + ucal_close(calendar); + return; + } + + // Make a UChar[] version of the test time zone id for use in the API calls. + u_uastrcpy(testTimeZoneId, testId); + + // Get the standard name from the test time zone. + GetTimeZoneDisplayName_FromCalendar(locale, testTimeZoneId, timestamp, UCAL_STANDARD, testDisplayName, DISPLAY_NAME_LENGTH, err); + if (U_FAILURE(*err)) + { + // Failed, but keep trying through the rest of the loop in case the failure is specific to this test zone. + continue; + } + + // See if the test time zone has a different standard name. + if (u_strcmp(testDisplayName, standardName) != 0) + { + // It has a different standard name. We can't use it. + continue; + } + + // Get the generic name from the test time zone. + GetTimeZoneDisplayName_FromPattern(locale, testTimeZoneId, timestamp, GENERIC_PATTERN_UCHAR, testDisplayName, DISPLAY_NAME_LENGTH, err); + if (U_FAILURE(*err)) + { + // Failed, but keep trying through the rest of the loop in case the failure is specific to this test zone. + continue; + } + + // See if the test time zone has a longer (or same size) generic name. + if (u_strlen(testDisplayName) >= originalGenericNameActualLength) + { + // The test time zone's generic name isn't any shorter than the one we already have. + continue; + } + + // We probably have found a better generic name. But just to be safe, make sure the test zone isn't + // using a generic name that is specific to a particular location. For example, "Antarctica/Troll" + // uses "Troll Time" as a generic name, but "Greenwich Mean Time" as a standard name. We don't + // want other zones that use "Greenwich Mean Time" to be labeled as "Troll Time". + + GetTimeZoneDisplayName_FromPattern(locale, testTimeZoneId, timestamp, GENERIC_LOCATION_PATTERN_UCHAR, testDisplayName2, DISPLAY_NAME_LENGTH, err); + if (U_FAILURE(*err)) + { + // Failed, but keep trying through the rest of the loop in case the failure is specific to this test zone. + continue; + } + + if (u_strcmp(testDisplayName, testDisplayName2) != 0) + { + // We have found a better generic name. Use it. + u_strcpy(genericName, testDisplayName); + break; + } + } + + uenum_close(pEnum); + ucal_close(calendar); +} + +/* +Gets the localized display name that is currently in effect for the specified time zone. +*/ +ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName, const UChar* timeZoneId, TimeZoneDisplayNameType type, UChar* result, int32_t resultLength) +{ + UErrorCode err = U_ZERO_ERROR; + char locale[ULOC_FULLNAME_CAPACITY]; + GetLocale(localeName, locale, ULOC_FULLNAME_CAPACITY, false, &err); + if (U_FAILURE(err)) + { + return GetResultCode(err); + } + + // Note: Due to how CLDR Metazones work, a past or future timestamp might use a different set of display names + // than are currently in effect. + // + // See https://github.com/unicode-org/cldr/blob/master/common/supplemental/metaZones.xml + // + // Example: As of writing this, Africa/Algiers is in the Europe_Central metazone, + // which has a standard-time name of "Central European Standard Time" (in English). + // However, in some previous dates, it used the Europe_Western metazone, + // having the standard-time name of "Western European Standard Time" (in English). + // Only the *current* name will be returned. + // + // TODO: Add a parameter for the timestamp that is used when getting the display names instead of + // getting "now" on the following line. Everything else should be using this timestamp. + // For now, since TimeZoneInfo presently uses only a single set of display names, we will + // use the names associated with the *current* date and time. + + UDate timestamp = ucal_getNow(); + + switch (type) + { + case TimeZoneDisplayName_Standard: + GetTimeZoneDisplayName_FromCalendar(locale, timeZoneId, timestamp, UCAL_STANDARD, result, resultLength, &err); + break; + + case TimeZoneDisplayName_DaylightSavings: + GetTimeZoneDisplayName_FromCalendar(locale, timeZoneId, timestamp, UCAL_DST, result, resultLength, &err); + break; + + case TimeZoneDisplayName_Generic: + GetTimeZoneDisplayName_FromPattern(locale, timeZoneId, timestamp, GENERIC_PATTERN_UCHAR, result, resultLength, &err); + if (U_SUCCESS(err)) + { + FixupTimeZoneGenericDisplayName(locale, timeZoneId, timestamp, result, &err); + } + break; + + case TimeZoneDisplayName_GenericLocation: + GetTimeZoneDisplayName_FromPattern(locale, timeZoneId, timestamp, GENERIC_LOCATION_PATTERN_UCHAR, result, resultLength, &err); + break; + + case TimeZoneDisplayName_ExemplarCity: + GetTimeZoneDisplayName_FromPattern(locale, timeZoneId, timestamp, EXEMPLAR_CITY_PATTERN_UCHAR, result, resultLength, &err); + break; + + default: + return UnknownError; + } + + return GetResultCode(err); +} diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h index c9fe188979c11..49bfb6250eb50 100644 --- a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h +++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h @@ -16,13 +16,10 @@ typedef enum TimeZoneDisplayName_Generic = 0, TimeZoneDisplayName_Standard = 1, TimeZoneDisplayName_DaylightSavings = 2, + TimeZoneDisplayName_GenericLocation = 3, + TimeZoneDisplayName_ExemplarCity = 4, } TimeZoneDisplayNameType; -PALEXPORT ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName, - const UChar* timeZoneId, - TimeZoneDisplayNameType type, - UChar* result, - int32_t resultLength); - PALEXPORT int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, UChar* ianaId, int32_t ianaIdLength); PALEXPORT int32_t GlobalizationNative_IanaIdToWindowsId(const UChar* ianaId, UChar* windowsId, int32_t windowsIdLength); +PALEXPORT ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName, const UChar* timeZoneId, TimeZoneDisplayNameType type, UChar* result, int32_t resultLength); diff --git a/src/libraries/Native/Unix/System.IO.Compression.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.IO.Compression.Native/CMakeLists.txt index e45d404b89519..f68e1acef69b0 100644 --- a/src/libraries/Native/Unix/System.IO.Compression.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.IO.Compression.Native/CMakeLists.txt @@ -100,4 +100,4 @@ add_library(System.IO.Compression.Native-Static set_target_properties(System.IO.Compression.Native-Static PROPERTIES OUTPUT_NAME System.IO.Compression.Native CLEAN_DIRECT_OUTPUT 1) -install (TARGETS System.IO.Compression.Native-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.IO.Compression.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Unix/System.IO.Ports.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.IO.Ports.Native/CMakeLists.txt index ede448009f2e9..b78dce7aa2e0a 100644 --- a/src/libraries/Native/Unix/System.IO.Ports.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.IO.Ports.Native/CMakeLists.txt @@ -21,4 +21,4 @@ if (GEN_SHARED_LIB) install_with_stripped_symbols (System.IO.Ports.Native PROGRAMS .) endif() -install (TARGETS System.IO.Ports.Native-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.IO.Ports.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index 2ff29dafa4883..12245f802024f 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -92,4 +92,4 @@ add_library(System.Native-Static set_target_properties(System.Native-Static PROPERTIES OUTPUT_NAME System.Native CLEAN_DIRECT_OUTPUT 1) -install (TARGETS System.Native-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 2e819aaa5c455..11124f34c29bb 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -2031,7 +2031,14 @@ int32_t SystemNative_GetSockOpt( { if (socketOptionName == SocketOptionName_SO_IP_DONTFRAGMENT) { - *optionValue = *optionValue == IP_PMTUDISC_DO ? 1 : 0; + if (optLen >= (socklen_t)sizeof(int)) + { + *(int*)optionValue = *(int*)optionValue == IP_PMTUDISC_DO ? 1 : 0; + } + else + { + *optionValue = *optionValue == IP_PMTUDISC_DO ? 1 : 0; + } } } #endif @@ -2139,7 +2146,14 @@ SystemNative_SetSockOpt(intptr_t socket, int32_t socketOptionLevel, int32_t sock { if (socketOptionName == SocketOptionName_SO_IP_DONTFRAGMENT) { - *optionValue = *optionValue != 0 ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; + if ((socklen_t)optionLen >= (socklen_t)sizeof(int)) + { + *(int*)optionValue = *(int*)optionValue != 0 ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; + } + else + { + *optionValue = *optionValue != 0 ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; + } } } #endif diff --git a/src/libraries/Native/Unix/System.Native/pal_random.c b/src/libraries/Native/Unix/System.Native/pal_random.c index b824b2968f95c..700435afb19b3 100644 --- a/src/libraries/Native/Unix/System.Native/pal_random.c +++ b/src/libraries/Native/Unix/System.Native/pal_random.c @@ -35,7 +35,7 @@ void SystemNative_GetNonCryptographicallySecureRandomBytes(uint8_t* buffer, int3 if (!sInitializedMRand) { - srand48(time(NULL)); + srand48((long int)time(NULL)); sInitializedMRand = true; } diff --git a/src/libraries/Native/Unix/System.Native/pal_threading.c b/src/libraries/Native/Unix/System.Native/pal_threading.c index 937edd1ce16e0..6f69d1056b8f1 100644 --- a/src/libraries/Native/Unix/System.Native/pal_threading.c +++ b/src/libraries/Native/Unix/System.Native/pal_threading.c @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(TARGET_OSX) // So we can use the declaration of pthread_cond_timedwait_relative_np @@ -169,13 +170,25 @@ int32_t SystemNative_LowLevelMonitor_TimedWait(LowLevelMonitor *monitor, int32_t #if HAVE_CLOCK_GETTIME_NSEC_NP timeoutTimeSpec.tv_sec = timeoutMilliseconds / 1000; timeoutTimeSpec.tv_nsec = (timeoutMilliseconds % 1000) * 1000 * 1000; + error = pthread_cond_timedwait_relative_np(&monitor->Condition, &monitor->Mutex, &timeoutTimeSpec); #else +#if HAVE_PTHREAD_CONDATTR_SETCLOCK && HAVE_CLOCK_MONOTONIC error = clock_gettime(CLOCK_MONOTONIC, &timeoutTimeSpec); assert(error == 0); +#else + struct timeval tv; + + error = gettimeofday(&tv, NULL); + assert(error == 0); + + timeoutTimeSpec.tv_sec = tv.tv_sec; + timeoutTimeSpec.tv_nsec = tv.tv_usec * 1000; +#endif uint64_t nanoseconds = (uint64_t)timeoutMilliseconds * 1000 * 1000 + (uint64_t)timeoutTimeSpec.tv_nsec; timeoutTimeSpec.tv_sec += nanoseconds / (1000 * 1000 * 1000); timeoutTimeSpec.tv_nsec = nanoseconds % (1000 * 1000 * 1000); + error = pthread_cond_timedwait(&monitor->Condition, &monitor->Mutex, &timeoutTimeSpec); #endif assert(error == 0 || error == ETIMEDOUT); diff --git a/src/libraries/Native/Unix/System.Net.Security.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Net.Security.Native/CMakeLists.txt index 3c03fa1696793..6fbe18f76872e 100644 --- a/src/libraries/Native/Unix/System.Net.Security.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Net.Security.Native/CMakeLists.txt @@ -49,4 +49,4 @@ if (GEN_SHARED_LIB) install_with_stripped_symbols (System.Net.Security.Native PROGRAMS .) endif() -install (TARGETS System.Net.Security.Native-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.Net.Security.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt index 51b1466554dc1..5ec5a520e6323 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/CMakeLists.txt @@ -23,6 +23,8 @@ set(NATIVECRYPTO_SOURCES pal_ssl.c pal_sslstream.c pal_x509.c + pal_x509chain.c + pal_x509store.c ) add_library(System.Security.Cryptography.Native.Android diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c index 684cf6f8b4c57..6280b4fd5c007 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.c @@ -3,17 +3,6 @@ #include "pal_bignum.h" -jobject AndroidCryptoNative_BigNumFromBinary(uint8_t* bytes, int32_t len) -{ - // return new BigInteger(bytes) - JNIEnv* env = GetJNIEnv(); - jbyteArray buffArray = (*env)->NewByteArray(env, len); - (*env)->SetByteArrayRegion(env, buffArray, 0, len, (jbyte*)bytes); - jobject bigNum = (*env)->NewObject(env, g_bigNumClass, g_bigNumCtorWithSign, 1, buffArray); - (*env)->DeleteLocalRef(env, buffArray); - return CheckJNIExceptions(env) ? FAIL : ToGRef(env, bigNum); -} - int32_t AndroidCryptoNative_BigNumToBinary(jobject bignum, uint8_t* output) { // bigNum.toByteArray() @@ -45,6 +34,17 @@ int32_t AndroidCryptoNative_GetBigNumBytes(jobject bignum) return CheckJNIExceptions(env) ? FAIL : (int32_t)bytesLen; } +jobject AndroidCryptoNative_BigNumFromBinary(uint8_t* bytes, int32_t len) +{ + // return new BigInteger(bytes) + JNIEnv* env = GetJNIEnv(); + jbyteArray buffArray = (*env)->NewByteArray(env, len); + (*env)->SetByteArrayRegion(env, buffArray, 0, len, (jbyte*)bytes); + jobject bigNum = (*env)->NewObject(env, g_bigNumClass, g_bigNumCtorWithSign, 1, buffArray); + (*env)->DeleteLocalRef(env, buffArray); + return CheckJNIExceptions(env) ? FAIL : bigNum; +} + int32_t AndroidCryptoNative_GetBigNumBytesIncludingPaddingByteForSign(jobject bignum) { // Use the array here to get the leading zero byte if it exists. diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.h index ed2b9de58dd4f..0abfc619f446b 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_bignum.h @@ -5,8 +5,13 @@ #include "pal_jni.h" -PALEXPORT jobject AndroidCryptoNative_BigNumFromBinary(uint8_t* bytes, int32_t len); PALEXPORT int32_t AndroidCryptoNative_BigNumToBinary(jobject bignum, uint8_t* output); PALEXPORT int32_t AndroidCryptoNative_GetBigNumBytes(jobject bignum); +/* +Create a BigInteger from its binary representation. + +The returned jobject will be a local reference. +*/ +jobject AndroidCryptoNative_BigNumFromBinary(uint8_t* bytes, int32_t len); int32_t AndroidCryptoNative_GetBigNumBytesIncludingPaddingByteForSign(jobject bignum); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_dsa.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_dsa.c index 1a220d0c2e054..eb9190de8facc 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_dsa.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_dsa.c @@ -7,18 +7,6 @@ #include "pal_bignum.h" #include "pal_misc.h" -#define INIT_LOCALS(name, ...) \ - enum { __VA_ARGS__, count_##name }; \ - jobject name[count_##name] = { 0 } \ - -#define RELEASE_LOCALS_ENV(name, releaseFn) \ -do { \ - for (int i = 0; i < count_##name; ++i) \ - { \ - releaseFn(env, name[i]); \ - } \ -} while(0) - int32_t AndroidCryptoNative_DsaGenerateKey(jobject* dsa, int32_t bits) { assert(dsa); @@ -56,21 +44,22 @@ static jobject GetQParameter(JNIEnv* env, jobject dsa) { assert(dsa); + jobject ret = NULL; + INIT_LOCALS(loc, algName, keyFactory, publicKey, publicKeySpec); loc[algName] = JSTRING("DSA"); loc[keyFactory] = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, loc[algName]); loc[publicKey] = (*env)->CallObjectMethod(env, dsa, g_keyPairGetPublicMethod); loc[publicKeySpec] = (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGetKeySpecMethod, loc[publicKey], g_DSAPublicKeySpecClass); - ON_EXCEPTION_PRINT_AND_GOTO(error); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); jobject q = (*env)->CallObjectMethod(env, loc[publicKeySpec], g_DSAPublicKeySpecGetQ); - ON_EXCEPTION_PRINT_AND_GOTO(error); - - return q; + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + ret = q; -error: +cleanup: RELEASE_LOCALS_ENV(loc, ReleaseLRef); - return FAIL; + return ret; } int32_t AndroidCryptoNative_DsaSizeSignature(jobject dsa) @@ -226,7 +215,7 @@ int32_t AndroidCryptoNative_GetDsaParameters( assert(gLength); assert(yLength); assert(xLength); - + JNIEnv* env = GetJNIEnv(); INIT_LOCALS(loc, algName, keyFactory, publicKey, publicKeySpec, privateKey, privateKeySpec); @@ -283,7 +272,7 @@ int32_t AndroidCryptoNative_DsaKeyCreateByExplicitParameters( assert(false); return 0; } - + JNIEnv* env = GetJNIEnv(); INIT_LOCALS(bn, P, Q, G, Y, X); @@ -324,7 +313,7 @@ int32_t AndroidCryptoNative_DsaKeyCreateByExplicitParameters( error: returnValue = FAIL; cleanup: - RELEASE_LOCALS_ENV(bn, ReleaseGRef); + RELEASE_LOCALS_ENV(bn, ReleaseLRef); RELEASE_LOCALS_ENV(loc, ReleaseLRef); return returnValue; diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ecc_import_export.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ecc_import_export.c index 596402f557230..924e6be61a9ea 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ecc_import_export.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ecc_import_export.c @@ -8,19 +8,6 @@ #include "pal_utilities.h" #include "pal_misc.h" - -#define INIT_LOCALS(name, ...) \ - enum { __VA_ARGS__, count_##name }; \ - jobject name[count_##name] = { 0 } \ - -#define RELEASE_LOCALS_ENV(name, releaseFn) \ -do { \ - for (int i = 0; i < count_##name; ++i) \ - { \ - releaseFn(env, name[i]); \ - } \ -} while(0) - int32_t AndroidCryptoNative_GetECKeyParameters(const EC_KEY* key, int32_t includePrivate, jobject* qx, @@ -253,10 +240,12 @@ int32_t AndroidCryptoNative_GetECCurveParameters(const EC_KEY* key, ReleaseGRef(env, *seed); *p = *a = *b = *gx = *gy = *order = *cofactor = *seed = NULL; - RELEASE_LOCALS_ENV(loc, ReleaseLRef); + // Clear local BigInteger instances. On success, these are converted to global + // references for the out variables, so the local release is only on error. RELEASE_LOCALS_ENV(bn, ReleaseLRef); exit: + RELEASE_LOCALS_ENV(loc, ReleaseLRef); return rc; } @@ -328,19 +317,21 @@ static jobject AndroidCryptoNative_CreateKeyPairFromCurveParameters( goto cleanup; error: - if (loc[privateKey]) + if (loc[privateKey] && (*env)->IsInstanceOf(env, loc[privateKey], g_DestroyableClass)) { // Destroy the private key data. (*env)->CallVoidMethod(env, loc[privateKey], g_destroy); - CheckJNIExceptions(env); // The destroy call might throw an exception. Clear the exception state. + (void)TryClearJNIExceptions(env); // The destroy call might throw an exception. Clear the exception state. } cleanup: - RELEASE_LOCALS_ENV(bn, ReleaseGRef); + RELEASE_LOCALS_ENV(bn, ReleaseLRef); RELEASE_LOCALS_ENV(loc, ReleaseLRef); return keyPair; } +#define CURVE_NOT_SUPPORTED -1 + int32_t AndroidCryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, const char* oid, uint8_t* qx, @@ -365,7 +356,7 @@ int32_t AndroidCryptoNative_EcKeyCreateByKeyParameters(EC_KEY** key, *key = AndroidCryptoNative_EcKeyCreateByOid(oid); if (*key == NULL) { - return FAIL; + return CURVE_NOT_SUPPORTED; } // Release the reference to the generated key pair. We're going to make our own with the explicit keys. @@ -555,7 +546,7 @@ EC_KEY* AndroidCryptoNative_EcKeyCreateByExplicitParameters(ECCurveType curveTyp keyInfo = AndroidCryptoNative_NewEcKey(AddGRef(env, loc[paramSpec]), keyPair); error: - RELEASE_LOCALS_ENV(bn, ReleaseGRef); + RELEASE_LOCALS_ENV(bn, ReleaseLRef); RELEASE_LOCALS_ENV(loc, ReleaseLRef); return keyInfo; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.c index d5249bf1b8fb0..f14bfc9f640cf 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.c @@ -19,7 +19,7 @@ EC_KEY* AndroidCryptoNative_NewEcKey(jobject curveParameters, jobject keyPair) return keyInfo; } -EC_KEY* AndroidCryptoNative_NewEcKeyFromPublicKey(JNIEnv *env, jobject /*ECPublicKey*/ publicKey) +EC_KEY* AndroidCryptoNative_NewEcKeyFromKeys(JNIEnv *env, jobject /*ECPublicKey*/ publicKey, jobject /*ECPrivateKey*/ privateKey) { assert(publicKey != NULL); @@ -27,7 +27,7 @@ EC_KEY* AndroidCryptoNative_NewEcKeyFromPublicKey(JNIEnv *env, jobject /*ECPubli return NULL; jobject curveParameters = (*env)->CallObjectMethod(env, publicKey, g_ECPublicKeyGetParams); - return AndroidCryptoNative_NewEcKey(ToGRef(env, curveParameters), AndroidCryptoNative_CreateKeyPair(env, publicKey, NULL)); + return AndroidCryptoNative_NewEcKey(ToGRef(env, curveParameters), AndroidCryptoNative_CreateKeyPair(env, publicKey, privateKey)); } #pragma clang diagnostic push @@ -45,11 +45,11 @@ void AndroidCryptoNative_EcKeyDestroy(EC_KEY* r) { // Destroy the private key data. jobject privateKey = (*env)->CallObjectMethod(env, r->keyPair, g_keyPairGetPrivateMethod); - if (privateKey) + if (privateKey && (*env)->IsInstanceOf(env, privateKey, g_DestroyableClass)) { (*env)->CallVoidMethod(env, privateKey, g_destroy); ReleaseLRef(env, privateKey); - CheckJNIExceptions(env); // The destroy call might throw an exception. Clear the exception state. + (void)TryClearJNIExceptions(env); // The destroy call might throw an exception. Clear the exception state. } } @@ -137,6 +137,7 @@ EC_KEY* AndroidCryptoNative_EcKeyCreateByOid(const char* oid) } jobject curveParameters = (*env)->CallObjectMethod(env, keySpec, g_ECPublicKeySpecGetParams); + ReleaseLRef(env, keySpec); return AndroidCryptoNative_NewEcKey(ToGRef(env, curveParameters), ToGRef(env, keyPair)); } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.h index 14692ab289200..212715520e4f7 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_eckey.h @@ -16,7 +16,7 @@ typedef struct EC_KEY } EC_KEY; EC_KEY* AndroidCryptoNative_NewEcKey(jobject curveParameters, jobject keyPair); -EC_KEY* AndroidCryptoNative_NewEcKeyFromPublicKey(JNIEnv *env, jobject /*ECPublicKey*/ publicKey); +EC_KEY* AndroidCryptoNative_NewEcKeyFromKeys(JNIEnv *env, jobject /*ECPublicKey*/ publicKey, jobject /*ECPrivateKey*/ privateKey); /* Cleans up and deletes an EC_KEY instance. diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c index 5721eedc37b8b..d166c23dd8645 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_jni.h" +#include JavaVM* gJvm; @@ -14,10 +15,10 @@ jmethodID g_ByteArrayInputStreamReset; jclass g_Enum; jmethodID g_EnumOrdinal; -// java/security/Key -jclass g_KeyClass; -jmethodID g_KeyGetAlgorithm; -jmethodID g_KeyGetEncoded; +// java/lang/Throwable +jclass g_ThrowableClass; +jmethodID g_ThrowableGetCause; +jmethodID g_ThrowableGetMessage; // java/security/SecureRandom jclass g_randClass; @@ -85,6 +86,12 @@ jmethodID g_sslCtxGetDefaultSslParamsMethod; jclass g_GCMParameterSpecClass; jmethodID g_GCMParameterSpecCtor; +// java/security/interfaces/DSAKey +jclass g_DSAKeyClass; + +// java/security/interfaces/ECKey +jclass g_ECKeyClass; + // java/security/interfaces/RSAKey jclass g_RSAKeyClass; jmethodID g_RSAKeyGetModulus; @@ -106,6 +113,27 @@ jmethodID g_keyPairGenInitializeWithParamsMethod; jmethodID g_keyPairGenInitializeMethod; jmethodID g_keyPairGenGenKeyPairMethod; +// java/security/KeyStore +jclass g_KeyStoreClass; +jmethodID g_KeyStoreGetInstance; +jmethodID g_KeyStoreAliases; +jmethodID g_KeyStoreContainsAlias; +jmethodID g_KeyStoreDeleteEntry; +jmethodID g_KeyStoreGetCertificate; +jmethodID g_KeyStoreGetEntry; +jmethodID g_KeyStoreLoad; +jmethodID g_KeyStoreSetCertificateEntry; +jmethodID g_KeyStoreSetKeyEntry; + +// java/security/KeyStore$PrivateKeyEntry +jclass g_PrivateKeyEntryClass; +jmethodID g_PrivateKeyEntryGetCertificate; +jmethodID g_PrivateKeyEntryGetPrivateKey; + +// java/security/KeyStore$TrustedCertificateEntry +jclass g_TrustedCertificateEntryClass; +jmethodID g_TrustedCertificateEntryGetTrustedCertificate; + // java/security/Signature jclass g_SignatureClass; jmethodID g_SignatureGetInstance; @@ -122,17 +150,80 @@ jmethodID g_CertFactoryGenerateCertificate; jmethodID g_CertFactoryGenerateCertificates; jmethodID g_CertFactoryGenerateCertPathFromList; jmethodID g_CertFactoryGenerateCertPathFromStream; -jmethodID g_CertFactoryGenerateCRL; // java/security/cert/CertPath jclass g_CertPathClass; jmethodID g_CertPathGetEncoded; +jmethodID g_CertPathGetCertificates; + +// java/security/cert/CertPathBuilder +jclass g_CertPathBuilderClass; +jmethodID g_CertPathBuilderGetInstance; +jmethodID g_CertPathBuilderBuild; + +// java/security/cert/CertPathValidator +jclass g_CertPathValidatorClass; +jmethodID g_CertPathValidatorGetInstance; +jmethodID g_CertPathValidatorValidate; +jmethodID g_CertPathValidatorGetRevocationChecker; // only in API level 24+ + +// java/security/cert/CertPathValidatorException +jclass g_CertPathValidatorExceptionClass; +jmethodID g_CertPathValidatorExceptionGetIndex; +jmethodID g_CertPathValidatorExceptionGetReason; // only in API level 24+ + +// java/security/cert/CertPathValidatorException$BasicReason - only in API level 24+ +jclass g_CertPathExceptionBasicReasonClass; + +// java/security/cert/CertStore +jclass g_CertStoreClass; +jmethodID g_CertStoreGetInstance; + +// java/security/cert/CollectionCertStoreParameters +jclass g_CollectionCertStoreParametersClass; +jmethodID g_CollectionCertStoreParametersCtor; + +// java/security/cert/PKIXBuilderParameters +jclass g_PKIXBuilderParametersClass; +jmethodID g_PKIXBuilderParametersCtor; +jmethodID g_PKIXBuilderParametersAddCertStore; +jmethodID g_PKIXBuilderParametersAddCertPathChecker; +jmethodID g_PKIXBuilderParametersSetDate; +jmethodID g_PKIXBuilderParametersSetRevocationEnabled; +jmethodID g_PKIXBuilderParametersSetTrustAnchors; + +// java/security/cert/PKIXCertPathBuilderResult +jclass g_PKIXCertPathBuilderResultClass; +jmethodID g_PKIXCertPathBuilderResultGetCertPath; +jmethodID g_PKIXCertPathBuilderResultGetTrustAnchor; + +// java/security/cert/PKIXReason - only in API level 24+ +jclass g_PKIXReasonClass; + +// java/security/cert/PKIXRevocationChecker - only in API level 24+ +jclass g_PKIXRevocationCheckerClass; +jmethodID g_PKIXRevocationCheckerSetOptions; + +// java/security/cert/PKIXRevocationChecker$Option - only in API level 24+ +jclass g_PKIXRevocationCheckerOptionClass; +jfieldID g_PKIXRevocationCheckerOptionOnlyEndEntity; + +// java/security/cert/TrustAnchor +jclass g_TrustAnchorClass; +jclass g_TrustAnchorCtor; +jmethodID g_TrustAnchorGetTrustedCert; // java/security/cert/X509Certificate jclass g_X509CertClass; +jmethodID g_X509CertEquals; jmethodID g_X509CertGetEncoded; jmethodID g_X509CertGetPublicKey; +// java/security/cert/X509CertSelector +jclass g_X509CertSelectorClass; +jmethodID g_X509CertSelectorCtor; +jmethodID g_X509CertSelectorSetCertificate; + // java/security/interfaces/RSAPrivateCrtKey jclass g_RSAPrivateCrtKeyClass; jmethodID g_RSAPrivateCrtKeyPubExpField; @@ -243,6 +334,8 @@ jmethodID g_destroy; // java/util/ArrayList jclass g_ArrayListClass; jmethodID g_ArrayListCtor; +jmethodID g_ArrayListCtorWithCapacity; +jmethodID g_ArrayListCtorWithCollection; jmethodID g_ArrayListAdd; // java/util/Collection @@ -252,16 +345,26 @@ jmethodID g_CollectionSize; // java/util/Date jclass g_DateClass; -jmethodID g_DateGetTime; +jmethodID g_DateCtor; + +// java/util/Enumeration +jclass g_Enumeration; +jmethodID g_EnumerationHasMoreElements; +jmethodID g_EnumerationNextElement; + +// java/util/HashSet +jclass g_HashSetClass; +jmethodID g_HashSetCtorWithCapacity; +jmethodID g_HashSetAdd; // java/util/Iterator jclass g_IteratorClass; jmethodID g_IteratorHasNext; jmethodID g_IteratorNext; -// java/util/Set -jclass g_SetClass; -jmethodID g_SetIterator; +// java/util/List +jclass g_ListClass; +jmethodID g_ListGet; // javax/net/ssl/SSLEngine jclass g_SSLEngine; @@ -307,11 +410,6 @@ jmethodID g_SSLEngineResultGetHandshakeStatusMethod; // javax/net/ssl/TrustManager jclass g_TrustManager; -// javax/security/auth/x500/X500Principal -jclass g_X500PrincipalClass; -jmethodID g_X500PrincipalGetEncoded; -jmethodID g_X500PrincipalHashCode; - // javax/crypto/KeyAgreement jclass g_KeyAgreementClass; jmethodID g_KeyAgreementGetInstance; @@ -349,14 +447,40 @@ void ReleaseLRef(JNIEnv *env, jobject lref) (*env)->DeleteLocalRef(env, lref); } -jclass GetClassGRef(JNIEnv *env, const char* name) +static bool TryGetClassGRef(JNIEnv *env, const char* name, jclass* out) { + *out = NULL; LOG_DEBUG("Finding %s class", name); - jclass klass = ToGRef(env, (*env)->FindClass (env, name)); - if (!klass) { + jclass klass = (*env)->FindClass (env, name); + if (klass == NULL) + return false; + + *out = ToGRef(env, klass); + return true; +} + +jclass GetClassGRef(JNIEnv *env, const char* name) +{ + jclass klass = NULL; + if (!TryGetClassGRef(env, name, &klass)) + { LOG_ERROR("class %s was not found", name); - assert(klass); } + + assert(klass); + return klass; +} + +static jclass GetOptionalClassGRef(JNIEnv *env, const char* name) +{ + jclass klass = NULL; + if (!TryGetClassGRef(env, name, &klass)) + { + LOG_DEBUG("optional class %s was not found", name); + // Failing to find an optional class causes an exception state, which we need to clear. + TryClearJNIExceptions(env); + } + return klass; } @@ -371,6 +495,32 @@ bool CheckJNIExceptions(JNIEnv* env) return false; } +bool TryClearJNIExceptions(JNIEnv* env) +{ + if ((*env)->ExceptionCheck(env)) + { + (*env)->ExceptionClear(env); + return true; + } + + return false; +} + +bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException) +{ + if (!(*env)->ExceptionCheck(env)) + return false; + + if (printException) + { + (*env)->ExceptionDescribe(env); + } + + *ex = (*env)->ExceptionOccurred(env); + (*env)->ExceptionClear(env); + return true; +} + void AssertOnJNIExceptions(JNIEnv* env) { assert(!CheckJNIExceptions(env)); @@ -404,6 +554,8 @@ jmethodID GetOptionalMethod(JNIEnv *env, bool isStatic, jclass klass, const char jmethodID mid = isStatic ? (*env)->GetStaticMethodID(env, klass, name, sig) : (*env)->GetMethodID(env, klass, name, sig); if (!mid) { LOG_INFO("optional method %s %s was not found", name, sig); + // Failing to find an optional method causes an exception state, which we need to clear. + TryClearJNIExceptions(env); } return mid; } @@ -419,6 +571,22 @@ jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, co return fid; } +static void DetatchThreadFromJNI(void* unused) +{ + LOG_DEBUG("Detaching thread from JNI"); + (void)unused; + (*gJvm)->DetachCurrentThread(gJvm); +} + +static pthread_key_t threadLocalEnvKey; +static pthread_once_t threadLocalEnvInitKey = PTHREAD_ONCE_INIT; + +static void +make_key() +{ + (void) pthread_key_create(&threadLocalEnvKey, &DetatchThreadFromJNI); +} + JNIEnv* GetJNIEnv() { JNIEnv *env; @@ -426,6 +594,11 @@ JNIEnv* GetJNIEnv() if (env) return env; jint ret = (*gJvm)->AttachCurrentThreadAsDaemon(gJvm, &env, NULL); + + (void) pthread_once(&threadLocalEnvInitKey, make_key); + LOG_DEBUG("Registering JNI thread detach. env ptr %p. Key: %ld", (void*)env, (long)threadLocalEnvKey); + pthread_setspecific(threadLocalEnvKey, env); + assert(ret == JNI_OK && "Unable to attach thread to JVM"); (void)ret; return env; @@ -455,9 +628,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_Enum = GetClassGRef(env, "java/lang/Enum"); g_EnumOrdinal = GetMethod(env, false, g_Enum, "ordinal", "()I"); - g_KeyClass = GetClassGRef(env, "java/security/Key"); - g_KeyGetAlgorithm = GetMethod(env, false, g_KeyClass, "getAlgorithm", "()Ljava/lang/String;"); - g_KeyGetEncoded = GetMethod(env, false, g_KeyClass, "getEncoded", "()[B"); + g_ThrowableClass = GetClassGRef(env, "java/lang/Throwable"); + g_ThrowableGetCause = GetMethod(env, false, g_ThrowableClass, "getCause", "()Ljava/lang/Throwable;"); + g_ThrowableGetMessage = GetMethod(env, false, g_ThrowableClass, "getMessage", "()Ljava/lang/String;"); g_randClass = GetClassGRef(env, "java/security/SecureRandom"); g_randCtor = GetMethod(env, false, g_randClass, "", "()V"); @@ -521,14 +694,71 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_CertFactoryGenerateCertificates = GetMethod(env, false, g_CertFactoryClass, "generateCertificates", "(Ljava/io/InputStream;)Ljava/util/Collection;"); g_CertFactoryGenerateCertPathFromList = GetMethod(env, false, g_CertFactoryClass, "generateCertPath", "(Ljava/util/List;)Ljava/security/cert/CertPath;"); g_CertFactoryGenerateCertPathFromStream = GetMethod(env, false, g_CertFactoryClass, "generateCertPath", "(Ljava/io/InputStream;Ljava/lang/String;)Ljava/security/cert/CertPath;"); - g_CertFactoryGenerateCRL = GetMethod(env, false, g_CertFactoryClass, "generateCRL", "(Ljava/io/InputStream;)Ljava/security/cert/CRL;"); - g_CertPathClass = GetClassGRef(env, "java/security/cert/CertPath"); - g_CertPathGetEncoded = GetMethod(env, false, g_CertPathClass, "getEncoded", "(Ljava/lang/String;)[B"); + g_CertPathClass = GetClassGRef(env, "java/security/cert/CertPath"); + g_CertPathGetEncoded = GetMethod(env, false, g_CertPathClass, "getEncoded", "(Ljava/lang/String;)[B"); + g_CertPathGetCertificates = GetMethod(env, false, g_CertPathClass, "getCertificates", "()Ljava/util/List;"); + + g_CertPathBuilderClass = GetClassGRef(env, "java/security/cert/CertPathBuilder"); + g_CertPathBuilderGetInstance = GetMethod(env, true, g_CertPathBuilderClass, "getInstance", "(Ljava/lang/String;)Ljava/security/cert/CertPathBuilder;"); + g_CertPathBuilderBuild = GetMethod(env, false, g_CertPathBuilderClass, "build", "(Ljava/security/cert/CertPathParameters;)Ljava/security/cert/CertPathBuilderResult;"); + + g_CertPathValidatorClass = GetClassGRef(env, "java/security/cert/CertPathValidator"); + g_CertPathValidatorGetInstance = GetMethod(env, true, g_CertPathValidatorClass, "getInstance", "(Ljava/lang/String;)Ljava/security/cert/CertPathValidator;"); + g_CertPathValidatorValidate = GetMethod(env, false, g_CertPathValidatorClass, "validate", "(Ljava/security/cert/CertPath;Ljava/security/cert/CertPathParameters;)Ljava/security/cert/CertPathValidatorResult;"); + g_CertPathValidatorGetRevocationChecker = GetOptionalMethod(env, false, g_CertPathValidatorClass, "getRevocationChecker", "()Ljava/security/cert/CertPathChecker;"); + + g_CertPathValidatorExceptionClass = GetClassGRef(env, "java/security/cert/CertPathValidatorException"); + g_CertPathValidatorExceptionGetIndex = GetMethod(env, false, g_CertPathValidatorExceptionClass, "getIndex", "()I"); + g_CertPathValidatorExceptionGetReason = GetOptionalMethod(env, false, g_CertPathValidatorExceptionClass, "getReason", "()Ljava/security/cert/CertPathValidatorException$Reason;"); + + g_CertPathExceptionBasicReasonClass = GetOptionalClassGRef(env, "java/security/cert/CertPathValidatorException$BasicReason"); + + g_CertStoreClass = GetClassGRef(env, "java/security/cert/CertStore"); + g_CertStoreGetInstance = GetMethod(env, true, g_CertStoreClass, "getInstance", "(Ljava/lang/String;Ljava/security/cert/CertStoreParameters;)Ljava/security/cert/CertStore;"); - g_X509CertClass = GetClassGRef(env, "java/security/cert/X509Certificate"); - g_X509CertGetEncoded = GetMethod(env, false, g_X509CertClass, "getEncoded", "()[B"); - g_X509CertGetPublicKey = GetMethod(env, false, g_X509CertClass, "getPublicKey", "()Ljava/security/PublicKey;"); + g_CollectionCertStoreParametersClass = GetClassGRef(env, "java/security/cert/CollectionCertStoreParameters"); + g_CollectionCertStoreParametersCtor = GetMethod(env, false, g_CollectionCertStoreParametersClass, "", "(Ljava/util/Collection;)V"); + + g_PKIXBuilderParametersClass = GetClassGRef(env, "java/security/cert/PKIXBuilderParameters"); + g_PKIXBuilderParametersCtor = GetMethod(env, false, g_PKIXBuilderParametersClass, "", "(Ljava/security/KeyStore;Ljava/security/cert/CertSelector;)V"); + g_PKIXBuilderParametersAddCertStore = GetMethod(env, false, g_PKIXBuilderParametersClass, "addCertStore", "(Ljava/security/cert/CertStore;)V"); + g_PKIXBuilderParametersAddCertPathChecker = GetMethod(env, false, g_PKIXBuilderParametersClass, "addCertPathChecker", "(Ljava/security/cert/PKIXCertPathChecker;)V"); + g_PKIXBuilderParametersSetDate = GetMethod(env, false, g_PKIXBuilderParametersClass, "setDate", "(Ljava/util/Date;)V"); + g_PKIXBuilderParametersSetRevocationEnabled = GetMethod(env, false, g_PKIXBuilderParametersClass, "setRevocationEnabled", "(Z)V"); + g_PKIXBuilderParametersSetTrustAnchors = GetMethod(env, false, g_PKIXBuilderParametersClass, "setTrustAnchors", "(Ljava/util/Set;)V"); + + g_PKIXCertPathBuilderResultClass = GetClassGRef(env, "java/security/cert/PKIXCertPathBuilderResult"); + g_PKIXCertPathBuilderResultGetCertPath = GetMethod(env, false, g_PKIXCertPathBuilderResultClass, "getCertPath", "()Ljava/security/cert/CertPath;"); + g_PKIXCertPathBuilderResultGetTrustAnchor = GetMethod(env, false, g_PKIXCertPathBuilderResultClass, "getTrustAnchor", "()Ljava/security/cert/TrustAnchor;"); + + g_PKIXReasonClass = GetOptionalClassGRef(env, "java/security/cert/PKIXReason"); + + if (g_CertPathValidatorGetRevocationChecker != NULL) + { + g_PKIXRevocationCheckerClass = GetClassGRef(env, "java/security/cert/PKIXRevocationChecker"); + g_PKIXRevocationCheckerSetOptions = GetMethod(env, false, g_PKIXRevocationCheckerClass, "setOptions", "(Ljava/util/Set;)V"); + + g_PKIXRevocationCheckerOptionClass = GetClassGRef(env, "java/security/cert/PKIXRevocationChecker$Option"); + g_PKIXRevocationCheckerOptionOnlyEndEntity = GetField(env, true, g_PKIXRevocationCheckerOptionClass, "ONLY_END_ENTITY", "Ljava/security/cert/PKIXRevocationChecker$Option;"); + } + + g_TrustAnchorClass = GetClassGRef(env, "java/security/cert/TrustAnchor"); + g_TrustAnchorCtor = GetMethod(env, false, g_TrustAnchorClass, "", "(Ljava/security/cert/X509Certificate;[B)V"); + g_TrustAnchorGetTrustedCert = GetMethod(env, false, g_TrustAnchorClass, "getTrustedCert", "()Ljava/security/cert/X509Certificate;"); + + g_X509CertClass = GetClassGRef(env, "java/security/cert/X509Certificate"); + g_X509CertEquals = GetMethod(env, false, g_X509CertClass, "equals", "(Ljava/lang/Object;)Z"); + g_X509CertGetEncoded = GetMethod(env, false, g_X509CertClass, "getEncoded", "()[B"); + g_X509CertGetPublicKey = GetMethod(env, false, g_X509CertClass, "getPublicKey", "()Ljava/security/PublicKey;"); + + g_X509CertSelectorClass = GetClassGRef(env, "java/security/cert/X509CertSelector"); + g_X509CertSelectorCtor = GetMethod(env, false, g_X509CertSelectorClass, "", "()V"); + g_X509CertSelectorSetCertificate = GetMethod(env, false, g_X509CertSelectorClass, "setCertificate", "(Ljava/security/cert/X509Certificate;)V"); + + g_DSAKeyClass = GetClassGRef(env, "java/security/interfaces/DSAKey"); + + g_ECKeyClass = GetClassGRef(env, "java/security/interfaces/ECKey"); g_RSAKeyClass = GetClassGRef(env, "java/security/interfaces/RSAKey"); g_RSAKeyGetModulus = GetMethod(env, false, g_RSAKeyClass, "getModulus", "()Ljava/math/BigInteger;"); @@ -547,6 +777,24 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_keyPairGenInitializeWithParamsMethod = GetMethod(env, false, g_keyPairGenClass, "initialize", "(Ljava/security/spec/AlgorithmParameterSpec;)V"); g_keyPairGenGenKeyPairMethod = GetMethod(env, false, g_keyPairGenClass, "genKeyPair", "()Ljava/security/KeyPair;"); + g_KeyStoreClass = GetClassGRef(env, "java/security/KeyStore"); + g_KeyStoreGetInstance = GetMethod(env, true, g_KeyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;"); + g_KeyStoreAliases = GetMethod(env, false, g_KeyStoreClass, "aliases", "()Ljava/util/Enumeration;"); + g_KeyStoreContainsAlias = GetMethod(env, false, g_KeyStoreClass, "containsAlias", "(Ljava/lang/String;)Z"); + g_KeyStoreDeleteEntry = GetMethod(env, false, g_KeyStoreClass, "deleteEntry", "(Ljava/lang/String;)V"); + g_KeyStoreGetCertificate = GetMethod(env, false, g_KeyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;"); + g_KeyStoreGetEntry = GetMethod(env, false, g_KeyStoreClass, "getEntry", "(Ljava/lang/String;Ljava/security/KeyStore$ProtectionParameter;)Ljava/security/KeyStore$Entry;"); + g_KeyStoreLoad = GetMethod(env, false, g_KeyStoreClass, "load", "(Ljava/io/InputStream;[C)V"); + g_KeyStoreSetCertificateEntry = GetMethod(env, false, g_KeyStoreClass, "setCertificateEntry", "(Ljava/lang/String;Ljava/security/cert/Certificate;)V"); + g_KeyStoreSetKeyEntry = GetMethod(env, false, g_KeyStoreClass, "setKeyEntry", "(Ljava/lang/String;Ljava/security/Key;[C[Ljava/security/cert/Certificate;)V"); + + g_PrivateKeyEntryClass = GetClassGRef(env, "java/security/KeyStore$PrivateKeyEntry"); + g_PrivateKeyEntryGetCertificate = GetMethod(env, false, g_PrivateKeyEntryClass, "getCertificate", "()Ljava/security/cert/Certificate;"); + g_PrivateKeyEntryGetPrivateKey = GetMethod(env, false, g_PrivateKeyEntryClass, "getPrivateKey", "()Ljava/security/PrivateKey;"); + + g_TrustedCertificateEntryClass = GetClassGRef(env, "java/security/KeyStore$TrustedCertificateEntry"); + g_TrustedCertificateEntryGetTrustedCertificate = GetMethod(env, false, g_TrustedCertificateEntryClass, "getTrustedCertificate", "()Ljava/security/cert/Certificate;"); + g_SignatureClass = GetClassGRef(env, "java/security/Signature"); g_SignatureGetInstance = GetMethod(env, true, g_SignatureClass, "getInstance", "(Ljava/lang/String;)Ljava/security/Signature;"); g_SignatureInitSign = GetMethod(env, false, g_SignatureClass, "initSign", "(Ljava/security/PrivateKey;)V"); @@ -580,7 +828,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_DSAPublicKeySpecClass = GetClassGRef(env, "java/security/spec/DSAPublicKeySpec"); g_DSAPublicKeySpecCtor = GetMethod(env, false, g_DSAPublicKeySpecClass, "", "(Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); g_DSAPublicKeySpecGetY = GetMethod(env, false, g_DSAPublicKeySpecClass, "getY", "()Ljava/math/BigInteger;"); - g_DSAPublicKeySpecGetP = GetMethod(env, false, g_DSAPublicKeySpecClass, "getP", "()Ljava/math/BigInteger;"); + g_DSAPublicKeySpecGetP = GetMethod(env, false, g_DSAPublicKeySpecClass, "getP", "()Ljava/math/BigInteger;"); g_DSAPublicKeySpecGetQ = GetMethod(env, false, g_DSAPublicKeySpecClass, "getQ", "()Ljava/math/BigInteger;"); g_DSAPublicKeySpecGetG = GetMethod(env, false, g_DSAPublicKeySpecClass, "getG", "()Ljava/math/BigInteger;"); @@ -643,23 +891,33 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_DestroyableClass = GetClassGRef(env, "javax/security/auth/Destroyable"); g_destroy = GetMethod(env, false, g_DestroyableClass, "destroy", "()V"); - g_ArrayListClass = GetClassGRef(env, "java/util/ArrayList"); - g_ArrayListCtor = GetMethod(env, false, g_ArrayListClass, "", "(I)V"); - g_ArrayListAdd = GetMethod(env, false, g_ArrayListClass, "add", "(Ljava/lang/Object;)Z"); + g_ArrayListClass = GetClassGRef(env, "java/util/ArrayList"); + g_ArrayListCtor = GetMethod(env, false, g_ArrayListClass, "", "()V"); + g_ArrayListCtorWithCapacity = GetMethod(env, false, g_ArrayListClass, "", "(I)V"); + g_ArrayListCtorWithCollection = GetMethod(env, false, g_ArrayListClass, "", "(Ljava/util/Collection;)V"); + g_ArrayListAdd = GetMethod(env, false, g_ArrayListClass, "add", "(Ljava/lang/Object;)Z"); g_CollectionClass = GetClassGRef(env, "java/util/Collection"); g_CollectionIterator = GetMethod(env, false, g_CollectionClass, "iterator", "()Ljava/util/Iterator;"); g_CollectionSize = GetMethod(env, false, g_CollectionClass, "size", "()I"); g_DateClass = GetClassGRef(env, "java/util/Date"); - g_DateGetTime = GetMethod(env, false, g_DateClass, "getTime", "()J"); + g_DateCtor = GetMethod(env, false, g_DateClass, "", "(J)V"); + + g_Enumeration = GetClassGRef(env, "java/util/Enumeration"); + g_EnumerationHasMoreElements = GetMethod(env, false, g_Enumeration, "hasMoreElements", "()Z"); + g_EnumerationNextElement = GetMethod(env, false, g_Enumeration, "nextElement", "()Ljava/lang/Object;"); + + g_HashSetClass = GetClassGRef(env, "java/util/HashSet"); + g_HashSetCtorWithCapacity = GetMethod(env, false, g_HashSetClass, "", "(I)V"); + g_HashSetAdd = GetMethod(env, false, g_HashSetClass, "add", "(Ljava/lang/Object;)Z"); g_IteratorClass = GetClassGRef(env, "java/util/Iterator"); g_IteratorHasNext = GetMethod(env, false, g_IteratorClass, "hasNext", "()Z"); g_IteratorNext = GetMethod(env, false, g_IteratorClass, "next", "()Ljava/lang/Object;"); - g_SetClass = GetClassGRef(env, "java/util/Set"); - g_SetIterator = GetMethod(env, false, g_SetClass, "iterator", "()Ljava/util/Iterator;"); + g_ListClass = GetClassGRef(env, "java/util/List"); + g_ListGet = GetMethod(env, false, g_ListClass, "get", "(I)Ljava/lang/Object;"); g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); g_SSLEngineSetUseClientModeMethod = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); @@ -699,10 +957,6 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_TrustManager = GetClassGRef(env, "javax/net/ssl/TrustManager"); - g_X500PrincipalClass = GetClassGRef(env, "javax/security/auth/x500/X500Principal"); - g_X500PrincipalGetEncoded = GetMethod(env, false, g_X500PrincipalClass, "getEncoded", "()[B"); - g_X500PrincipalHashCode = GetMethod(env, false, g_X500PrincipalClass, "hashCode", "()I"); - g_KeyAgreementClass = GetClassGRef(env, "javax/crypto/KeyAgreement"); g_KeyAgreementGetInstance = GetMethod(env, true, g_KeyAgreementClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/KeyAgreement;"); g_KeyAgreementInit = GetMethod(env, false, g_KeyAgreementClass, "init", "(Ljava/security/Key;)V"); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h index 252565fb0c934..1414e740ad3af 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h @@ -23,10 +23,10 @@ extern jmethodID g_ByteArrayInputStreamReset; extern jclass g_Enum; extern jmethodID g_EnumOrdinal; -// java/security/Key -extern jclass g_KeyClass; -extern jmethodID g_KeyGetAlgorithm; -extern jmethodID g_KeyGetEncoded; +// java/lang/Throwable +extern jclass g_ThrowableClass; +extern jmethodID g_ThrowableGetCause; +extern jmethodID g_ThrowableGetMessage; // java/security/SecureRandom extern jclass g_randClass; @@ -101,17 +101,86 @@ extern jmethodID g_CertFactoryGenerateCertificate; extern jmethodID g_CertFactoryGenerateCertificates; extern jmethodID g_CertFactoryGenerateCertPathFromList; extern jmethodID g_CertFactoryGenerateCertPathFromStream; -extern jmethodID g_CertFactoryGenerateCRL; // java/security/cert/CertPath extern jclass g_CertPathClass; extern jmethodID g_CertPathGetEncoded; +extern jmethodID g_CertPathGetCertificates; + +// java/security/cert/CertPathBuilder +extern jclass g_CertPathBuilderClass; +extern jmethodID g_CertPathBuilderGetInstance; +extern jmethodID g_CertPathBuilderBuild; + +// java/security/cert/CertPathValidator +extern jclass g_CertPathValidatorClass; +extern jmethodID g_CertPathValidatorGetInstance; +extern jmethodID g_CertPathValidatorValidate; +extern jmethodID g_CertPathValidatorGetRevocationChecker; // only in API level 24+ + +// java/security/cert/CertPathValidatorException +extern jclass g_CertPathValidatorExceptionClass; +extern jmethodID g_CertPathValidatorExceptionGetIndex; +extern jmethodID g_CertPathValidatorExceptionGetReason; + +// java/security/cert/CertPathValidatorException$BasicReason - only in API level 24+ +extern jclass g_CertPathExceptionBasicReasonClass; + +// java/security/cert/CertStore +extern jclass g_CertStoreClass; +extern jmethodID g_CertStoreGetInstance; + +// java/security/cert/CollectionCertStoreParameters +extern jclass g_CollectionCertStoreParametersClass; +extern jmethodID g_CollectionCertStoreParametersCtor; + +// java/security/cert/PKIXBuilderParameters +extern jclass g_PKIXBuilderParametersClass; +extern jmethodID g_PKIXBuilderParametersCtor; +extern jmethodID g_PKIXBuilderParametersAddCertStore; +extern jmethodID g_PKIXBuilderParametersAddCertPathChecker; +extern jmethodID g_PKIXBuilderParametersSetDate; +extern jmethodID g_PKIXBuilderParametersSetRevocationEnabled; +extern jmethodID g_PKIXBuilderParametersSetTrustAnchors; + +// java/security/cert/PKIXCertPathBuilderResult +extern jclass g_PKIXCertPathBuilderResultClass; +extern jmethodID g_PKIXCertPathBuilderResultGetCertPath; +extern jmethodID g_PKIXCertPathBuilderResultGetTrustAnchor; + +// java/security/cert/PKIXReason - only in API level 24+ +extern jclass g_PKIXReasonClass; + +// java/security/cert/PKIXRevocationChecker - only in API level 24+ +extern jclass g_PKIXRevocationCheckerClass; +extern jmethodID g_PKIXRevocationCheckerSetOptions; + +// java/security/cert/PKIXRevocationChecker$Option - only in API level 24+ +extern jclass g_PKIXRevocationCheckerOptionClass; +extern jfieldID g_PKIXRevocationCheckerOptionOnlyEndEntity; + +// java/security/cert/TrustAnchor +extern jclass g_TrustAnchorClass; +extern jclass g_TrustAnchorCtor; +extern jmethodID g_TrustAnchorGetTrustedCert; // java/security/cert/X509Certificate extern jclass g_X509CertClass; +extern jmethodID g_X509CertEquals; extern jmethodID g_X509CertGetEncoded; extern jmethodID g_X509CertGetPublicKey; +// java/security/cert/X509CertSelector +extern jclass g_X509CertSelectorClass; +extern jmethodID g_X509CertSelectorCtor; +extern jmethodID g_X509CertSelectorSetCertificate; + +// java/security/interfaces/DSAKey +extern jclass g_DSAKeyClass; + +// java/security/interfaces/ECKey +extern jclass g_ECKeyClass; + // java/security/interfaces/RSAKey extern jclass g_RSAKeyClass; extern jmethodID g_RSAKeyGetModulus; @@ -133,6 +202,27 @@ extern jmethodID g_keyPairGenInitializeMethod; extern jmethodID g_keyPairGenInitializeWithParamsMethod; extern jmethodID g_keyPairGenGenKeyPairMethod; +// java/security/KeyStore +extern jclass g_KeyStoreClass; +extern jmethodID g_KeyStoreGetInstance; +extern jmethodID g_KeyStoreAliases; +extern jmethodID g_KeyStoreContainsAlias; +extern jmethodID g_KeyStoreDeleteEntry; +extern jmethodID g_KeyStoreGetCertificate; +extern jmethodID g_KeyStoreGetEntry; +extern jmethodID g_KeyStoreLoad; +extern jmethodID g_KeyStoreSetCertificateEntry; +extern jmethodID g_KeyStoreSetKeyEntry; + +// java/security/KeyStore$PrivateKeyEntry +extern jclass g_PrivateKeyEntryClass; +extern jmethodID g_PrivateKeyEntryGetCertificate; +extern jmethodID g_PrivateKeyEntryGetPrivateKey; + +// java/security/KeyStore$TrustedCertificateEntry +extern jclass g_TrustedCertificateEntryClass; +extern jmethodID g_TrustedCertificateEntryGetTrustedCertificate; + // java/security/Signature extern jclass g_SignatureClass; extern jmethodID g_SignatureGetInstance; @@ -249,9 +339,11 @@ extern jmethodID g_X509EncodedKeySpecCtor; extern jclass g_DestroyableClass; extern jmethodID g_destroy; -// java/util/Collection +// java/util/ArrayList extern jclass g_ArrayListClass; extern jmethodID g_ArrayListCtor; +extern jmethodID g_ArrayListCtorWithCapacity; +extern jmethodID g_ArrayListCtorWithCollection; extern jmethodID g_ArrayListAdd; // java/util/Collection @@ -261,16 +353,26 @@ extern jmethodID g_CollectionSize; // java/util/Date extern jclass g_DateClass; -extern jmethodID g_DateGetTime; +extern jmethodID g_DateCtor; + +// java/util/Enumeration +extern jclass g_Enumeration; +extern jmethodID g_EnumerationHasMoreElements; +extern jmethodID g_EnumerationNextElement; + +// java/util/HashSet +extern jclass g_HashSetClass; +extern jmethodID g_HashSetCtorWithCapacity; +extern jmethodID g_HashSetAdd; // java/util/Iterator extern jclass g_IteratorClass; extern jmethodID g_IteratorHasNext; extern jmethodID g_IteratorNext; -// java/util/Set -extern jclass g_SetClass; -extern jmethodID g_SetIterator; +// java/util/List +extern jclass g_ListClass; +extern jmethodID g_ListGet; // javax/net/ssl/SSLEngine extern jclass g_SSLEngine; @@ -316,11 +418,6 @@ extern jmethodID g_SSLEngineResultGetHandshakeStatusMethod; // javax/net/ssl/TrustManager extern jclass g_TrustManager; -// javax/security/auth/x500/X500Principal -extern jclass g_X500PrincipalClass; -extern jmethodID g_X500PrincipalGetEncoded; -extern jmethodID g_X500PrincipalHashCode; - // javax/crypto/KeyAgreement extern jclass g_KeyAgreementClass; extern jmethodID g_KeyAgreementGetInstance; @@ -335,14 +432,47 @@ extern jmethodID g_KeyAgreementGenerateSecret; #define JSTRING(str) ((jstring)(*env)->NewStringUTF(env, str)) #define ON_EXCEPTION_PRINT_AND_GOTO(label) if (CheckJNIExceptions(env)) goto label +#define INIT_LOCALS(name, ...) \ + enum { __VA_ARGS__, count_##name }; \ + jobject name[count_##name] = { 0 } \ + +#define RELEASE_LOCALS(name, env) \ +do { \ + for (int i_##name = 0; i_##name < count_##name; ++i_##name) \ + { \ + jobject local = name[i_##name]; \ + if (local != NULL) \ + (*env)->DeleteLocalRef(env, local); \ + } \ +} while(0) + +#define RELEASE_LOCALS_ENV(name, releaseFn) \ +do { \ + for (int i = 0; i < count_##name; ++i) \ + { \ + releaseFn(env, name[i]); \ + } \ +} while(0) + void SaveTo(uint8_t* src, uint8_t** dst, size_t len, bool overwrite); jobject ToGRef(JNIEnv *env, jobject lref); jobject AddGRef(JNIEnv *env, jobject gref); void ReleaseGRef(JNIEnv *env, jobject gref); void ReleaseLRef(JNIEnv *env, jobject lref); jclass GetClassGRef(JNIEnv *env, const char* name); + +// Print and clear any JNI exceptions. Returns true if there was an exception, false otherwise. bool CheckJNIExceptions(JNIEnv* env); + +// Clear any JNI exceptions without printing them. Returns true if there was an exception, false otherwise. +bool TryClearJNIExceptions(JNIEnv* env); + +// Get any pending JNI exception. Returns true if there was an exception, false otherwise. +bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException); + +// Assert on any JNI exceptions. Prints the exception before asserting. void AssertOnJNIExceptions(JNIEnv* env); + jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); jmethodID GetOptionalMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c index 68091b788a073..5468632964730 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.c @@ -47,39 +47,40 @@ PALEXPORT void AndroidCryptoNative_RsaDestroy(RSA* rsa) PALEXPORT int32_t AndroidCryptoNative_RsaPublicEncrypt(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding) { - if (!rsa) - return RSA_FAIL; - + assert(rsa != NULL); JNIEnv* env = GetJNIEnv(); - jobject algName; + int32_t ret = RSA_FAIL; + INIT_LOCALS(loc, algName, cipher, fromBytes, encryptedBytes); + if (padding == Pkcs1) - algName = JSTRING("RSA/ECB/PKCS1Padding"); + { + loc[algName] = JSTRING("RSA/ECB/PKCS1Padding"); + } else if (padding == OaepSHA1) - algName = JSTRING("RSA/ECB/OAEPPadding"); + { + loc[algName] = JSTRING("RSA/ECB/OAEPPadding"); + } else - algName = JSTRING("RSA/ECB/NoPadding"); - - jobject cipher = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName); - (*env)->CallVoidMethod(env, cipher, g_cipherInit2Method, CIPHER_ENCRYPT_MODE, rsa->publicKey); - jbyteArray fromBytes = (*env)->NewByteArray(env, flen); - (*env)->SetByteArrayRegion(env, fromBytes, 0, flen, (jbyte*)from); - jbyteArray encryptedBytes = (jbyteArray)(*env)->CallObjectMethod(env, cipher, g_cipherDoFinal2Method, fromBytes); - if (CheckJNIExceptions(env)) { - (*env)->DeleteLocalRef(env, fromBytes); - (*env)->DeleteLocalRef(env, cipher); - return RSA_FAIL; + loc[algName] = JSTRING("RSA/ECB/NoPadding"); } - jsize encryptedBytesLen = (*env)->GetArrayLength(env, encryptedBytes); - (*env)->GetByteArrayRegion(env, encryptedBytes, 0, encryptedBytesLen, (jbyte*) to); - (*env)->DeleteLocalRef(env, cipher); - (*env)->DeleteLocalRef(env, fromBytes); - (*env)->DeleteLocalRef(env, encryptedBytes); - (*env)->DeleteLocalRef(env, algName); + loc[cipher] = (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, loc[algName]); + (*env)->CallVoidMethod(env, loc[cipher], g_cipherInit2Method, CIPHER_ENCRYPT_MODE, rsa->publicKey); + loc[fromBytes] = (*env)->NewByteArray(env, flen); + (*env)->SetByteArrayRegion(env, loc[fromBytes], 0, flen, (jbyte*)from); + loc[encryptedBytes] = (jbyteArray)(*env)->CallObjectMethod(env, loc[cipher], g_cipherDoFinal2Method, loc[fromBytes]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - return (int32_t)encryptedBytesLen; + jsize encryptedBytesLen = (*env)->GetArrayLength(env, loc[encryptedBytes]); + (*env)->GetByteArrayRegion(env, loc[encryptedBytes], 0, encryptedBytesLen, (jbyte*) to); + + ret = (int32_t)encryptedBytesLen; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; } PALEXPORT int32_t AndroidCryptoNative_RsaPrivateDecrypt(int32_t flen, uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding) @@ -162,7 +163,7 @@ PALEXPORT RSA* AndroidCryptoNative_DecodeRsaSubjectPublicKeyInfo(uint8_t* buf, i return FAIL; } - RSA* rsa = AndroidCryptoNative_NewRsaFromPublicKey(env, publicKey); + RSA* rsa = AndroidCryptoNative_NewRsaFromKeys(env, publicKey, NULL /*privateKey*/); (*env)->DeleteLocalRef(env, publicKey); return rsa; @@ -337,63 +338,54 @@ PALEXPORT int32_t AndroidCryptoNative_SetRsaParameters(RSA* rsa, return FAIL; JNIEnv* env = GetJNIEnv(); + INIT_LOCALS(bn, N, E, D, P, Q, DMP1, DMQ1, IQMP); + INIT_LOCALS(loc, algName, keyFactory, rsaPubKeySpec, rsaPrivateKeySpec); - jobject nObj = AndroidCryptoNative_BigNumFromBinary(n, nLength); - jobject eObj = AndroidCryptoNative_BigNumFromBinary(e, eLength); + bn[N] = AndroidCryptoNative_BigNumFromBinary(n, nLength); + bn[E] = AndroidCryptoNative_BigNumFromBinary(e, eLength); rsa->keyWidthInBits = nLength * 8; - jobject algName = JSTRING("RSA"); - jobject keyFactory = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, algName); + loc[algName] = JSTRING("RSA"); + loc[keyFactory] = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, loc[algName]); if (dLength > 0) { // private key section - jobject dObj = AndroidCryptoNative_BigNumFromBinary(d, dLength); - jobject pObj = AndroidCryptoNative_BigNumFromBinary(p, pLength); - jobject qObj = AndroidCryptoNative_BigNumFromBinary(q, qLength); - jobject dmp1Obj = AndroidCryptoNative_BigNumFromBinary(dmp1, dmp1Length); - jobject dmq1Obj = AndroidCryptoNative_BigNumFromBinary(dmq1, dmq1Length); - jobject iqmpObj = AndroidCryptoNative_BigNumFromBinary(iqmp, iqmpLength); + bn[D] = AndroidCryptoNative_BigNumFromBinary(d, dLength); + bn[P] = AndroidCryptoNative_BigNumFromBinary(p, pLength); + bn[Q] = AndroidCryptoNative_BigNumFromBinary(q, qLength); + bn[DMP1] = AndroidCryptoNative_BigNumFromBinary(dmp1, dmp1Length); + bn[DMQ1] = AndroidCryptoNative_BigNumFromBinary(dmq1, dmq1Length); + bn[IQMP] = AndroidCryptoNative_BigNumFromBinary(iqmp, iqmpLength); - jobject rsaPrivateKeySpec = (*env)->NewObject(env, g_RSAPrivateCrtKeySpecClass, g_RSAPrivateCrtKeySpecCtor, - nObj, eObj, dObj, pObj, qObj, dmp1Obj, dmq1Obj, iqmpObj); + loc[rsaPrivateKeySpec] = (*env)->NewObject(env, g_RSAPrivateCrtKeySpecClass, g_RSAPrivateCrtKeySpecCtor, + bn[N], bn[E], bn[D], bn[P], bn[Q], bn[DMP1], bn[DMQ1], bn[IQMP]); ReleaseGRef(env, rsa->privateKey); - rsa->privateKey = ToGRef(env, (*env)->CallObjectMethod(env, keyFactory, g_KeyFactoryGenPrivateMethod, rsaPrivateKeySpec)); - - (*env)->DeleteGlobalRef(env, dObj); - (*env)->DeleteGlobalRef(env, pObj); - (*env)->DeleteGlobalRef(env, qObj); - (*env)->DeleteGlobalRef(env, dmp1Obj); - (*env)->DeleteGlobalRef(env, dmq1Obj); - (*env)->DeleteGlobalRef(env, iqmpObj); - (*env)->DeleteLocalRef(env, rsaPrivateKeySpec); + rsa->privateKey = ToGRef(env, (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPrivateMethod, loc[rsaPrivateKeySpec])); } - jobject rsaPubKeySpec = (*env)->NewObject(env, g_RSAPublicCrtKeySpecClass, g_RSAPublicCrtKeySpecCtor, nObj, eObj); + loc[rsaPubKeySpec] = (*env)->NewObject(env, g_RSAPublicCrtKeySpecClass, g_RSAPublicCrtKeySpecCtor, bn[N], bn[E]); ReleaseGRef(env, rsa->publicKey); - rsa->publicKey = ToGRef(env, (*env)->CallObjectMethod(env, keyFactory, g_KeyFactoryGenPublicMethod, rsaPubKeySpec)); - - (*env)->DeleteLocalRef(env, algName); - (*env)->DeleteLocalRef(env, keyFactory); - (*env)->DeleteGlobalRef(env, nObj); - (*env)->DeleteGlobalRef(env, eObj); - (*env)->DeleteLocalRef(env, rsaPubKeySpec); + rsa->publicKey = ToGRef(env, (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPublicMethod, loc[rsaPubKeySpec])); + RELEASE_LOCALS(bn, env); + RELEASE_LOCALS(loc, env); return CheckJNIExceptions(env) ? FAIL : SUCCESS; } -RSA* AndroidCryptoNative_NewRsaFromPublicKey(JNIEnv* env, jobject /*RSAPublicKey*/ key) +RSA* AndroidCryptoNative_NewRsaFromKeys(JNIEnv* env, jobject /*RSAPublicKey*/ publicKey, jobject /*RSAPrivateKey*/ privateKey) { - if (!(*env)->IsInstanceOf(env, key, g_RSAPublicKeyClass)) + if (!(*env)->IsInstanceOf(env, publicKey, g_RSAPublicKeyClass)) return NULL; - jobject modulus = (*env)->CallObjectMethod(env, key, g_RSAKeyGetModulus); + jobject modulus = (*env)->CallObjectMethod(env, publicKey, g_RSAKeyGetModulus); RSA* ret = AndroidCryptoNative_RsaCreate(); - ret->publicKey = AddGRef(env, key); + ret->publicKey = AddGRef(env, publicKey); + ret->privateKey = AddGRef(env, privateKey); ret->keyWidthInBits = AndroidCryptoNative_GetBigNumBytes(modulus) * 8; (*env)->DeleteLocalRef(env, modulus); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h index 2bba552d3f69f..5cc99ecc71f66 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_rsa.h @@ -41,4 +41,5 @@ PALEXPORT int32_t AndroidCryptoNative_SetRsaParameters(RSA* rsa, uint8_t* p, int32_t pLength, uint8_t* dmp1, int32_t dmp1Length, uint8_t* q, int32_t qLength, uint8_t* dmq1, int32_t dmq1Length, uint8_t* iqmp, int32_t iqmpLength); +RSA* AndroidCryptoNative_NewRsaFromKeys(JNIEnv* env, jobject /*RSAPublicKey*/ publicKey, jobject /*RSAPrivateKey*/ privateKey); RSA* AndroidCryptoNative_NewRsaFromPublicKey(JNIEnv* env, jobject /*RSAPublicKey*/ key); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c index f3124540dd780..dfa72406a6c5e 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c @@ -4,31 +4,20 @@ #include "pal_x509.h" #include "pal_eckey.h" -#include "pal_rsa.h" #include "pal_misc.h" +#include "pal_rsa.h" #include +#include #include #include -#define INIT_LOCALS(name, ...) \ - enum { __VA_ARGS__, count_##name }; \ - jobject name[count_##name] = { 0 }; \ - -#define RELEASE_LOCALS(name, env) \ -{ \ - for (int i_##name = 0; i_##name < count_##name; ++i_##name) \ - { \ - jobject local = name[i_##name]; \ - if (local != NULL) \ - (*env)->DeleteLocalRef(env, local); \ - } \ -} \ - #define INSUFFICIENT_BUFFER -1 static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len); +static void FindCertStart(const uint8_t** buffer, int32_t* len); + // Handles both DER and PEM formats jobject /*X509Certificate*/ AndroidCryptoNative_X509Decode(const uint8_t* buf, int32_t len) { @@ -36,7 +25,9 @@ jobject /*X509Certificate*/ AndroidCryptoNative_X509Decode(const uint8_t* buf, i JNIEnv* env = GetJNIEnv(); jobject ret = NULL; - INIT_LOCALS(loc, bytes, stream, certType, certFactory) + INIT_LOCALS(loc, bytes, stream, certType, certFactory); + + FindCertStart(&buf, &len); // byte[] bytes = new byte[] { ... } // InputStream stream = new ByteArrayInputStream(bytes); @@ -58,7 +49,7 @@ jobject /*X509Certificate*/ AndroidCryptoNative_X509Decode(const uint8_t* buf, i ret = ToGRef(env, ret); cleanup: - RELEASE_LOCALS(loc, env) + RELEASE_LOCALS(loc, env); return ret; } @@ -89,7 +80,7 @@ int32_t AndroidCryptoNative_X509DecodeCollection(const uint8_t* buf, JNIEnv* env = GetJNIEnv(); int32_t ret = FAIL; - INIT_LOCALS(loc, bytes, stream, certType, certFactory, certs, iter) + INIT_LOCALS(loc, bytes, stream, certType, certFactory, certs, iter); // byte[] bytes = new byte[] { ... } // InputStream stream = new ByteArrayInputStream(bytes); @@ -151,7 +142,7 @@ int32_t AndroidCryptoNative_X509DecodeCollection(const uint8_t* buf, ret = SUCCESS; cleanup: - RELEASE_LOCALS(loc, env) + RELEASE_LOCALS(loc, env); return ret; } @@ -165,12 +156,12 @@ int32_t AndroidCryptoNative_X509ExportPkcs7(jobject* /*X509Certificate[]*/ certs JNIEnv* env = GetJNIEnv(); int32_t ret = FAIL; - INIT_LOCALS(loc, certList, certType, certFactory, certPath, pkcs7Type, encoded) + INIT_LOCALS(loc, certList, certType, certFactory, certPath, pkcs7Type, encoded); // ArrayList certList = new ArrayList(); // foreach (Certificate cert in certs) // certList.add(cert); - loc[certList] = (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtor, certsLen); + loc[certList] = (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtorWithCapacity, certsLen); for (int i = 0; i < certsLen; ++i) { (*env)->CallBooleanMethod(env, loc[certList], g_ArrayListAdd, certs[i]); @@ -184,7 +175,8 @@ int32_t AndroidCryptoNative_X509ExportPkcs7(jobject* /*X509Certificate[]*/ certs // CertPath certPath = certFactory.generateCertPath(certList); // byte[] encoded = certPath.getEncoded("PKCS7"); - loc[certPath] = (*env)->CallObjectMethod(env, loc[certFactory], g_CertFactoryGenerateCertPathFromList, loc[certList]); + loc[certPath] = + (*env)->CallObjectMethod(env, loc[certFactory], g_CertFactoryGenerateCertPathFromList, loc[certList]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); loc[pkcs7Type] = JSTRING("PKCS7"); loc[encoded] = (*env)->CallObjectMethod(env, loc[certPath], g_CertPathGetEncoded, loc[pkcs7Type]); @@ -193,7 +185,7 @@ int32_t AndroidCryptoNative_X509ExportPkcs7(jobject* /*X509Certificate[]*/ certs ret = PopulateByteArray(env, loc[encoded], out, outLen); cleanup: - RELEASE_LOCALS(loc, env) + RELEASE_LOCALS(loc, env); return ret; } @@ -203,14 +195,16 @@ PAL_X509ContentType AndroidCryptoNative_X509GetContentType(const uint8_t* buf, i JNIEnv* env = GetJNIEnv(); PAL_X509ContentType ret = PAL_X509Unknown; - INIT_LOCALS(loc, bytes, stream, certType, certFactory, pkcs7Type, certPath, cert) + INIT_LOCALS(loc, bytes, stream, certType, certFactory, pkcs7Type, certPath, cert); - // This functin checks: + // This function checks: // - PKCS7 DER/PEM // - X509 DER/PEM // The generateCertificate method used for the X509 DER/PEM check will succeed for some // PKCS7 blobs, so it is done after the PKCS7 check. + FindCertStart(&buf, &len); + // byte[] bytes = new byte[] { ... } // InputStream stream = new ByteArrayInputStream(bytes); loc[bytes] = (*env)->NewByteArray(env, len); @@ -225,8 +219,9 @@ PAL_X509ContentType AndroidCryptoNative_X509GetContentType(const uint8_t* buf, i // CertPath certPath = certFactory.generateCertPath(stream, "PKCS7"); loc[pkcs7Type] = JSTRING("PKCS7"); - loc[certPath] = (*env)->CallObjectMethod(env, loc[certFactory], g_CertFactoryGenerateCertPathFromStream, loc[stream], loc[pkcs7Type]); - if (!CheckJNIExceptions(env)) + loc[certPath] = (*env)->CallObjectMethod( + env, loc[certFactory], g_CertFactoryGenerateCertPathFromStream, loc[stream], loc[pkcs7Type]); + if (!TryClearJNIExceptions(env)) { ret = PAL_Pkcs7; goto cleanup; @@ -236,14 +231,14 @@ PAL_X509ContentType AndroidCryptoNative_X509GetContentType(const uint8_t* buf, i // Certificate cert = certFactory.generateCertificate(stream); (*env)->CallVoidMethod(env, loc[stream], g_ByteArrayInputStreamReset); loc[cert] = (*env)->CallObjectMethod(env, loc[certFactory], g_CertFactoryGenerateCertificate, loc[stream]); - if (!CheckJNIExceptions(env)) + if (!TryClearJNIExceptions(env)) { ret = PAL_Certificate; goto cleanup; } cleanup: - RELEASE_LOCALS(loc, env) + RELEASE_LOCALS(loc, env); return ret; } @@ -255,16 +250,20 @@ void* AndroidCryptoNative_X509PublicKey(jobject /*X509Certificate*/ cert, PAL_Ke void* keyHandle; jobject key = (*env)->CallObjectMethod(env, cert, g_X509CertGetPublicKey); + if (CheckJNIExceptions(env) || !key) + { + return NULL; + } switch (algorithm) { case PAL_EC: - keyHandle = AndroidCryptoNative_NewEcKeyFromPublicKey(env, key); + keyHandle = AndroidCryptoNative_NewEcKeyFromKeys(env, key, NULL /*privateKey*/); break; case PAL_DSA: - keyHandle = AndroidCryptoNative_CreateKeyPair(env, key, NULL); + keyHandle = AndroidCryptoNative_CreateKeyPair(env, key, NULL /*privateKey*/); break; case PAL_RSA: - keyHandle = AndroidCryptoNative_NewRsaFromPublicKey(env, key); + keyHandle = AndroidCryptoNative_NewRsaFromKeys(env, key, NULL /*privateKey*/); break; default: keyHandle = NULL; @@ -287,3 +286,56 @@ static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); return CheckJNIExceptions(env) ? FAIL : SUCCESS; } + +static void FindCertStart(const uint8_t** buffer, int32_t* len) +{ + assert(buffer != NULL && *buffer != NULL); + assert(*len >= 0); + + if (iscntrl(**buffer) && !isspace(**buffer)) + { + // If the character is a control character that isn't whitespace, then we're probably using a DER encoding + // and not using a PEM encoding in ASCII. + return; + } + + const uint8_t* bufferLocal = *buffer; + int32_t lengthLocal = *len; + + while (lengthLocal > 0) + { + const char pemHeader[] = "-----BEGIN "; + int32_t pemHeaderLength = (int32_t)(sizeof(pemHeader) - 1); // Exclude the null-terminator + // Skip until we see the - that could start a PEM block. + while (lengthLocal >= pemHeaderLength && (!iscntrl(*bufferLocal) || isspace(*bufferLocal)) && + *bufferLocal != pemHeader[0]) + { + bufferLocal += 1; + lengthLocal -= 1; + } + + if (lengthLocal < pemHeaderLength || (iscntrl(*bufferLocal) && !isspace(*bufferLocal))) + { + // Either the buffer doesn't have enough space to contain a PEM header + // or we encountered a control character that isn't whitespace. + // In the insufficient size case, we didn't find the PEM header, so we can't skip to it. + // In the control character case, we know that this isn't explanatory info since that needs to + // all be printable or whitespace characters, not non-whitespace control characters. + return; + } + + if (memcmp(bufferLocal, pemHeader, (size_t)pemHeaderLength) == 0) + { + // We found the PEM header. + *buffer = bufferLocal; + *len = lengthLocal; + return; + } + else + { + // This PEM header is invalid. Skip it. + bufferLocal += 1; + lengthLocal -= 1; + } + } +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.h index 476d0dedeaf35..05a5839ce876b 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.h @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_jni.h" +#include // Creation and lifetime PALEXPORT jobject /*X509Certificate*/ AndroidCryptoNative_X509Decode(const uint8_t* buf, int32_t len); @@ -36,18 +37,6 @@ PALEXPORT int32_t AndroidCryptoNative_X509ExportPkcs7(jobject* /*X509Certificate uint8_t* out, int32_t* outLen); -// Matches managed X509ContentType enum -enum -{ - PAL_X509Unknown = 0, - PAL_Certificate = 1, - PAL_SerializedCert = 2, - PAL_Pkcs12 = 3, - PAL_SerializedStore = 4, - PAL_Pkcs7 = 5, - PAL_Authenticode = 6, -}; -typedef uint32_t PAL_X509ContentType; PALEXPORT PAL_X509ContentType AndroidCryptoNative_X509GetContentType(const uint8_t* buf, int32_t len); // Matches managed PAL_KeyAlgorithm enum @@ -56,8 +45,10 @@ enum PAL_DSA = 0, PAL_EC = 1, PAL_RSA = 2, + + PAL_UnknownAlgorithm = -1, }; -typedef uint32_t PAL_KeyAlgorithm; +typedef int32_t PAL_KeyAlgorithm; /* Gets an opaque handle for a certificate's public key diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509chain.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509chain.c new file mode 100644 index 0000000000000..adc736b0af11f --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509chain.c @@ -0,0 +1,601 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_x509chain.h" +#include + +#include +#include +#include + +struct X509ChainContext_t +{ + jobject /*PKIXBuilderParameters*/ params; + jobject /*CertPath*/ certPath; + jobject /*TrustAnchor*/ trustAnchor; + + jobject /*ArrayList*/ errorList; + jobject /*ArrayList*/ revocationErrorList; +}; + +struct ValidationError_t +{ + uint16_t* message; + int index; + PAL_X509ChainStatusFlags chainStatus; +}; + +X509ChainContext* AndroidCryptoNative_X509ChainCreateContext(jobject /*X509Certificate*/ cert, + jobject* /*X509Certificate[]*/ extraStore, + int32_t extraStoreLen) +{ + assert(cert != NULL); + assert(extraStore != NULL || extraStoreLen == 0); + JNIEnv* env = GetJNIEnv(); + + X509ChainContext* ret = NULL; + INIT_LOCALS(loc, keyStoreType, keyStore, targetSel, params, certList, certStoreType, certStoreParams, certStore); + + // String keyStoreType = "AndroidCAStore"; + // KeyStore keyStore = KeyStore.getInstance(keyStoreType); + // keyStore.load(null, null); + loc[keyStoreType] = JSTRING("AndroidCAStore"); + loc[keyStore] = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, loc[keyStoreType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[keyStore], g_KeyStoreLoad, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // X509CertSelector targetSel = new X509CertSelector(); + // targetSel.setCertificate(cert); + loc[targetSel] = (*env)->NewObject(env, g_X509CertSelectorClass, g_X509CertSelectorCtor); + (*env)->CallVoidMethod(env, loc[targetSel], g_X509CertSelectorSetCertificate, cert); + + // PKIXBuilderParameters params = new PKIXBuilderParameters(keyStore, targetSelector); + loc[params] = (*env)->NewObject( + env, g_PKIXBuilderParametersClass, g_PKIXBuilderParametersCtor, loc[keyStore], loc[targetSel]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // ArrayList certList = new ArrayList(); + // certList.add(cert); + // for (int i = 0; i < extraStoreLen; i++) { + // certList.add(extraStore[i]); + // } + loc[certList] = (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtorWithCapacity, extraStoreLen); + (*env)->CallBooleanMethod(env, loc[certList], g_ArrayListAdd, cert); + for (int i = 0; i < extraStoreLen; ++i) + { + (*env)->CallBooleanMethod(env, loc[certList], g_ArrayListAdd, extraStore[i]); + } + + // String certStoreType = "Collection"; + // CollectionCertStoreParameters certStoreParams = new CollectionCertStoreParameters(certList); + // CertStore certStore = CertStore.getInstance(certStoreType, certStoreParams); + loc[certStoreType] = JSTRING("Collection"); + loc[certStoreParams] = (*env)->NewObject( + env, g_CollectionCertStoreParametersClass, g_CollectionCertStoreParametersCtor, loc[certList]); + loc[certStore] = (*env)->CallStaticObjectMethod( + env, g_CertStoreClass, g_CertStoreGetInstance, loc[certStoreType], loc[certStoreParams]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // params.addCertStore(certStore); + (*env)->CallVoidMethod(env, loc[params], g_PKIXBuilderParametersAddCertStore, loc[certStore]); + + ret = malloc(sizeof(X509ChainContext)); + memset(ret, 0, sizeof(X509ChainContext)); + ret->params = AddGRef(env, loc[params]); + ret->errorList = ToGRef(env, (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtor)); + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +void AndroidCryptoNative_X509ChainDestroyContext(X509ChainContext* ctx) +{ + if (ctx == NULL) + return; + + JNIEnv* env = GetJNIEnv(); + ReleaseGRef(env, ctx->params); + ReleaseGRef(env, ctx->certPath); + ReleaseGRef(env, ctx->trustAnchor); + ReleaseGRef(env, ctx->errorList); + ReleaseGRef(env, ctx->revocationErrorList); + free(ctx); +} + +int32_t AndroidCryptoNative_X509ChainBuild(X509ChainContext* ctx, int64_t timeInMsFromUnixEpoch) +{ + assert(ctx != NULL); + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + INIT_LOCALS(loc, date, builderType, builder, result, ex, certPath, trustAnchor); + + jobject params = ctx->params; + + // Date date = new Date(timeInMsFromUnixEpoch); + // params.setDate(date); + loc[date] = (*env)->NewObject(env, g_DateClass, g_DateCtor, timeInMsFromUnixEpoch); + (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersSetDate, loc[date]); + + // Disable revocation checking when building the cert path. It will be handled in a validation pass if the path is + // successfully built. + // params.setRevocationEnabled(false); + (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersSetRevocationEnabled, false); + + // String builderType = "PKIX"; + // CertPathBuilder builder = CertPathBuilder.getInstance(builderType); + // PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)builder.build(params); + loc[builderType] = JSTRING("PKIX"); + loc[builder] = + (*env)->CallStaticObjectMethod(env, g_CertPathBuilderClass, g_CertPathBuilderGetInstance, loc[builderType]); + loc[result] = (*env)->CallObjectMethod(env, loc[builder], g_CertPathBuilderBuild, params); + if (TryGetJNIException(env, &loc[ex], false /*printException*/)) + { + (*env)->CallBooleanMethod(env, ctx->errorList, g_ArrayListAdd, loc[ex]); + goto cleanup; + } + + loc[certPath] = (*env)->CallObjectMethod(env, loc[result], g_PKIXCertPathBuilderResultGetCertPath); + loc[trustAnchor] = (*env)->CallObjectMethod(env, loc[result], g_PKIXCertPathBuilderResultGetTrustAnchor); + + ctx->certPath = AddGRef(env, loc[certPath]); + ctx->trustAnchor = AddGRef(env, loc[trustAnchor]); + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +int32_t AndroidCryptoNative_X509ChainGetCertificateCount(X509ChainContext* ctx) +{ + assert(ctx != NULL); + JNIEnv* env = GetJNIEnv(); + + // List certPathList = certPath.getCertificates(); + jobject certPathList = (*env)->CallObjectMethod(env, ctx->certPath, g_CertPathGetCertificates); + int certCount = (int)(*env)->CallIntMethod(env, certPathList, g_CollectionSize); + + (*env)->DeleteLocalRef(env, certPathList); + return certCount + 1; // +1 for the trust anchor +} + +int32_t AndroidCryptoNative_X509ChainGetCertificates(X509ChainContext* ctx, + jobject* /*X509Certificate[]*/ certs, + int32_t certsLen) +{ + assert(ctx != NULL); + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + + // List certPathList = certPath.getCertificates(); + jobject certPathList = (*env)->CallObjectMethod(env, ctx->certPath, g_CertPathGetCertificates); + int certCount = (int)(*env)->CallIntMethod(env, certPathList, g_CollectionSize); + if (certsLen < certCount + 1) + goto cleanup; + + // for (int i = 0; i < certPathList.size(); ++i) { + // Certificate cert = certPathList.get(i); + // certs[i] = cert; + // } + int32_t i; + for (i = 0; i < certCount; ++i) + { + jobject cert = (*env)->CallObjectMethod(env, certPathList, g_ListGet, i); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + certs[i] = ToGRef(env, cert); + } + + // Certificate trustedCert = trustAnchor.getTrustedCert(); + // certs[i] = trustedCert; + jobject trustedCert = (*env)->CallObjectMethod(env, ctx->trustAnchor, g_TrustAnchorGetTrustedCert); + if (i == 0 || !(*env)->IsSameObject(env, certs[i-1], trustedCert)) + { + certs[i] = ToGRef(env, trustedCert); + ret = i + 1; + } + else + { + ret = i; + certs[i] = NULL; + } + +cleanup: + (*env)->DeleteLocalRef(env, certPathList); + return ret; +} + +int32_t AndroidCryptoNative_X509ChainGetErrorCount(X509ChainContext* ctx) +{ + assert(ctx != NULL); + JNIEnv* env = GetJNIEnv(); + int32_t count = (*env)->CallIntMethod(env, ctx->errorList, g_CollectionSize); + if (ctx->revocationErrorList != NULL) + { + count += (*env)->CallIntMethod(env, ctx->revocationErrorList, g_CollectionSize); + } + + return count; +} + +enum +{ + PKIXREASON_NAME_CHAINING, + PKIXREASON_INVALID_KEY_USAGE, + PKIXREASON_INVALID_POLICY, + PKIXREASON_NO_TRUST_ANCHOR, + PKIXREASON_UNRECOGNIZED_CRIT_EXT, + PKIXREASON_NOT_CA_CERT, + PKIXREASON_PATH_TOO_LONG, + PKIXREASON_INVALID_NAME, +}; + +enum +{ + BASICREASON_UNSPECIFIED, + BASICREASON_EXPIRED, + BASICREASON_NOT_YET_VALID, + BASICREASON_REVOKED, + BASICREASON_UNDETERMINED_REVOCATION_STATUS, + BASICREASON_INVALID_SIGNATURE, + BASICREASON_ALGORITHM_CONSTRAINED, +}; + +static PAL_X509ChainStatusFlags ChainStatusFromValidatorExceptionReason(JNIEnv* env, jobject reason) +{ + int value = (*env)->CallIntMethod(env, reason, g_EnumOrdinal); + if (g_CertPathExceptionBasicReasonClass != NULL && + (*env)->IsInstanceOf(env, reason, g_CertPathExceptionBasicReasonClass)) + { + switch (value) + { + case BASICREASON_UNSPECIFIED: + return PAL_X509ChainPartialChain; + case BASICREASON_EXPIRED: + case BASICREASON_NOT_YET_VALID: + return PAL_X509ChainNotTimeValid; + case BASICREASON_REVOKED: + return PAL_X509ChainRevoked; + case BASICREASON_UNDETERMINED_REVOCATION_STATUS: + return PAL_X509ChainRevocationStatusUnknown; + case BASICREASON_INVALID_SIGNATURE: + return PAL_X509ChainNotSignatureValid; + case BASICREASON_ALGORITHM_CONSTRAINED: + return PAL_X509ChainPartialChain; + } + } + else if (g_PKIXReasonClass != NULL && (*env)->IsInstanceOf(env, reason, g_PKIXReasonClass)) + { + switch (value) + { + case PKIXREASON_NAME_CHAINING: + return PAL_X509ChainPartialChain; + case PKIXREASON_INVALID_KEY_USAGE: + return PAL_X509ChainNotValidForUsage; + case PKIXREASON_INVALID_POLICY: + return PAL_X509ChainInvalidPolicyConstraints; + case PKIXREASON_NO_TRUST_ANCHOR: + return PAL_X509ChainPartialChain; + case PKIXREASON_UNRECOGNIZED_CRIT_EXT: + return PAL_X509ChainHasNotSupportedCriticalExtension; + case PKIXREASON_NOT_CA_CERT: + return PAL_X509ChainUntrustedRoot; + case PKIXREASON_PATH_TOO_LONG: + return PAL_X509ChainInvalidBasicConstraints; + case PKIXREASON_INVALID_NAME: + return PAL_X509ChainInvalidNameConstraints; + } + } + + return PAL_X509ChainPartialChain; +} + +static void PopulateValidationError(JNIEnv* env, jobject error, bool isRevocationError, ValidationError* out) +{ + int index = -1; + PAL_X509ChainStatusFlags chainStatus = PAL_X509ChainNoError; + if ((*env)->IsInstanceOf(env, error, g_CertPathValidatorExceptionClass)) + { + index = (*env)->CallIntMethod(env, error, g_CertPathValidatorExceptionGetIndex); + + // Get the reason (if the API is available) and convert it to a chain status flag + if (g_CertPathValidatorExceptionGetReason != NULL) + { + jobject reason = (*env)->CallObjectMethod(env, error, g_CertPathValidatorExceptionGetReason); + chainStatus = ChainStatusFromValidatorExceptionReason(env, reason); + (*env)->DeleteLocalRef(env, reason); + } + } + else + { + chainStatus = isRevocationError ? PAL_X509ChainRevocationStatusUnknown : PAL_X509ChainPartialChain; + } + + jobject message = (*env)->CallObjectMethod(env, error, g_ThrowableGetMessage); + uint16_t* messagePtr = NULL; + if (message != NULL) + { + jsize messageLen = message == NULL ? 0 : (*env)->GetStringLength(env, message); + + // +1 for null terminator + messagePtr = malloc(sizeof(uint16_t) * (size_t)(messageLen + 1)); + messagePtr[messageLen] = '\0'; + (*env)->GetStringRegion(env, message, 0, messageLen, (jchar*)messagePtr); + } + + // If the error is known to be from revocation checking, but couldn't be mapped to a revocation status, + // report it as RevocationStatusUnknown + if (isRevocationError && chainStatus != PAL_X509ChainRevocationStatusUnknown && chainStatus != PAL_X509ChainRevoked) + { + chainStatus = PAL_X509ChainRevocationStatusUnknown; + } + + out->message = messagePtr; + out->index = index; + out->chainStatus = chainStatus; + + (*env)->DeleteLocalRef(env, message); +} + +int32_t AndroidCryptoNative_X509ChainGetErrors(X509ChainContext* ctx, ValidationError* errors, int32_t errorsLen) +{ + assert(ctx != NULL); + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + + int32_t errorCount = (*env)->CallIntMethod(env, ctx->errorList, g_CollectionSize); + int32_t revocationErrorCount = + ctx->revocationErrorList == NULL ? 0 : (*env)->CallIntMethod(env, ctx->revocationErrorList, g_CollectionSize); + + if (errorsLen < errorCount + revocationErrorCount) + goto exit; + + // for (int i = 0; i < errorList.size(); ++i) { + // Throwable error = errorList.get(i); + // << populate errors[i] >> + // } + for (int32_t i = 0; i < errorCount; ++i) + { + jobject error = (*env)->CallObjectMethod(env, ctx->errorList, g_ListGet, i); + ON_EXCEPTION_PRINT_AND_GOTO(exit); + PopulateValidationError(env, error, false /*isRevocationError*/, &errors[i]); + (*env)->DeleteLocalRef(env, error); + } + + // for (int i = 0; i < revocationErrorList.size(); ++i) { + // Throwable error = revocationErrorList.get(i); + // << populate errors[i] >> + // } + for (int32_t i = 0; i < revocationErrorCount; ++i) + { + jobject error = (*env)->CallObjectMethod(env, ctx->revocationErrorList, g_ListGet, i); + ON_EXCEPTION_PRINT_AND_GOTO(exit); + PopulateValidationError(env, error, true /*isRevocationError*/, &errors[errorCount + i]); + (*env)->DeleteLocalRef(env, error); + } + + ret = SUCCESS; + +exit: + return ret; +} + +int32_t AndroidCryptoNative_X509ChainSetCustomTrustStore(X509ChainContext* ctx, + jobject* /*X509Certificate*/ customTrustStore, + int32_t customTrustStoreLen) +{ + assert(ctx != NULL); + JNIEnv* env = GetJNIEnv(); + + // HashSet anchors = new HashSet(customTrustStoreLen); + // for (Certificate cert : customTrustStore) { + // TrustAnchor anchor = new TrustAnchor(cert, null); + // anchors.Add(anchor); + // } + jobject anchors = (*env)->NewObject(env, g_HashSetClass, g_HashSetCtorWithCapacity, customTrustStoreLen); + for (int i = 0; i < customTrustStoreLen; ++i) + { + jobject anchor = (*env)->NewObject(env, g_TrustAnchorClass, g_TrustAnchorCtor, customTrustStore[i], NULL); + (*env)->CallBooleanMethod(env, anchors, g_HashSetAdd, anchor); + (*env)->DeleteLocalRef(env, anchor); + } + + // params.setTrustAnchors(anchors); + (*env)->CallVoidMethod(env, ctx->params, g_PKIXBuilderParametersSetTrustAnchors, anchors); + + (*env)->DeleteLocalRef(env, anchors); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +bool AndroidCryptoNative_X509ChainSupportsRevocationOptions(void) +{ + return g_CertPathValidatorGetRevocationChecker != NULL && g_PKIXRevocationCheckerClass != NULL; +} + +static jobject /*CertPath*/ CreateCertPathFromAnchor(JNIEnv* env, jobject /*TrustAnchor*/ trustAnchor) +{ + jobject ret = NULL; + INIT_LOCALS(loc, certList, trustedCert, certFactoryType, certFactory); + + // ArrayList certList = new ArrayList(1); + loc[certList] = (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtorWithCapacity, 1); + + // Certificate trustedCert = trustAnchor.getTrustedCert(); + // certList.add(trustedCert); + loc[trustedCert] = (*env)->CallObjectMethod(env, trustAnchor, g_TrustAnchorGetTrustedCert); + (*env)->CallBooleanMethod(env, loc[certList], g_ArrayListAdd, loc[trustedCert]); + + // CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + loc[certFactoryType] = JSTRING("X.509"); + loc[certFactory] = + (*env)->CallStaticObjectMethod(env, g_CertFactoryClass, g_CertFactoryGetInstance, loc[certFactoryType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // CertPath certPath = certFactory.generateCertPath(certList); + jobject certPath = + (*env)->CallObjectMethod(env, loc[certFactory], g_CertFactoryGenerateCertPathFromList, loc[certList]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = certPath; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +static int32_t ValidateWithRevocation(JNIEnv* env, + X509ChainContext* ctx, + jobject /*CertPathValidator*/ validator, + PAL_X509RevocationMode revocationMode, + PAL_X509RevocationFlag revocationFlag) +{ + assert(ctx != NULL); + assert(validator != NULL); + + int32_t ret = FAIL; + INIT_LOCALS(loc, certPathFromAnchor, options, checker, result, ex); + + if (revocationMode == X509RevocationMode_Offline) + { + // Android does not supply a way to disable OCSP/CRL fetching + LOG_INFO("Treating revocation mode 'Offline' as 'Online'."); + } + + jobject certPathToUse = NULL; + if (revocationFlag == X509RevocationFlag_EntireChain) + { + LOG_INFO("Treating revocation flag 'EntireChain' as 'ExcludeRoot'. " + "Revocation will be not be checked for root certificate."); + + certPathToUse = ctx->certPath; + } + else if (revocationFlag == X509RevocationFlag_EndCertificateOnly) + { + // List certPathList = certPath.getCertificates(); + // int certCount = certPathList.size(); + jobject certPathList = (*env)->CallObjectMethod(env, ctx->certPath, g_CertPathGetCertificates); + int certCount = (int)(*env)->CallIntMethod(env, certPathList, g_CollectionSize); + if (certCount == 0) + { + // If the chain only consists only of the trust anchor, create a path with just + // the trust anchor for revocation checking for end certificate only. This should + // still pass normal (non-revocation) validation when it is the only certificate. + loc[certPathFromAnchor] = CreateCertPathFromAnchor(env, ctx->trustAnchor); + certPathToUse = loc[certPathFromAnchor]; + } + else + { + certPathToUse = ctx->certPath; + if (AndroidCryptoNative_X509ChainSupportsRevocationOptions()) + { + // Only add the ONLY_END_ENTITY if we are not just checking the trust anchor. If ONLY_END_ENTITY is + // specified, revocation checking will skip the trust anchor even if it is the only certificate. + + // HashSet options = new HashSet(3); + // options.add(PKIXRevocationChecker.Option.ONLY_END_ENTITY); + loc[options] = (*env)->NewObject(env, g_HashSetClass, g_HashSetCtorWithCapacity, 3); + jobject endOnly = (*env)->GetStaticObjectField( + env, g_PKIXRevocationCheckerOptionClass, g_PKIXRevocationCheckerOptionOnlyEndEntity); + (*env)->CallBooleanMethod(env, loc[options], g_HashSetAdd, endOnly); + (*env)->DeleteLocalRef(env, endOnly); + } + else + { + LOG_INFO("Treating revocation flag 'EndCertificateOnly' as 'ExcludeRoot'. " + "Revocation will be checked for non-end certificates."); + } + } + + (*env)->DeleteLocalRef(env, certPathList); + } + else + { + assert(revocationFlag == X509RevocationFlag_ExcludeRoot); + certPathToUse = ctx->certPath; + } + + jobject params = ctx->params; + if (AndroidCryptoNative_X509ChainSupportsRevocationOptions()) + { + // PKIXRevocationChecker checker = validator.getRevocationChecker(); + loc[checker] = (*env)->CallObjectMethod(env, validator, g_CertPathValidatorGetRevocationChecker); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // Set any specific options + if (loc[options] != NULL) + { + // checker.setOptions(options); + (*env)->CallVoidMethod(env, loc[checker], g_PKIXRevocationCheckerSetOptions, loc[options]); + } + + // params.addCertPathChecker(checker); + (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersAddCertPathChecker, loc[checker]); + } + + // params.setRevocationEnabled(true); + // PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(certPathToUse, params); + (*env)->CallVoidMethod(env, params, g_PKIXBuilderParametersSetRevocationEnabled, true); + loc[result] = (*env)->CallObjectMethod(env, validator, g_CertPathValidatorValidate, certPathToUse, params); + if (TryGetJNIException(env, &loc[ex], false /*printException*/)) + { + if (ctx->revocationErrorList == NULL) + { + ctx->revocationErrorList = ToGRef(env, (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtor)); + } + + (*env)->CallBooleanMethod(env, ctx->revocationErrorList, g_ArrayListAdd, loc[ex]); + } + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +int32_t AndroidCryptoNative_X509ChainValidate(X509ChainContext* ctx, + PAL_X509RevocationMode revocationMode, + PAL_X509RevocationFlag revocationFlag, + bool* checkedRevocation) +{ + assert(ctx != NULL); + assert(checkedRevocation != NULL); + JNIEnv* env = GetJNIEnv(); + + *checkedRevocation = false; + int32_t ret = FAIL; + INIT_LOCALS(loc, validatorType, validator, result, ex); + + // String validatorType = "PKIX"; + // CertPathValidator validator = CertPathValidator.getInstance(validatorType); + loc[validatorType] = JSTRING("PKIX"); + loc[validator] = (*env)->CallStaticObjectMethod( + env, g_CertPathValidatorClass, g_CertPathValidatorGetInstance, loc[validatorType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)validator.validate(certPath, params); + loc[result] = + (*env)->CallObjectMethod(env, loc[validator], g_CertPathValidatorValidate, ctx->certPath, ctx->params); + if (TryGetJNIException(env, &loc[ex], false /*printException*/)) + { + (*env)->CallBooleanMethod(env, ctx->errorList, g_ArrayListAdd, loc[ex]); + ret = SUCCESS; + } + else if (revocationMode != X509RevocationMode_NoCheck) + { + ret = ValidateWithRevocation(env, ctx, loc[validator], revocationMode, revocationFlag); + *checkedRevocation = true; + } + else + { + ret = SUCCESS; + } + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509chain.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509chain.h new file mode 100644 index 0000000000000..4c92fa74c5659 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509chain.h @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "pal_jni.h" + +typedef struct X509ChainContext_t X509ChainContext; +typedef struct ValidationError_t ValidationError; + +/* +Create a context for building a certificate chain +*/ +PALEXPORT X509ChainContext* AndroidCryptoNative_X509ChainCreateContext(jobject /*X509Certificate*/ cert, + jobject* /*X509Certificate*/ extraStore, + int32_t extraStoreLen); + +/* +Destroy the context +*/ +PALEXPORT void AndroidCryptoNative_X509ChainDestroyContext(X509ChainContext* ctx); + +/* +Build a certificate path + +Always validates time and trust root. +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainBuild(X509ChainContext* ctx, int64_t timeInMsFromUnixEpoch); + +/* +Return the number of certificates in the path +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainGetCertificateCount(X509ChainContext* ctx); + +/* +Get the certificates in the path. + +Returns the number of certificates exported. +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainGetCertificates(X509ChainContext* ctx, + jobject* /*X509Certificate[]*/ certs, + int32_t certsLen); + +/* +Return the number of errors encountered when building and validating the certificate path +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainGetErrorCount(X509ChainContext* ctx); + +/* +Get the errors + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainGetErrors(X509ChainContext* ctx, + ValidationError* errors, + int32_t errorsLen); + +/* +Set the custom trust store +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainSetCustomTrustStore(X509ChainContext* ctx, + jobject* /*X509Certificate[]*/ customTrustStore, + int32_t customTrustStoreLen); + +/* +Returns true if revocation checking is supported. Returns false otherwise. +*/ +PALEXPORT bool AndroidCryptoNative_X509ChainSupportsRevocationOptions(void); + +// Matches managed X509RevocationMode enum +enum +{ + X509RevocationMode_NoCheck = 0, + X509RevocationMode_Online = 1, + X509RevocationMode_Offline = 2, +}; +typedef uint32_t PAL_X509RevocationMode; + +// Matches managed X509RevocationFlag enum +enum +{ + X509RevocationFlag_EndCertificateOnly = 0, + X509RevocationFlag_EntireChain = 1, + X509RevocationFlag_ExcludeRoot = 2, +}; +typedef uint32_t PAL_X509RevocationFlag; + +/* +Validate a certificate path. +*/ +PALEXPORT int32_t AndroidCryptoNative_X509ChainValidate(X509ChainContext* chain, + PAL_X509RevocationMode revocationMode, + PAL_X509RevocationFlag revocationFlag, + bool* checkedRevocation); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c new file mode 100644 index 0000000000000..64b839d2018d3 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.c @@ -0,0 +1,454 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_x509store.h" +#include "pal_eckey.h" +#include "pal_misc.h" +#include "pal_rsa.h" + +#include +#include +#include + +typedef enum +{ + EntryFlags_None = 0, + EntryFlags_HasCertificate = 1, + EntryFlags_HasPrivateKey = 2, + EntryFlags_MatchesCertificate = 4, +} EntryFlags; + +// Returns whether or not the store contains the specified alias +// If the entry exists, the flags parameter is set based on the contents of the entry +static bool ContainsEntryForAlias( + JNIEnv* env, jobject /*KeyStore*/ store, jobject /*X509Certificate*/ cert, jstring alias, EntryFlags* flags) +{ + bool ret = false; + EntryFlags flagsLocal = EntryFlags_None; + + INIT_LOCALS(loc, entry, existingCert); + + bool containsAlias = (*env)->CallBooleanMethod(env, store, g_KeyStoreContainsAlias, alias); + if (!containsAlias) + goto cleanup; + + ret = true; + + // KeyStore.Entry entry = store.getEntry(alias, null); + // if (entry instanceof KeyStore.PrivateKeyEntry) { + // existingCert = ((KeyStore.PrivateKeyEntry)entry).getCertificate(); + // } else if (entry instanceof KeyStore.TrustedCertificateEntry) { + // existingCert = ((KeyStore.TrustedCertificateEntry)entry).getTrustedCertificate(); + // } + loc[entry] = (*env)->CallObjectMethod(env, store, g_KeyStoreGetEntry, alias, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + if ((*env)->IsInstanceOf(env, loc[entry], g_PrivateKeyEntryClass)) + { + // Private key entries always have a certificate + flagsLocal |= EntryFlags_HasCertificate; + flagsLocal |= EntryFlags_HasPrivateKey; + loc[existingCert] = (*env)->CallObjectMethod(env, loc[entry], g_PrivateKeyEntryGetCertificate); + } + else if ((*env)->IsInstanceOf(env, loc[entry], g_TrustedCertificateEntryClass)) + { + flagsLocal |= EntryFlags_HasCertificate; + loc[existingCert] = (*env)->CallObjectMethod(env, loc[entry], g_TrustedCertificateEntryGetTrustedCertificate); + } + else + { + // Entry for alias exists, but doesn't represent a certificate or private key + certificate + goto cleanup; + } + + assert(loc[existingCert] != NULL); + if ((*env)->CallBooleanMethod(env, cert, g_X509CertEquals, loc[existingCert])) + { + flagsLocal |= EntryFlags_MatchesCertificate; + } + +cleanup: + RELEASE_LOCALS(loc, env); + *flags = flagsLocal; + return ret; +} + +static bool ContainsMatchingCertificateForAlias(JNIEnv* env, + jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + jstring alias) +{ + EntryFlags flags; + if (!ContainsEntryForAlias(env, store, cert, alias, &flags)) + return false; + + EntryFlags matchesFlags = EntryFlags_HasCertificate & EntryFlags_MatchesCertificate; + return (flags & matchesFlags) == matchesFlags; +} + +int32_t AndroidCryptoNative_X509StoreAddCertificate(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + const char* hashString) +{ + assert(store != NULL); + assert(cert != NULL); + + JNIEnv* env = GetJNIEnv(); + + jstring alias = JSTRING(hashString); + EntryFlags flags; + if (ContainsEntryForAlias(env, store, cert, alias, &flags)) + { + ReleaseLRef(env, alias); + EntryFlags matchesFlags = EntryFlags_HasCertificate & EntryFlags_MatchesCertificate; + if ((flags & matchesFlags) != matchesFlags) + { + LOG_ERROR("Store already contains alias with entry that does not match the expected certificate"); + return FAIL; + } + + // Certificate is already in store - nothing to do + LOG_DEBUG("Store already contains certificate"); + return SUCCESS; + } + + // store.setCertificateEntry(alias, cert); + (*env)->CallVoidMethod(env, store, g_KeyStoreSetCertificateEntry, alias, cert); + (*env)->DeleteLocalRef(env, alias); + + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_X509StoreAddCertificateWithPrivateKey(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + void* key, + PAL_KeyAlgorithm algorithm, + const char* hashString) +{ + assert(store != NULL); + assert(cert != NULL); + assert(key != NULL); + + int32_t ret = FAIL; + JNIEnv* env = GetJNIEnv(); + + INIT_LOCALS(loc, alias, certs); + jobject privateKey = NULL; + + loc[alias] = JSTRING(hashString); + + EntryFlags flags; + if (ContainsEntryForAlias(env, store, cert, loc[alias], &flags)) + { + EntryFlags matchesFlags = EntryFlags_HasCertificate & EntryFlags_MatchesCertificate; + if ((flags & matchesFlags) != matchesFlags) + { + RELEASE_LOCALS(loc, env); + LOG_ERROR("Store already contains alias with entry that does not match the expected certificate"); + return FAIL; + } + + if ((flags & EntryFlags_HasPrivateKey) == EntryFlags_HasPrivateKey) + { + RELEASE_LOCALS(loc, env); + // Certificate with private key is already in store - nothing to do + LOG_DEBUG("Store already contains certificate with private key"); + return SUCCESS; + } + + // Delete existing entry. We will replace the existing cert with the cert + private key. + // store.deleteEntry(alias); + (*env)->CallVoidMethod(env, store, g_KeyStoreDeleteEntry, loc[alias]); + } + + bool releasePrivateKey = true; + switch (algorithm) + { + case PAL_EC: + { + EC_KEY* ec = (EC_KEY*)key; + privateKey = (*env)->CallObjectMethod(env, ec->keyPair, g_keyPairGetPrivateMethod); + break; + } + case PAL_DSA: + { + // key is a KeyPair jobject + privateKey = (*env)->CallObjectMethod(env, key, g_keyPairGetPrivateMethod); + break; + } + case PAL_RSA: + { + RSA* rsa = (RSA*)key; + privateKey = rsa->privateKey; + releasePrivateKey = false; // Private key is a global ref stored directly on RSA handle + break; + } + default: + { + releasePrivateKey = false; + LOG_ERROR("Unknown algorithm for private key"); + goto cleanup; + } + } + + // X509Certificate[] certs = new X509Certificate[] { cert }; + // store.setKeyEntry(alias, privateKey, null, certs); + loc[certs] = (*env)->NewObjectArray(env, 1, g_X509CertClass, cert); + (*env)->CallVoidMethod(env, store, g_KeyStoreSetKeyEntry, loc[alias], privateKey, NULL, loc[certs]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + if (releasePrivateKey) + { + (*env)->DeleteLocalRef(env, privateKey); + } + + return ret; +} + +bool AndroidCryptoNative_X509StoreContainsCertificate(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + const char* hashString) +{ + assert(store != NULL); + assert(cert != NULL); + + JNIEnv* env = GetJNIEnv(); + jstring alias = JSTRING(hashString); + + bool containsCert = ContainsMatchingCertificateForAlias(env, store, cert, alias); + (*env)->DeleteLocalRef(env, alias); + return containsCert; +} + +static void* HandleFromKeys(JNIEnv* env, + jobject /*PublicKey*/ publicKey, + jobject /*PrivateKey*/ privateKey, + PAL_KeyAlgorithm* algorithm) +{ + if ((*env)->IsInstanceOf(env, privateKey, g_DSAKeyClass)) + { + *algorithm = PAL_DSA; + return AndroidCryptoNative_CreateKeyPair(env, publicKey, privateKey); + } + else if ((*env)->IsInstanceOf(env, privateKey, g_ECKeyClass)) + { + *algorithm = PAL_EC; + return AndroidCryptoNative_NewEcKeyFromKeys(env, publicKey, privateKey); + } + else if ((*env)->IsInstanceOf(env, privateKey, g_RSAKeyClass)) + { + *algorithm = PAL_RSA; + return AndroidCryptoNative_NewRsaFromKeys(env, publicKey, privateKey); + } + + LOG_INFO("Ignoring unknown privake key type"); + *algorithm = PAL_UnknownAlgorithm; + return NULL; +} + +static int32_t +EnumerateCertificates(JNIEnv* env, jobject /*KeyStore*/ store, EnumCertificatesCallback cb, void* context) +{ + int32_t ret = FAIL; + + // Enumeration aliases = store.aliases(); + jobject aliases = (*env)->CallObjectMethod(env, store, g_KeyStoreAliases); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // while (aliases.hasMoreElements()) { + // String alias = aliases.nextElement(); + // KeyStore.Entry entry = store.getEntry(alias); + // if (entry instanceof KeyStore.PrivateKeyEntry) { + // ... + // } else if (entry instanceof KeyStore.TrustedCertificateEntry) { + // ... + // } + // } + jboolean hasNext = (*env)->CallBooleanMethod(env, aliases, g_EnumerationHasMoreElements); + while (hasNext) + { + INIT_LOCALS(loc, alias, entry, cert, publicKey, privateKey); + + loc[alias] = (*env)->CallObjectMethod(env, aliases, g_EnumerationNextElement); + ON_EXCEPTION_PRINT_AND_GOTO(loop_cleanup); + + loc[entry] = (*env)->CallObjectMethod(env, store, g_KeyStoreGetEntry, loc[alias], NULL); + ON_EXCEPTION_PRINT_AND_GOTO(loop_cleanup); + + if ((*env)->IsInstanceOf(env, loc[entry], g_PrivateKeyEntryClass)) + { + // Certificate cert = entry.getCertificate(); + // Public publicKey = cert.getPublicKey(); + // PrivateKey privateKey = entry.getPrivateKey(); + loc[cert] = (*env)->CallObjectMethod(env, loc[entry], g_PrivateKeyEntryGetCertificate); + loc[publicKey] = (*env)->CallObjectMethod(env, loc[cert], g_X509CertGetPublicKey); + loc[privateKey] = (*env)->CallObjectMethod(env, loc[entry], g_PrivateKeyEntryGetPrivateKey); + + PAL_KeyAlgorithm keyAlgorithm = PAL_UnknownAlgorithm; + void* keyHandle = HandleFromKeys(env, loc[publicKey], loc[privateKey], &keyAlgorithm); + + // Private key entries always have a certificate. + // For key algorithms we recognize, the certificate and private key handle are given to the callback. + // For key algorithms we do not recognize, only the certificate will be given to the callback. + cb(AddGRef(env, loc[cert]), keyHandle, keyAlgorithm, context); + } + else if ((*env)->IsInstanceOf(env, loc[entry], g_TrustedCertificateEntryClass)) + { + // Certificate cert = entry.getTrustedCertificate(); + loc[cert] = (*env)->CallObjectMethod(env, loc[entry], g_TrustedCertificateEntryGetTrustedCertificate); + cb(AddGRef(env, loc[cert]), NULL /*privateKey*/, PAL_UnknownAlgorithm, context); + } + + loop_cleanup: + RELEASE_LOCALS(loc, env); + + hasNext = (*env)->CallBooleanMethod(env, aliases, g_EnumerationHasMoreElements); + } + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, aliases); + return ret; +} + +int32_t AndroidCryptoNative_X509StoreEnumerateCertificates(jobject /*KeyStore*/ store, + EnumCertificatesCallback cb, + void* context) +{ + assert(store != NULL); + assert(cb != NULL); + + JNIEnv* env = GetJNIEnv(); + return EnumerateCertificates(env, store, cb, context); +} + +static bool SystemAliasFilter(JNIEnv* env, jstring alias) +{ + const char systemPrefix[] = "system:"; + size_t prefixLen = (sizeof(systemPrefix) - 1) / sizeof(char); + + const char* aliasPtr = (*env)->GetStringUTFChars(env, alias, NULL); + bool isSystem = (strncmp(aliasPtr, systemPrefix, prefixLen) == 0); + (*env)->ReleaseStringUTFChars(env, alias, aliasPtr); + return isSystem; +} + +typedef bool (*FilterAliasFunction)(JNIEnv* env, jstring alias); +static int32_t EnumerateTrustedCertificates( + JNIEnv* env, jobject /*KeyStore*/ store, bool systemOnly, EnumTrustedCertificatesCallback cb, void* context) +{ + int32_t ret = FAIL; + + // Filter to only system certificates if necessary + // System certificates are included for 'current user' (matches Windows) + FilterAliasFunction filter = systemOnly ? &SystemAliasFilter : NULL; + + // Enumeration aliases = store.aliases(); + jobject aliases = (*env)->CallObjectMethod(env, store, g_KeyStoreAliases); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // while (aliases.hasMoreElements()) { + // String alias = aliases.nextElement(); + // X509Certificate cert = (X509Certificate)store.getCertificate(alias); + // if (cert != null) { + // cb(cert, context); + // } + // } + jboolean hasNext = (*env)->CallBooleanMethod(env, aliases, g_EnumerationHasMoreElements); + while (hasNext) + { + jstring alias = (*env)->CallObjectMethod(env, aliases, g_EnumerationNextElement); + ON_EXCEPTION_PRINT_AND_GOTO(loop_cleanup); + + if (filter == NULL || filter(env, alias)) + { + jobject cert = (*env)->CallObjectMethod(env, store, g_KeyStoreGetCertificate, alias); + if (cert != NULL && !CheckJNIExceptions(env)) + { + cert = ToGRef(env, cert); + cb(cert, context); + } + } + + hasNext = (*env)->CallBooleanMethod(env, aliases, g_EnumerationHasMoreElements); + + loop_cleanup: + (*env)->DeleteLocalRef(env, alias); + } + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, aliases); + return ret; +} + +int32_t AndroidCryptoNative_X509StoreEnumerateTrustedCertificates(bool systemOnly, + EnumTrustedCertificatesCallback cb, + void* context) +{ + assert(cb != NULL); + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + INIT_LOCALS(loc, storeType, store); + + // KeyStore store = KeyStore.getInstance("AndroidCAStore"); + // store.load(null, null); + loc[storeType] = JSTRING("AndroidCAStore"); + loc[store] = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, loc[storeType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[store], g_KeyStoreLoad, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = EnumerateTrustedCertificates(env, loc[store], systemOnly, cb, context); + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +jobject /*KeyStore*/ AndroidCryptoNative_X509StoreOpenDefault(void) +{ + JNIEnv* env = GetJNIEnv(); + jobject ret = NULL; + + // KeyStore store = KeyStore.getInstance("AndroidKeyStore"); + // store.load(null, null); + jstring storeType = JSTRING("AndroidKeyStore"); + jobject store = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, storeType); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, store, g_KeyStoreLoad, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + ret = ToGRef(env, store); + +cleanup: + (*env)->DeleteLocalRef(env, storeType); + return ret; +} + +int32_t AndroidCryptoNative_X509StoreRemoveCertificate(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + const char* hashString) +{ + assert(store != NULL); + + JNIEnv* env = GetJNIEnv(); + + jstring alias = JSTRING(hashString); + if (!ContainsMatchingCertificateForAlias(env, store, cert, alias)) + { + // Certificate is not in store - nothing to do + return SUCCESS; + } + + // store.deleteEntry(alias); + (*env)->CallVoidMethod(env, store, g_KeyStoreDeleteEntry, alias); + + (*env)->DeleteLocalRef(env, alias); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.h new file mode 100644 index 0000000000000..1702f19ca0b45 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509store.h @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_jni.h" +#include "pal_x509.h" + +/* +Add a certificate to the specified key store + +If the certificate is already in the store, this function simply returns success. +Returns 1 on success, 0 otherwise. +*/ +PALEXPORT int32_t AndroidCryptoNative_X509StoreAddCertificate(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + const char* hashString); + +/* +Add a certificate with a private key to the specified key store + +If the certificate is already in the store, it is replaced. This means that a certificate +without a private key will be replaced with one with a private key. +Returns 1 on success, 0 otherwise. +*/ +PALEXPORT int32_t AndroidCryptoNative_X509StoreAddCertificateWithPrivateKey(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + void* key, + PAL_KeyAlgorithm algorithm, + const char* hashString); + +/* +Get whether or not a certificate is contained in the specified key store +*/ +PALEXPORT bool AndroidCryptoNative_X509StoreContainsCertificate(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + const char* hashString); + +/* +Enumerate certificates for the specified key store +The certificate and key passed to the callback will already be a global jobject reference. +*/ +typedef void (*EnumCertificatesCallback)(jobject /*X509Certificate*/ cert, + void* privateKey, + PAL_KeyAlgorithm algorithm, + void* context); +PALEXPORT int32_t AndroidCryptoNative_X509StoreEnumerateCertificates(jobject /*KeyStore*/ store, + EnumCertificatesCallback cb, + void* context); + +/* +Enumerate trusted certificates +The certificate passed to the callback will already be a global jobject reference. + +Returns 1 on success, 0 otherwise. +*/ +typedef void (*EnumTrustedCertificatesCallback)(jobject /*X509Certificate*/ cert, void* context); +PALEXPORT int32_t AndroidCryptoNative_X509StoreEnumerateTrustedCertificates(bool isSystem, + EnumTrustedCertificatesCallback cb, + void* context); +/* +Open the default key store +*/ +PALEXPORT jobject /*KeyStore*/ AndroidCryptoNative_X509StoreOpenDefault(void); + +/* +Remove a certificate from the specified key store + +If the certificate is not in the store, this function returns success. +Returns 1 on success, 0 otherwise. +*/ +PALEXPORT int32_t AndroidCryptoNative_X509StoreRemoveCertificate(jobject /*KeyStore*/ store, + jobject /*X509Certificate*/ cert, + const char* hashString); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt index a22275331045c..56d07f732e101 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -65,4 +65,4 @@ if (GEN_SHARED_LIB) install_with_stripped_symbols (System.Security.Cryptography.Native.Apple PROGRAMS .) endif() -install (TARGETS System.Security.Cryptography.Native.Apple-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.Security.Cryptography.Native.Apple-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.c index 190c1dc1c9976..dada6ea247783 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.c @@ -4,12 +4,9 @@ #include "pal_ssl.h" #include -// TLS 1.3 is only defined with 10.13 headers, but we build on 10.12 -#define kTLSProtocol13_ForwardDef 10 - // 10.13.4 introduced public API but linking would fail on all prior versions. // For that reason we use function pointers instead of direct call. -// This can be revisited after we drop support for 10.12. +// This can be revisited after we drop support for 10.12 and iOS 10 static OSStatus (*SSLSetALPNProtocolsPtr)(SSLContextRef context, CFArrayRef protocols) = NULL; static OSStatus (*SSLCopyALPNProtocolsPtr)(SSLContextRef context, CFArrayRef* protocols) = NULL; @@ -39,11 +36,11 @@ static SSLProtocol PalSslProtocolToSslProtocol(PAL_SslProtocol palProtocolId) { switch (palProtocolId) { - case PAL_SslProtocol_Tls13: - return kTLSProtocol13_ForwardDef; - case PAL_SslProtocol_Tls12: #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + case PAL_SslProtocol_Tls13: + return kTLSProtocol13; + case PAL_SslProtocol_Tls12: return kTLSProtocol12; case PAL_SslProtocol_Tls11: return kTLSProtocol11; @@ -546,7 +543,7 @@ int32_t AppleCryptoNative_SslGetProtocolVersion(SSLContextRef sslContext, PAL_Ss { PAL_SslProtocol matchedProtocol = PAL_SslProtocol_None; - if (protocol == kTLSProtocol13_ForwardDef) + if (protocol == kTLSProtocol13) matchedProtocol = PAL_SslProtocol_Tls13; else if (protocol == kTLSProtocol12) matchedProtocol = PAL_SslProtocol_Tls12; diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h index 067f54b099269..295688c0004d9 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h @@ -6,21 +6,10 @@ #include "pal_digest.h" #include "pal_seckey.h" #include "pal_compiler.h" +#include #include -enum -{ - PAL_X509Unknown = 0, - PAL_Certificate = 1, - PAL_SerializedCert = 2, - PAL_Pkcs12 = 3, - PAL_SerializedStore = 4, - PAL_Pkcs7 = 5, - PAL_Authenticode = 6, -}; -typedef uint32_t PAL_X509ContentType; - /* Given a handle, determine if it represents a SecCertificateRef, SecIdentityRef, or other. If the handle is a certificate or identity it is CFRetain()ed (and must later be CFRelease()d). diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h index 6151db60e671b..e11bf3a821132 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h @@ -6,40 +6,10 @@ #include "pal_digest.h" #include "pal_seckey.h" #include "pal_compiler.h" +#include #include -enum -{ - PAL_X509ChainNoError = 0, - PAL_X509ChainNotTimeValid = 0x00000001, - PAL_X509ChainNotTimeNested = 0x00000002, - PAL_X509ChainRevoked = 0x00000004, - PAL_X509ChainNotSignatureValid = 0x00000008, - PAL_X509ChainNotValidForUsage = 0x00000010, - PAL_X509ChainUntrustedRoot = 0x00000020, - PAL_X509ChainRevocationStatusUnknown = 0x00000040, - PAL_X509ChainCyclic = 0x00000080, - PAL_X509ChainInvalidExtension = 0x00000100, - PAL_X509ChainInvalidPolicyConstraints = 0x00000200, - PAL_X509ChainInvalidBasicConstraints = 0x00000400, - PAL_X509ChainInvalidNameConstraints = 0x00000800, - PAL_X509ChainHasNotSupportedNameConstraint = 0x00001000, - PAL_X509ChainHasNotDefinedNameConstraint = 0x00002000, - PAL_X509ChainHasNotPermittedNameConstraint = 0x00004000, - PAL_X509ChainHasExcludedNameConstraint = 0x00008000, - PAL_X509ChainPartialChain = 0x00010000, - PAL_X509ChainCtlNotTimeValid = 0x00020000, - PAL_X509ChainCtlNotSignatureValid = 0x00040000, - PAL_X509ChainCtlNotValidForUsage = 0x00080000, - PAL_X509ChainOfflineRevocation = 0x01000000, - PAL_X509ChainNoIssuanceChainPolicy = 0x02000000, - PAL_X509ChainExplicitDistrust = 0x04000000, - PAL_X509ChainHasNotSupportedCriticalExtension = 0x08000000, - PAL_X509ChainHasWeakSignature = 0x00100000, -}; -typedef uint32_t PAL_X509ChainStatusFlags; - #define PAL_X509ChainErrorNone 0 #define PAL_X509ChainErrorUnknownValueType (((uint64_t)0x0001L) << 32) #define PAL_X509ChainErrorUnknownValue (((uint64_t)0x0002L) << 32) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt index 83b9ae5999816..62668e747c704 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/CMakeLists.txt @@ -119,4 +119,4 @@ if (GEN_SHARED_LIB) install_with_stripped_symbols (System.Security.Cryptography.Native.OpenSsl PROGRAMS .) endif() -install (TARGETS System.Security.Cryptography.Native.OpenSsl-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.Security.Cryptography.Native.OpenSsl-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c index cc383b0a7fbfe..27b72a320c6f5 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.c @@ -298,6 +298,23 @@ int32_t local_DSA_set0_key(DSA* dsa, BIGNUM* bnY, BIGNUM* bnX) return 1; } +RSA* local_EVP_PKEY_get0_RSA(EVP_PKEY* pkey) +{ + if (pkey == NULL) + { + return NULL; + } + + RSA* rsa = EVP_PKEY_get1_RSA(pkey); + + if (rsa != NULL) + { + RSA_free(rsa); + } + + return rsa; +} + int32_t local_EVP_PKEY_up_ref(EVP_PKEY* pkey) { if (!pkey) @@ -788,4 +805,12 @@ int local_BIO_up_ref(BIO *bio) return CRYPTO_add_lock(&bio->references, 1, CRYPTO_LOCK_BIO, __FILE__, __LINE__) > 1; } + +int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2) +{ + // On OpenSSL 1.0.2 there aren't two different identifiers for RSA, + // so just pass the request on th EVP_PKEY_CTX_ctrl with the only identifier defined. + return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, optype, cmd, p1, p2); +} + #endif diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h index c8905b44eb8ec..b9060b7f761e9 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/apibridge.h @@ -15,6 +15,7 @@ int32_t local_DSA_set0_pqg(DSA* dsa, BIGNUM* bnP, BIGNUM* bnQ, BIGNUM* bnG); void local_EVP_CIPHER_CTX_free(EVP_CIPHER_CTX* ctx); EVP_CIPHER_CTX* local_EVP_CIPHER_CTX_new(void); int32_t local_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx); +RSA* local_EVP_PKEY_get0_RSA(EVP_PKEY* pkey); int32_t local_EVP_PKEY_up_ref(EVP_PKEY* pkey); void local_HMAC_CTX_free(HMAC_CTX* ctx); HMAC_CTX* local_HMAC_CTX_new(void); @@ -26,6 +27,7 @@ int32_t local_RSA_meth_get_flags(const RSA_METHOD* meth); int32_t local_RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp); int32_t local_RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q); int32_t local_RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d); +int32_t local_RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2); int32_t local_SSL_is_init_finished(const SSL* ssl); int32_t local_SSL_CTX_config(SSL_CTX* ctx, const char* name); unsigned long local_SSL_CTX_set_options(SSL_CTX* ctx, unsigned long options); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c index bd379df88242e..f92f0eabd5342 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/entrypoints.c @@ -151,6 +151,7 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_EvpPkeySetDsa) DllImportEntry(CryptoNative_EvpPkeySetEcKey) DllImportEntry(CryptoNative_EvpPkeySetRsa) + DllImportEntry(CryptoNative_EvpPKeySize) DllImportEntry(CryptoNative_EvpRC2Cbc) DllImportEntry(CryptoNative_EvpRC2Ecb) DllImportEntry(CryptoNative_EvpSha1) @@ -221,12 +222,11 @@ static const Entry s_cryptoNative[] = DllImportEntry(CryptoNative_ReadX509AsDerFromBio) DllImportEntry(CryptoNative_RecursiveFreeX509Stack) DllImportEntry(CryptoNative_RsaCreate) + DllImportEntry(CryptoNative_RsaDecrypt) DllImportEntry(CryptoNative_RsaDestroy) - DllImportEntry(CryptoNative_RsaGenerateKeyEx) - DllImportEntry(CryptoNative_RsaPrivateDecrypt) + DllImportEntry(CryptoNative_RsaGenerateKey) DllImportEntry(CryptoNative_RsaPublicEncrypt) - DllImportEntry(CryptoNative_RsaSign) - DllImportEntry(CryptoNative_RsaSignPrimitive) + DllImportEntry(CryptoNative_RsaSignHash) DllImportEntry(CryptoNative_RsaSize) DllImportEntry(CryptoNative_RsaUpRef) DllImportEntry(CryptoNative_RsaVerificationPrimitive) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Security.Cryptography.Native/extra_libs.cmake index 5a0776577a98a..3090da4e9d412 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/extra_libs.cmake @@ -3,28 +3,9 @@ macro(append_extra_cryptography_libs NativeLibsExtra) if(CMAKE_STATIC_LIB_LINK) set(CMAKE_FIND_LIBRARY_SUFFIXES .a) endif(CMAKE_STATIC_LIB_LINK) - - if(CLR_CMAKE_TARGET_ANDROID AND NOT CROSS_ROOTFS) - # TEMP: consume OpenSSL dependencies from external sources via env. variables - set(OPENSSL_FOUND 1) - set(OPENSSL_INCLUDE_DIR $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/ssl/include) - if(CLR_CMAKE_TARGET_ARCH_ARM64) - set(OPENSSL_CRYPTO_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/crypto/libs/android.arm64-v8a/libcrypto.so) - set(OPENSSL_SSL_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/ssl/libs/android.arm64-v8a/libssl.so) - elseif(CLR_CMAKE_TARGET_ARCH_ARM) - set(OPENSSL_CRYPTO_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/crypto/libs/android.armeabi-v7a/libcrypto.so) - set(OPENSSL_SSL_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/ssl/libs/android.armeabi-v7a/libssl.so) - elseif(CLR_CMAKE_TARGET_ARCH_I386) - set(OPENSSL_CRYPTO_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/crypto/libs/android.x86/libcrypto.so) - set(OPENSSL_SSL_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/ssl/libs/android.x86/libssl.so) - else() - set(OPENSSL_CRYPTO_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/crypto/libs/android.x86_64/libcrypto.so) - set(OPENSSL_SSL_LIBRARY $ENV{ANDROID_OPENSSL_AAR}/prefab/modules/ssl/libs/android.x86_64/libssl.so) - endif() - else() - find_package(OpenSSL) - endif() - + + find_package(OpenSSL) + if(NOT OPENSSL_FOUND) message(FATAL_ERROR "!!! Cannot find libssl and System.Security.Cryptography.Native cannot build without it. Try installing libssl-dev (on Linux, but this may vary by distro) or openssl (on macOS) !!!. See the requirements document for your specific operating system: https://github.com/dotnet/runtime/tree/main/docs/workflow/requirements.") endif(NOT OPENSSL_FOUND) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h index 5490d1a83666c..f405c5f6186c7 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.h @@ -131,6 +131,7 @@ EVP_CIPHER_CTX* EVP_CIPHER_CTX_new(void); int32_t EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX* ctx); void EVP_MD_CTX_free(EVP_MD_CTX* ctx); EVP_MD_CTX* EVP_MD_CTX_new(void); +RSA* EVP_PKEY_get0_RSA(EVP_PKEY* pkey); int32_t EVP_PKEY_up_ref(EVP_PKEY* pkey); void HMAC_CTX_free(HMAC_CTX* ctx); HMAC_CTX* HMAC_CTX_new(void); @@ -148,6 +149,7 @@ void RSA_get0_factors(const RSA* rsa, const BIGNUM** p, const BIGNUM** q); void RSA_get0_key(const RSA* rsa, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d); int32_t RSA_meth_get_flags(const RSA_METHOD* meth); const RSA_METHOD* RSA_PKCS1_OpenSSL(void); +int32_t RSA_pkey_ctx_ctrl(EVP_PKEY_CTX* ctx, int32_t optype, int32_t cmd, int32_t p1, void* p2); int32_t RSA_set0_crt_params(RSA* rsa, BIGNUM* dmp1, BIGNUM* dmq1, BIGNUM* iqmp); int32_t RSA_set0_factors(RSA* rsa, BIGNUM* p, BIGNUM* q); int32_t RSA_set0_key(RSA* rsa, BIGNUM* n, BIGNUM* e, BIGNUM* d); @@ -176,6 +178,23 @@ const X509_ALGOR* X509_get0_tbs_sigalg(const X509* x509); X509_PUBKEY* X509_get_X509_PUBKEY(const X509* x509); int32_t X509_get_version(const X509* x509); int32_t X509_up_ref(X509* x509); + +// Redefine EVP_PKEY_CTX_set_rsa operations to use (local_)RSA_pkey_ctx_ctrl so the path is the same +// for 1.0-built on 1.1 as on 1.1-built on 1.1. +#undef EVP_PKEY_CTX_set_rsa_keygen_bits +#define EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) \ + RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL) + +// EVP_PKEY_CTX_set_rsa_oaep_md doesn't call RSA_pkey_ctx_ctrl in 1.1, so don't redefine it here. + +#undef EVP_PKEY_CTX_set_rsa_padding +#define EVP_PKEY_CTX_set_rsa_padding(ctx, pad) \ + RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, NULL) + +#undef EVP_PKEY_CTX_set_rsa_pss_saltlen +#define EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, len) \ + RSA_pkey_ctx_ctrl(ctx, (EVP_PKEY_OP_SIGN|EVP_PKEY_OP_VERIFY), EVP_PKEY_CTRL_RSA_PSS_SALTLEN, len, NULL) + #endif #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_0_2_RTM @@ -200,6 +219,11 @@ void SSL_CTX_set_alpn_select_cb(SSL_CTX* ctx, void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsigned int* len); #endif +// The value -1 has the correct meaning on 1.0.x, but the constant wasn't named. +#ifndef RSA_PSS_SALTLEN_DIGEST +#define RSA_PSS_SALTLEN_DIGEST -1 +#endif + #define API_EXISTS(fn) (fn != NULL) // List of all functions from the libssl that are used in the System.Security.Cryptography.Native. @@ -362,19 +386,31 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi RENAMED_FUNCTION(EVP_MD_CTX_free, EVP_MD_CTX_destroy) \ RENAMED_FUNCTION(EVP_MD_CTX_new, EVP_MD_CTX_create) \ REQUIRED_FUNCTION(EVP_MD_size) \ + REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_free) \ + REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \ + REQUIRED_FUNCTION(EVP_PKEY_CTX_new_id) \ + REQUIRED_FUNCTION(EVP_PKEY_base_id) \ + REQUIRED_FUNCTION(EVP_PKEY_decrypt) \ + REQUIRED_FUNCTION(EVP_PKEY_decrypt_init) \ REQUIRED_FUNCTION(EVP_PKEY_derive_set_peer) \ REQUIRED_FUNCTION(EVP_PKEY_derive_init) \ REQUIRED_FUNCTION(EVP_PKEY_derive) \ REQUIRED_FUNCTION(EVP_PKEY_free) \ + FALLBACK_FUNCTION(EVP_PKEY_get0_RSA) \ REQUIRED_FUNCTION(EVP_PKEY_get1_DSA) \ REQUIRED_FUNCTION(EVP_PKEY_get1_EC_KEY) \ REQUIRED_FUNCTION(EVP_PKEY_get1_RSA) \ + REQUIRED_FUNCTION(EVP_PKEY_keygen) \ + REQUIRED_FUNCTION(EVP_PKEY_keygen_init) \ REQUIRED_FUNCTION(EVP_PKEY_new) \ REQUIRED_FUNCTION(EVP_PKEY_set1_DSA) \ REQUIRED_FUNCTION(EVP_PKEY_set1_EC_KEY) \ REQUIRED_FUNCTION(EVP_PKEY_set1_RSA) \ + REQUIRED_FUNCTION(EVP_PKEY_sign) \ + REQUIRED_FUNCTION(EVP_PKEY_sign_init) \ + REQUIRED_FUNCTION(EVP_PKEY_size) \ FALLBACK_FUNCTION(EVP_PKEY_up_ref) \ REQUIRED_FUNCTION(EVP_rc2_cbc) \ REQUIRED_FUNCTION(EVP_rc2_ecb) \ @@ -453,16 +489,14 @@ void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsi FALLBACK_FUNCTION(RSA_get0_key) \ FALLBACK_FUNCTION(RSA_meth_get_flags) \ REQUIRED_FUNCTION(RSA_new) \ + FALLBACK_FUNCTION(RSA_pkey_ctx_ctrl) \ RENAMED_FUNCTION(RSA_PKCS1_OpenSSL, RSA_PKCS1_SSLeay) \ - REQUIRED_FUNCTION(RSA_private_decrypt) \ - REQUIRED_FUNCTION(RSA_private_encrypt) \ REQUIRED_FUNCTION(RSA_public_decrypt) \ REQUIRED_FUNCTION(RSA_public_encrypt) \ FALLBACK_FUNCTION(RSA_set0_crt_params) \ FALLBACK_FUNCTION(RSA_set0_factors) \ FALLBACK_FUNCTION(RSA_set0_key) \ REQUIRED_FUNCTION(RSA_set_method) \ - REQUIRED_FUNCTION(RSA_sign) \ REQUIRED_FUNCTION(RSA_size) \ REQUIRED_FUNCTION(RSA_up_ref) \ REQUIRED_FUNCTION(RSA_verify) \ @@ -773,19 +807,31 @@ FOR_ALL_OPENSSL_FUNCTIONS #define EVP_MD_CTX_free EVP_MD_CTX_free_ptr #define EVP_MD_CTX_new EVP_MD_CTX_new_ptr #define EVP_MD_size EVP_MD_size_ptr +#define EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_ctrl_ptr #define EVP_PKEY_CTX_free EVP_PKEY_CTX_free_ptr +#define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr +#define EVP_PKEY_CTX_new_id EVP_PKEY_CTX_new_id_ptr +#define EVP_PKEY_base_id EVP_PKEY_base_id_ptr +#define EVP_PKEY_decrypt_init EVP_PKEY_decrypt_init_ptr +#define EVP_PKEY_decrypt EVP_PKEY_decrypt_ptr #define EVP_PKEY_derive_set_peer EVP_PKEY_derive_set_peer_ptr #define EVP_PKEY_derive_init EVP_PKEY_derive_init_ptr #define EVP_PKEY_derive EVP_PKEY_derive_ptr #define EVP_PKEY_free EVP_PKEY_free_ptr +#define EVP_PKEY_get0_RSA EVP_PKEY_get0_RSA_ptr #define EVP_PKEY_get1_DSA EVP_PKEY_get1_DSA_ptr #define EVP_PKEY_get1_EC_KEY EVP_PKEY_get1_EC_KEY_ptr #define EVP_PKEY_get1_RSA EVP_PKEY_get1_RSA_ptr +#define EVP_PKEY_keygen EVP_PKEY_keygen_ptr +#define EVP_PKEY_keygen_init EVP_PKEY_keygen_init_ptr #define EVP_PKEY_new EVP_PKEY_new_ptr #define EVP_PKEY_set1_DSA EVP_PKEY_set1_DSA_ptr #define EVP_PKEY_set1_EC_KEY EVP_PKEY_set1_EC_KEY_ptr #define EVP_PKEY_set1_RSA EVP_PKEY_set1_RSA_ptr +#define EVP_PKEY_sign_init EVP_PKEY_sign_init_ptr +#define EVP_PKEY_sign EVP_PKEY_sign_ptr +#define EVP_PKEY_size EVP_PKEY_size_ptr #define EVP_PKEY_up_ref EVP_PKEY_up_ref_ptr #define EVP_rc2_cbc EVP_rc2_cbc_ptr #define EVP_rc2_ecb EVP_rc2_ecb_ptr @@ -864,16 +910,14 @@ FOR_ALL_OPENSSL_FUNCTIONS #define RSA_get_method RSA_get_method_ptr #define RSA_meth_get_flags RSA_meth_get_flags_ptr #define RSA_new RSA_new_ptr +#define RSA_pkey_ctx_ctrl RSA_pkey_ctx_ctrl_ptr #define RSA_PKCS1_OpenSSL RSA_PKCS1_OpenSSL_ptr -#define RSA_private_decrypt RSA_private_decrypt_ptr -#define RSA_private_encrypt RSA_private_encrypt_ptr #define RSA_public_decrypt RSA_public_decrypt_ptr #define RSA_public_encrypt RSA_public_encrypt_ptr #define RSA_set0_crt_params RSA_set0_crt_params_ptr #define RSA_set0_factors RSA_set0_factors_ptr #define RSA_set0_key RSA_set0_key_ptr #define RSA_set_method RSA_set_method_ptr -#define RSA_sign RSA_sign_ptr #define RSA_size RSA_size_ptr #define RSA_up_ref RSA_up_ref_ptr #define RSA_verify RSA_verify_ptr @@ -1084,6 +1128,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #define RSA_set0_crt_params local_RSA_set0_crt_params #define RSA_set0_factors local_RSA_set0_factors #define RSA_set0_key local_RSA_set0_key +#define RSA_pkey_ctx_ctrl local_RSA_pkey_ctx_ctrl #define SSL_CTX_set_security_level local_SSL_CTX_set_security_level #define SSL_is_init_finished local_SSL_is_init_finished #define X509_CRL_get0_nextUpdate local_X509_CRL_get0_nextUpdate diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c index 2be8588ef6417..83b18f7621f12 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.c @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#include #include "pal_evp_pkey.h" EVP_PKEY* CryptoNative_EvpPkeyCreate() @@ -16,6 +17,12 @@ void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey) } } +int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey) +{ + assert(pkey != NULL); + return EVP_PKEY_size(pkey); +} + int32_t CryptoNative_UpRefEvpPkey(EVP_PKEY* pkey) { if (!pkey) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h index 58169a557be90..98139a8c417d7 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey.h @@ -23,6 +23,11 @@ Always succeeds. */ PALEXPORT void CryptoNative_EvpPkeyDestroy(EVP_PKEY* pkey); +/* +Returns the maximum size, in bytes, of an operation with the provided key. +*/ +PALEXPORT int32_t CryptoNative_EvpPKeySize(EVP_PKEY* pkey); + /* Used by System.Security.Cryptography.X509Certificates' OpenSslX509CertificateReader when duplicating a private key context as part of duplicating the Pal object. diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c index ab02a5c0d6433..b874bf4be0671 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c @@ -2,6 +2,188 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_evp_pkey_rsa.h" +#include "pal_utilities.h" +#include + +static int HasNoPrivateKey(const RSA* rsa); + +EVP_PKEY* CryptoNative_RsaGenerateKey(int keySize) +{ + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + + if (ctx == NULL) + { + return NULL; + } + + EVP_PKEY* pkey = NULL; + EVP_PKEY* ret = NULL; + + if (EVP_PKEY_keygen_init(ctx) == 1 && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, keySize) == 1 && + EVP_PKEY_keygen(ctx, &pkey) == 1) + { + ret = pkey; + pkey = NULL; + } + + if (pkey != NULL) + { + EVP_PKEY_free(pkey); + } + + EVP_PKEY_CTX_free(ctx); + return ret; +} + +int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey, + const uint8_t* source, + int32_t sourceLen, + RsaPaddingMode padding, + const EVP_MD* digest, + uint8_t* destination, + int32_t destinationLen) +{ + assert(pkey != NULL); + assert(source != NULL); + assert(destination != NULL); + assert(padding >= RsaPaddingPkcs1 && padding <= RsaPaddingOaepOrPss); + assert(digest != NULL || padding == RsaPaddingPkcs1); + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL); + + int ret = -1; + + if (ctx == NULL || EVP_PKEY_decrypt_init(ctx) <= 0) + { + goto done; + } + + if (padding == RsaPaddingPkcs1) + { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) + { + goto done; + } + } + else + { + assert(padding == RsaPaddingOaepOrPss); + + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) + { + goto done; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-qual" + if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, digest) <= 0) +#pragma clang diagnostic pop + { + goto done; + } + } + + // This check may no longer be needed on OpenSSL 3.0 + { + const RSA* rsa = EVP_PKEY_get0_RSA(pkey); + + if (rsa == NULL || HasNoPrivateKey(rsa)) + { + ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__); + goto done; + } + } + + size_t written = Int32ToSizeT(destinationLen); + + if (EVP_PKEY_decrypt(ctx, destination, &written, source, Int32ToSizeT(sourceLen)) > 0) + { + ret = SizeTToInt32(written); + } + +done: + if (ctx != NULL) + { + EVP_PKEY_CTX_free(ctx); + } + + return ret; +} + +int32_t CryptoNative_RsaSignHash(EVP_PKEY* pkey, + RsaPaddingMode padding, + const EVP_MD* digest, + const uint8_t* hash, + int32_t hashLen, + uint8_t* destination, + int32_t destinationLen) +{ + assert(pkey != NULL); + assert(destination != NULL); + assert(padding >= RsaPaddingPkcs1 && padding <= RsaPaddingOaepOrPss); + assert(digest != NULL || padding == RsaPaddingPkcs1); + + EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL); + + int ret = -1; + + if (ctx == NULL || EVP_PKEY_sign_init(ctx) <= 0) + { + goto done; + } + + if (padding == RsaPaddingPkcs1) + { + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) + { + goto done; + } + } + else + { + assert(padding == RsaPaddingOaepOrPss); + + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0 || + EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_DIGEST) <= 0) + { + goto done; + } + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-qual" + if (EVP_PKEY_CTX_set_signature_md(ctx, digest) <= 0) +#pragma clang diagnostic pop + { + goto done; + } + + // This check may no longer be needed on OpenSSL 3.0 + { + const RSA* rsa = EVP_PKEY_get0_RSA(pkey); + + if (rsa == NULL || HasNoPrivateKey(rsa)) + { + ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__); + goto done; + } + } + + size_t written = Int32ToSizeT(destinationLen); + + if (EVP_PKEY_sign(ctx, destination, &written, hash, Int32ToSizeT(hashLen)) > 0) + { + ret = SizeTToInt32(written); + } + +done: + if (ctx != NULL) + { + EVP_PKEY_CTX_free(ctx); + } + + return ret; +} RSA* CryptoNative_EvpPkeyGetRsa(EVP_PKEY* pkey) { @@ -12,3 +194,57 @@ int32_t CryptoNative_EvpPkeySetRsa(EVP_PKEY* pkey, RSA* rsa) { return EVP_PKEY_set1_RSA(pkey, rsa); } + +static int HasNoPrivateKey(const RSA* rsa) +{ + if (rsa == NULL) + return 1; + + // Shared pointer, don't free. + const RSA_METHOD* meth = RSA_get_method(rsa); + + // The method has descibed itself as having the private key external to the structure. + // That doesn't mean it's actually present, but we can't tell. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-qual" + if (RSA_meth_get_flags((RSA_METHOD*)meth) & RSA_FLAG_EXT_PKEY) +#pragma clang diagnostic pop + { + return 0; + } + + // In the event that there's a middle-ground where we report failure when success is expected, + // one could do something like check if the RSA_METHOD intercepts all private key operations: + // + // * meth->rsa_priv_enc + // * meth->rsa_priv_dec + // * meth->rsa_sign (in 1.0.x this is only respected if the RSA_FLAG_SIGN_VER flag is asserted) + // + // But, for now, leave it at the EXT_PKEY flag test. + + // The module is documented as accepting either d or the full set of CRT parameters (p, q, dp, dq, qInv) + // So if we see d, we're good. Otherwise, if any of the rest are missing, we're public-only. + const BIGNUM* d; + RSA_get0_key(rsa, NULL, NULL, &d); + + if (d != NULL) + { + return 0; + } + + const BIGNUM* p; + const BIGNUM* q; + const BIGNUM* dmp1; + const BIGNUM* dmq1; + const BIGNUM* iqmp; + + RSA_get0_factors(rsa, &p, &q); + RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); + + if (p == NULL || q == NULL || dmp1 == NULL || dmq1 == NULL || iqmp == NULL) + { + return 1; + } + + return 0; +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h index 3609e677c6de3..332696b6873ac 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h @@ -1,9 +1,51 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include "pal_types.h" -#include "pal_compiler.h" #include "opensslshim.h" +#include "pal_compiler.h" +#include "pal_types.h" + +/* +Padding options for RSA. +Matches RSAEncryptionPaddingMode / RSASignaturePaddingMode. +*/ +typedef enum +{ + RsaPaddingPkcs1, + RsaPaddingOaepOrPss, +} RsaPaddingMode; + +/* +Creates an RSA key of the requested size. +*/ +PALEXPORT EVP_PKEY* CryptoNative_RsaGenerateKey(int32_t keySize); + +/* +Decrypt source into destination using the specified RSA key (wrapped in an EVP_PKEY) and padding/digest options. + +Returns the number of bytes written to destination, -1 on error. +*/ +PALEXPORT int32_t CryptoNative_RsaDecrypt(EVP_PKEY* pkey, + const uint8_t* source, + int32_t sourceLen, + RsaPaddingMode padding, + const EVP_MD* digest, + uint8_t* destination, + int32_t destinationLen); + +/* +Complete the RSA signature generation for the specified hash using the provided RSA key +(wrapped in an EVP_PKEY) and padding/digest options. + +Returns the number of bytes written to destination, -1 on error. +*/ +PALEXPORT int32_t CryptoNative_RsaSignHash(EVP_PKEY* pkey, + RsaPaddingMode padding, + const EVP_MD* digest, + const uint8_t* hash, + int32_t hashLen, + uint8_t* destination, + int32_t destinationLen); /* Shims the EVP_PKEY_get1_RSA method. diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c index 877eac82ca09e..1f115a5f2533f 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.c @@ -49,60 +49,6 @@ static int GetOpenSslPadding(RsaPadding padding) return RSA_NO_PADDING; } -static int HasNoPrivateKey(RSA* rsa) -{ - if (rsa == NULL) - return 1; - - // Shared pointer, don't free. - const RSA_METHOD* meth = RSA_get_method(rsa); - - // The method has descibed itself as having the private key external to the structure. - // That doesn't mean it's actually present, but we can't tell. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-qual" - if (RSA_meth_get_flags((RSA_METHOD*)meth) & RSA_FLAG_EXT_PKEY) -#pragma clang diagnostic pop - { - return 0; - } - - // In the event that there's a middle-ground where we report failure when success is expected, - // one could do something like check if the RSA_METHOD intercepts all private key operations: - // - // * meth->rsa_priv_enc - // * meth->rsa_priv_dec - // * meth->rsa_sign (in 1.0.x this is only respected if the RSA_FLAG_SIGN_VER flag is asserted) - // - // But, for now, leave it at the EXT_PKEY flag test. - - // The module is documented as accepting either d or the full set of CRT parameters (p, q, dp, dq, qInv) - // So if we see d, we're good. Otherwise, if any of the rest are missing, we're public-only. - const BIGNUM* d; - RSA_get0_key(rsa, NULL, NULL, &d); - - if (d != NULL) - { - return 0; - } - - const BIGNUM* p; - const BIGNUM* q; - const BIGNUM* dmp1; - const BIGNUM* dmq1; - const BIGNUM* iqmp; - - RSA_get0_factors(rsa, &p, &q); - RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); - - if (p == NULL || q == NULL || dmp1 == NULL || dmq1 == NULL || iqmp == NULL) - { - return 1; - } - - return 0; -} - int32_t CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding) { @@ -110,30 +56,6 @@ CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RS return RSA_public_encrypt(flen, from, to, rsa, openSslPadding); } -int32_t -CryptoNative_RsaPrivateDecrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding) -{ - if (HasNoPrivateKey(rsa)) - { - ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_DECRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__); - return -1; - } - - int openSslPadding = GetOpenSslPadding(padding); - return RSA_private_decrypt(flen, from, to, rsa, openSslPadding); -} - -int32_t CryptoNative_RsaSignPrimitive(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa) -{ - if (HasNoPrivateKey(rsa)) - { - ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_NULL_PRIVATE_ENCRYPT, RSA_R_VALUE_MISSING, __FILE__, __LINE__); - return -1; - } - - return RSA_private_encrypt(flen, from, to, rsa, RSA_NO_PADDING); -} - int32_t CryptoNative_RsaVerificationPrimitive(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa) { return RSA_public_decrypt(flen, from, to, rsa, RSA_NO_PADDING); @@ -144,46 +66,6 @@ int32_t CryptoNative_RsaSize(RSA* rsa) return RSA_size(rsa); } -int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, BIGNUM* e) -{ - return RSA_generate_key_ex(rsa, bits, e, NULL); -} - -int32_t -CryptoNative_RsaSign(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa) -{ - if (siglen == NULL) - { - assert(false); - return 0; - } - - *siglen = 0; - - if (HasNoPrivateKey(rsa)) - { - ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_SIGN, RSA_R_VALUE_MISSING, __FILE__, __LINE__); - return 0; - } - - // Shared pointer to the metadata about the message digest algorithm - const EVP_MD* digest = EVP_get_digestbynid(type); - - // If the digest itself isn't known then RSA_R_UNKNOWN_ALGORITHM_TYPE will get reported, but - // we have to check that the digest size matches what we expect. - if (digest != NULL && mlen != EVP_MD_size(digest)) - { - ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_SIGN, RSA_R_INVALID_MESSAGE_LENGTH, __FILE__, __LINE__); - return 0; - } - - unsigned int unsignedSigLen = 0; - int32_t ret = RSA_sign(type, m, Int32ToUint32(mlen), sigret, &unsignedSigLen, rsa); - assert(unsignedSigLen <= INT32_MAX); - *siglen = (int32_t)unsignedSigLen; - return ret; -} - int32_t CryptoNative_RsaVerify(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigbuf, int32_t siglen, RSA* rsa) { diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h index 4db6722feb9a9..c58a059833ea6 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_rsa.h @@ -54,14 +54,6 @@ Returns the size of the signature, or -1 on error. PALEXPORT int32_t CryptoNative_RsaPublicEncrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding); -/* -Shims the RSA_private_decrypt method. - -Returns the size of the signature, or -1 on error. -*/ -PALEXPORT int32_t -CryptoNative_RsaPrivateDecrypt(int32_t flen, const uint8_t* from, uint8_t* to, RSA* rsa, RsaPadding padding); - /* Shims RSA_private_encrypt with a fixed value of RSA_NO_PADDING. @@ -85,13 +77,6 @@ Returns the RSA modulus size in bytes. */ PALEXPORT int32_t CryptoNative_RsaSize(RSA* rsa); -/* -Shims the RSA_generate_key_ex method. - -Returns 1 upon success, otherwise 0. -*/ -PALEXPORT int32_t CryptoNative_RsaGenerateKeyEx(RSA* rsa, int32_t bits, BIGNUM* e); - /* Shims the RSA_sign method. diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c index c0dbf05a4154b..b59560383f8fa 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_ssl.c @@ -644,16 +644,22 @@ int32_t CryptoNative_SslGetCurrentCipherId(SSL* ssl, int32_t* cipherId) // This function generates key pair and creates simple certificate. static int MakeSelfSignedCertificate(X509 * cert, EVP_PKEY* evp) { - RSA* rsa = CryptoNative_RsaCreate(); + RSA* rsa = NULL; ASN1_TIME* time = ASN1_TIME_new(); - BIGNUM* bn = BN_new(); - BN_set_word(bn, RSA_F4); X509_NAME * asnName; unsigned char * name = (unsigned char*)"localhost"; int ret = 0; - if (rsa != NULL && CryptoNative_RsaGenerateKeyEx(rsa, 2048, bn) == 1) + EVP_PKEY* pkey = CryptoNative_RsaGenerateKey(2048); + + if (pkey != NULL) + { + rsa = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); + } + + if (rsa != NULL) { if (CryptoNative_EvpPkeySetRsa(evp, rsa) == 1) { @@ -675,11 +681,6 @@ static int MakeSelfSignedCertificate(X509 * cert, EVP_PKEY* evp) ret = X509_sign(cert, evp, EVP_sha256()); } - if (bn != NULL) - { - BN_free(bn); - } - if (rsa != NULL) { RSA_free(rsa); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.c index 2e525482c8382..7bedeb39733d2 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.c @@ -32,7 +32,6 @@ c_static_assert(PAL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY == X509_V_ERR_U c_static_assert(PAL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); c_static_assert(PAL_X509_V_ERR_CERT_CHAIN_TOO_LONG == X509_V_ERR_CERT_CHAIN_TOO_LONG); c_static_assert(PAL_X509_V_ERR_CERT_REVOKED == X509_V_ERR_CERT_REVOKED); -c_static_assert(PAL_X509_V_ERR_INVALID_CA == X509_V_ERR_INVALID_CA); c_static_assert(PAL_X509_V_ERR_PATH_LENGTH_EXCEEDED == X509_V_ERR_PATH_LENGTH_EXCEEDED); c_static_assert(PAL_X509_V_ERR_INVALID_PURPOSE == X509_V_ERR_INVALID_PURPOSE); c_static_assert(PAL_X509_V_ERR_CERT_UNTRUSTED == X509_V_ERR_CERT_UNTRUSTED); @@ -47,6 +46,26 @@ c_static_assert(PAL_X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE == X509_V_ERR_KEYUS c_static_assert(PAL_X509_V_ERR_INVALID_EXTENSION == X509_V_ERR_INVALID_EXTENSION); c_static_assert(PAL_X509_V_ERR_INVALID_POLICY_EXTENSION == X509_V_ERR_INVALID_POLICY_EXTENSION); c_static_assert(PAL_X509_V_ERR_NO_EXPLICIT_POLICY == X509_V_ERR_NO_EXPLICIT_POLICY); +c_static_assert(PAL_X509_V_ERR_DIFFERENT_CRL_SCOPE == X509_V_ERR_DIFFERENT_CRL_SCOPE); +c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE == X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE); +c_static_assert(PAL_X509_V_ERR_UNNESTED_RESOURCE == X509_V_ERR_UNNESTED_RESOURCE); +c_static_assert(PAL_X509_V_ERR_PERMITTED_VIOLATION == X509_V_ERR_PERMITTED_VIOLATION); +c_static_assert(PAL_X509_V_ERR_EXCLUDED_VIOLATION == X509_V_ERR_EXCLUDED_VIOLATION); +c_static_assert(PAL_X509_V_ERR_SUBTREE_MINMAX == X509_V_ERR_SUBTREE_MINMAX); +c_static_assert(PAL_X509_V_ERR_APPLICATION_VERIFICATION == X509_V_ERR_APPLICATION_VERIFICATION); +c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE == X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE); +c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX == X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX); +c_static_assert(PAL_X509_V_ERR_UNSUPPORTED_NAME_SYNTAX == X509_V_ERR_UNSUPPORTED_NAME_SYNTAX); +c_static_assert(PAL_X509_V_ERR_CRL_PATH_VALIDATION_ERROR == X509_V_ERR_CRL_PATH_VALIDATION_ERROR); +c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_VERSION == X509_V_ERR_SUITE_B_INVALID_VERSION); +c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_ALGORITHM == X509_V_ERR_SUITE_B_INVALID_ALGORITHM); +c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_CURVE == X509_V_ERR_SUITE_B_INVALID_CURVE); +c_static_assert(PAL_X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM == X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM); +c_static_assert(PAL_X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED == X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED); +c_static_assert(PAL_X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 == X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256); +c_static_assert(PAL_X509_V_ERR_HOSTNAME_MISMATCH == X509_V_ERR_HOSTNAME_MISMATCH); +c_static_assert(PAL_X509_V_ERR_EMAIL_MISMATCH == X509_V_ERR_EMAIL_MISMATCH); +c_static_assert(PAL_X509_V_ERR_IP_ADDRESS_MISMATCH == X509_V_ERR_IP_ADDRESS_MISMATCH); EVP_PKEY* CryptoNative_GetX509EvpPublicKey(X509* x509) { @@ -1115,7 +1134,10 @@ CryptoNative_X509ChainVerifyOcsp(X509_STORE_CTX* storeCtx, OCSP_REQUEST* req, OC if (bio != NULL) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-qual" if (i2d_OCSP_RESPONSE_bio(bio, resp)) +#pragma clang diagnostic pop { clearErr = 0; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.h index 0601afbd3c765..d28f0cc5c4684 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native/pal_x509.h @@ -17,7 +17,10 @@ typedef enum { /* The error codes used when verifying X509 certificate chains. -These values should be kept in sync with Interop.Crypto.X509VerifyStatusCode. +These values should be kept in sync with Interop.Crypto.X509VerifyStatusCodeUniversal. + +Codes specific to specific versions of OpenSSL can also be returned, +but are not represented in this enum due to their non-constant nature. */ typedef enum { PAL_X509_V_OK = 0, @@ -42,7 +45,9 @@ typedef enum { PAL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = 21, PAL_X509_V_ERR_CERT_CHAIN_TOO_LONG = 22, PAL_X509_V_ERR_CERT_REVOKED = 23, - PAL_X509_V_ERR_INVALID_CA = 24, + + // Code 24 varies + PAL_X509_V_ERR_PATH_LENGTH_EXCEEDED = 25, PAL_X509_V_ERR_INVALID_PURPOSE = 26, PAL_X509_V_ERR_CERT_UNTRUSTED = 27, @@ -57,6 +62,26 @@ typedef enum { PAL_X509_V_ERR_INVALID_EXTENSION = 41, PAL_X509_V_ERR_INVALID_POLICY_EXTENSION = 42, PAL_X509_V_ERR_NO_EXPLICIT_POLICY = 43, + PAL_X509_V_ERR_DIFFERENT_CRL_SCOPE = 44, + PAL_X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE = 45, + PAL_X509_V_ERR_UNNESTED_RESOURCE = 46, + PAL_X509_V_ERR_PERMITTED_VIOLATION = 47, + PAL_X509_V_ERR_EXCLUDED_VIOLATION = 48, + PAL_X509_V_ERR_SUBTREE_MINMAX = 49, + PAL_X509_V_ERR_APPLICATION_VERIFICATION = 50, + PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE = 51, + PAL_X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX = 52, + PAL_X509_V_ERR_UNSUPPORTED_NAME_SYNTAX = 53, + PAL_X509_V_ERR_CRL_PATH_VALIDATION_ERROR = 54, + PAL_X509_V_ERR_SUITE_B_INVALID_VERSION = 56, + PAL_X509_V_ERR_SUITE_B_INVALID_ALGORITHM = 57, + PAL_X509_V_ERR_SUITE_B_INVALID_CURVE = 58, + PAL_X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM = 59, + PAL_X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED = 60, + PAL_X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 = 61, + PAL_X509_V_ERR_HOSTNAME_MISMATCH = 62, + PAL_X509_V_ERR_EMAIL_MISMATCH = 63, + PAL_X509_V_ERR_IP_ADDRESS_MISMATCH = 64, } X509VerifyStatusCode; typedef int32_t (*X509StoreVerifyCallback)(int32_t, X509_STORE_CTX*); diff --git a/src/libraries/Native/Windows/CMakeLists.txt b/src/libraries/Native/Windows/CMakeLists.txt index 3f9924adb0a9d..b9b6c823d0b49 100644 --- a/src/libraries/Native/Windows/CMakeLists.txt +++ b/src/libraries/Native/Windows/CMakeLists.txt @@ -9,15 +9,11 @@ SET (CMAKE_C_FLAGS_RELWITHDEBINFO_INIT "/O2 /Zi") SET (CMAKE_ASM_MASM_FLAGS "${CMAKE_ASM_MASM_FLAGS} /ZH:SHA_256") # Configuration of our libray specs and our directories -SET (CMAKE_INSTALL_PREFIX $ENV{__CMakeBinDir}) SET (CMAKE_INCLUDE_CURRENT_DIR ON) SET (CMAKE_SHARED_LIBRARY_PREFIX "") set(__SharedLinkArgs) set(__LinkArgs) -if ($ENV{__LinkArgs}) - SET (__LinkArgs $ENV{__LinkArgs}) -endif() # Force an out of source build if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") @@ -49,9 +45,8 @@ string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") add_compile_options(/EHa) # enable C++ EH (w/ SEH exceptions) add_compile_options(/Zp8) # pack structs on 8-byte boundary add_compile_options(/Gy) # separate functions for linker -add_compile_options(/Zc:wchar_t-) # C++ language conformance: wchar_t is NOT the native type, but a typedef add_compile_options(/Zc:forScope) # C++ language conformance: enforce Standard C++ for scoping rules -string(REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +string(REPLACE "/GR " " " CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") add_compile_options(/GR-) # disable C++ RTTI add_compile_options(/FC) # use full pathnames in diagnostics add_compile_options(/MP) # Build with Multiple Processes (number of processes equal to the number of processors) @@ -62,7 +57,7 @@ add_compile_options(/Zl) # enable debugging information add_compile_options(/wd4960 /wd4961 /wd4603 /wd4627 /wd4838 /wd4456 /wd4457 /wd4458 /wd4459 /wd4091 /we4640) add_compile_options(/ZH:SHA_256) # use SHA256 for generating hashes of compiler processed source files. -if ($ENV{__BuildArch} STREQUAL "x86") +if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86") add_compile_options(/Gz) endif () @@ -80,7 +75,6 @@ list(APPEND __SharedLinkArgs /GUARD:CF) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") add_compile_options($<$:/GL>) -add_compile_options($<$:/O1>) list(APPEND __LinkLibraries $<$:libcmtd.lib>) list(APPEND __LinkLibraries $<$:libcmt.lib>) @@ -115,7 +109,7 @@ endif() # Debug build specific flags list(INSERT __SharedLinkArgs 0 $<$,$>:/NOVCFEATURE>) -if ($ENV{__BuildArch} STREQUAL "x86_64" OR $ENV{__BuildArch} STREQUAL "amd64") +if (${CLR_CMAKE_HOST_ARCH} STREQUAL "x86_64" OR ${CLR_CMAKE_HOST_ARCH} STREQUAL "amd64") add_definitions(-DTARGET_64BIT=1) endif () diff --git a/src/libraries/Native/Windows/System.IO.Compression.Native/CMakeLists.txt b/src/libraries/Native/Windows/System.IO.Compression.Native/CMakeLists.txt index e161657b3960d..0ca238d56fd92 100644 --- a/src/libraries/Native/Windows/System.IO.Compression.Native/CMakeLists.txt +++ b/src/libraries/Native/Windows/System.IO.Compression.Native/CMakeLists.txt @@ -10,7 +10,7 @@ if (GEN_SHARED_LIB) include (GenerateExportHeader) endif() -if($ENV{__BuildArch} STREQUAL x86 OR $ENV{__BuildArch} STREQUAL x64) +if(${CLR_CMAKE_HOST_ARCH} STREQUAL x86 OR ${CLR_CMAKE_HOST_ARCH} STREQUAL x64) set(NATIVECOMPRESSION_SOURCES zlib-intel/adler32.c zlib-intel/compress.c @@ -122,4 +122,4 @@ if (GEN_SHARED_LIB) install (FILES $ DESTINATION .) endif() -install (TARGETS System.IO.Compression.Native-Static DESTINATION ${STATIC_LIB_DESTINATION}) +install (TARGETS System.IO.Compression.Native-Static DESTINATION ${STATIC_LIB_DESTINATION} COMPONENT libs) diff --git a/src/libraries/Native/Windows/System.IO.Compression.Native/zlib-intel/inflate.c b/src/libraries/Native/Windows/System.IO.Compression.Native/zlib-intel/inflate.c index 165e465a08471..4a2a217a3e62a 100644 --- a/src/libraries/Native/Windows/System.IO.Compression.Native/zlib-intel/inflate.c +++ b/src/libraries/Native/Windows/System.IO.Compression.Native/zlib-intel/inflate.c @@ -1540,7 +1540,7 @@ z_streamp source; struct inflate_state FAR *state; struct inflate_state FAR *copy; unsigned char FAR *window; - unsigned wsize; + unsigned wsize = 0; /* check input */ if (inflateStateCheck(source) || dest == Z_NULL) diff --git a/src/libraries/Native/build-native.cmd b/src/libraries/Native/build-native.cmd index 6b6b74d5f578b..b6c19847989a6 100644 --- a/src/libraries/Native/build-native.cmd +++ b/src/libraries/Native/build-native.cmd @@ -11,26 +11,24 @@ set __CMakeBinDir="" set __IntermediatesDir="" set __BuildArch=x64 set __BuildTarget="build" -set __VCBuildArch=x86_amd64 set __TargetOS=windows set CMAKE_BUILD_TYPE=Debug -set "__LinkArgs= " set "__LinkLibraries= " -set __Ninja=0 +set __Ninja=1 :Arg_Loop :: Since the native build requires some configuration information before msbuild is called, we have to do some manual args parsing -if [%1] == [] goto :ToolsVersion +if [%1] == [] goto :InitVSEnv if /i [%1] == [Release] ( set CMAKE_BUILD_TYPE=Release&&shift&goto Arg_Loop) if /i [%1] == [Debug] ( set CMAKE_BUILD_TYPE=Debug&&shift&goto Arg_Loop) -if /i [%1] == [AnyCPU] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) -if /i [%1] == [x86] ( set __BuildArch=x86&&set __VCBuildArch=x86&&shift&goto Arg_Loop) -if /i [%1] == [arm] ( set __BuildArch=arm&&set __VCBuildArch=x86_arm&&shift&goto Arg_Loop) -if /i [%1] == [x64] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) -if /i [%1] == [amd64] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) -if /i [%1] == [arm64] ( set __BuildArch=arm64&&set __VCBuildArch=x86_arm64&&shift&goto Arg_Loop) -if /i [%1] == [wasm] ( set __BuildArch=wasm&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) +if /i [%1] == [AnyCPU] ( set __BuildArch=x64&&shift&goto Arg_Loop) +if /i [%1] == [x86] ( set __BuildArch=x86&&shift&goto Arg_Loop) +if /i [%1] == [arm] ( set __BuildArch=arm&&shift&goto Arg_Loop) +if /i [%1] == [x64] ( set __BuildArch=x64&&shift&goto Arg_Loop) +if /i [%1] == [amd64] ( set __BuildArch=x64&&shift&goto Arg_Loop) +if /i [%1] == [arm64] ( set __BuildArch=arm64&&shift&goto Arg_Loop) +if /i [%1] == [wasm] ( set __BuildArch=wasm&&shift&goto Arg_Loop) if /i [%1] == [outconfig] ( set __outConfig=%2&&shift&&shift&goto Arg_Loop) @@ -38,66 +36,27 @@ if /i [%1] == [Browser] ( set __TargetOS=Browser&&shift&goto Arg_Loop) if /i [%1] == [rebuild] ( set __BuildTarget=rebuild&&shift&goto Arg_Loop) -if /i [%1] == [ninja] ( set __Ninja=1&&shift&goto Arg_Loop) +if /i [%1] == [msbuild] ( set __Ninja=0&&shift&goto Arg_Loop) shift goto :Arg_Loop -:ToolsVersion -:: Default to highest Visual Studio version available -:: -:: For VS2017 and later, multiple instances can be installed on the same box SxS and VSxxxCOMNTOOLS is only set if the user -:: has launched the VS2017 or VS2019 Developer Command Prompt. -:: -:: Following this logic, we will default to the VS2017 or VS2019 toolset if VS150COMNTOOLS or VS160COMMONTOOLS tools is -:: set, as this indicates the user is running from the VS2017 or VS2019 Developer Command Prompt and -:: is already configured to use that toolset. Otherwise, we will fail the script if no supported VS instance can be found. - -if defined VisualStudioVersion goto :RunVCVars - -set _VSWHERE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -if exist %_VSWHERE% ( - for /f "usebackq tokens=*" %%i in (`%_VSWHERE% -latest -prerelease -property installationPath`) do set _VSCOMNTOOLS=%%i\Common7\Tools -) -if not exist "%_VSCOMNTOOLS%" goto :MissingVersion - -call "%_VSCOMNTOOLS%\VsDevCmd.bat" -no_logo - -:RunVCVars -if "%VisualStudioVersion%"=="16.0" ( - goto :VS2019 -) else if "%VisualStudioVersion%"=="15.0" ( - goto :VS2017 -) - -:MissingVersion -:: Can't find appropriate VS install -echo Error: Visual Studio 2019 required -echo Please see https://github.com/dotnet/runtime/tree/main/docs/workflow/building/libraries for build instructions. -exit /b 1 +:InitVSEnv +call "%__engNativeDir%\init-vs-env.cmd" %__BuildArch% +if NOT [%errorlevel%] == [0] goto :Failure -:VS2019 -:: Setup vars for VS2019 -set __VSVersion=vs2019 -set __PlatformToolset=v142 -:: Set the environment for the native build -call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -goto :SetupDirs - -:VS2017 -:: Setup vars for VS2017 -set __VSVersion=vs2017 -set __PlatformToolset=v141 -:: Set the environment for the native build -call "%VS150COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -goto :SetupDirs - -:SetupDirs :: Setup to cmake the native components echo Commencing build of native components echo. -if /i "%__BuildArch%" == "wasm" set __sourceDir=%~dp0\Unix +:: cmake requires forward slashes in paths +set __cmakeRepoRoot=%__repoRoot:\=/% +set __ExtraCmakeParams="-DCMAKE_REPO_ROOT=%__cmakeRepoRoot%" + +if /i "%__BuildArch%" == "wasm" ( + set __sourceDir=%~dp0\Unix + set __ExtraCmakeParams=%__ExtraCmakeParams% "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" +) if [%__outConfig%] == [] set __outConfig=%__TargetOS%-%__BuildArch%-%CMAKE_BUILD_TYPE% @@ -107,6 +66,9 @@ if %__CMakeBinDir% == "" ( if %__IntermediatesDir% == "" ( set "__IntermediatesDir=%__artifactsDir%\obj\native\%__outConfig%" ) +if %__Ninja% == 0 ( + set "__IntermediatesDir=%__IntermediatesDir%\ide" +) set "__CMakeBinDir=%__CMakeBinDir:\=/%" set "__IntermediatesDir=%__IntermediatesDir:\=/%" @@ -122,33 +84,14 @@ set MSBUILD_EMPTY_PROJECT_CONTENT= ^ echo %MSBUILD_EMPTY_PROJECT_CONTENT% > "%__artifactsDir%\obj\native\Directory.Build.props" echo %MSBUILD_EMPTY_PROJECT_CONTENT% > "%__artifactsDir%\obj\native\Directory.Build.targets" -if exist "%VSINSTALLDIR%DIA SDK" goto FindCMake -echo Error: DIA SDK is missing at "%VSINSTALLDIR%DIA SDK". ^ -Did you install all the requirements for building on Windows, including the "Desktop Development with C++" workload? ^ -Please see https://github.com/dotnet/runtime/blob/main/docs/workflow/requirements/windows-requirements.md ^ -Another possibility is that you have a parallel installation of Visual Studio and the DIA SDK is there. In this case it ^ -may help to copy its "DIA SDK" folder into "%VSINSTALLDIR%" manually, then try again. -exit /b 1 - -:FindCMake -if defined CMakePath goto GenVSSolution -:: Find CMake - -:: Eval the output from set-cmake-path.ps1 -for /f "delims=" %%a in ('powershell -NoProfile -ExecutionPolicy ByPass "& ""%__repoRoot%\eng\native\set-cmake-path.ps1"""') do %%a - -:GenVSSolution :: generate version file powershell -NoProfile -ExecutionPolicy ByPass -NoLogo -File "%__repoRoot%\eng\common\msbuild.ps1" /clp:nosummary %__ArcadeScriptArgs%^ "%__repoRoot%\eng\empty.csproj" /p:NativeVersionFile="%__artifactsDir%\obj\_version.h"^ /t:GenerateNativeVersionFile /restore :: Regenerate the VS solution -:: cmake requires forward slashes in paths -set __cmakeRepoRoot=%__repoRoot:\=/% - pushd "%__IntermediatesDir%" -call "%__repoRoot%\eng\native\gen-buildsys.cmd" "%__sourceDir%" "%__IntermediatesDir%" %__VSVersion% %__BuildArch% "-DCMAKE_REPO_ROOT=%__cmakeRepoRoot%" +call "%__repoRoot%\eng\native\gen-buildsys.cmd" "%__sourceDir%" "%__IntermediatesDir%" %__VSVersion% %__BuildArch% %__ExtraCmakeParams% if NOT [%errorlevel%] == [0] goto :Failure popd diff --git a/src/libraries/Native/build-native.proj b/src/libraries/Native/build-native.proj index 900d7f0ad7262..edba5f1fbddb7 100644 --- a/src/libraries/Native/build-native.proj +++ b/src/libraries/Native/build-native.proj @@ -6,7 +6,6 @@ $(BuildTargetFramework) $(NetCoreAppCurrent) <_BuildNativeArgs>$(TargetArchitecture) $(Configuration) outconfig $(TargetFramework)-$(TargetOS)-$(Configuration)-$(TargetArchitecture) -os $(TargetOS) - <_BuildNativeArgs Condition="'$(Ninja)' == 'true'">$(_BuildNativeArgs) ninja @@ -18,6 +17,7 @@ BeforeTargets="Build" Condition="!$([MSBuild]::IsOsPlatform(Windows))"> + <_BuildNativeArgs Condition="'$(Ninja)' == 'true'">$(_BuildNativeArgs) ninja + + <_BuildNativeArgs Condition="'$(Ninja)' == 'false'">$(_BuildNativeArgs) msbuild + + diff --git a/src/libraries/Native/build-native.sh b/src/libraries/Native/build-native.sh index 0dc7c80d674fa..55d56a7099c35 100755 --- a/src/libraries/Native/build-native.sh +++ b/src/libraries/Native/build-native.sh @@ -56,10 +56,10 @@ if [[ "$__BuildArch" == wasm ]]; then export CLR_CC=$(which emcc) export CLR_CXX=$(which em++) -elif [[ "$__TargetOS" == iOS ]]; then +elif [[ "$__TargetOS" == iOS || "$__TargetOS" == iOSSimulator ]]; then # nothing to do here true -elif [[ "$__TargetOS" == tvOS ]]; then +elif [[ "$__TargetOS" == tvOS || "$__TargetOS" == tvOSSimulator ]]; then # nothing to do here true elif [[ "$__TargetOS" == Android && -z "$ROOTFS_DIR" ]]; then @@ -103,7 +103,7 @@ elif [[ "$__TargetOS" == Android && -z "$ROOTFS_DIR" ]]; then echo "Error: Unknown Android architecture $__BuildArch." exit 1 fi -elif [[ "$__TargetOS" == iOS ]]; then +elif [[ "$__TargetOS" == iOSSimulator ]]; then __CMakeArgs="-DCMAKE_SYSTEM_NAME=iOS $__CMakeArgs" if [[ "$__BuildArch" == x64 ]]; then # set default iOS simulator deployment target (8.0 is the minimum supported by Xcode 11) @@ -114,6 +114,16 @@ elif [[ "$__TargetOS" == iOS ]]; then # keep in sync with src/mono/Directory.Build.props __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"i386\" $__CMakeArgs" elif [[ "$__BuildArch" == arm64 ]]; then + # set default iOS device deployment target + # keep in sync with src/mono/Directory.Build.props + __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphonesimulator -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" + else + echo "Error: Unknown iOSSimulator architecture $__BuildArch." + exit 1 + fi +elif [[ "$__TargetOS" == iOS ]]; then + __CMakeArgs="-DCMAKE_SYSTEM_NAME=iOS $__CMakeArgs" + if [[ "$__BuildArch" == arm64 ]]; then # set default iOS device deployment target # keep in sync with src/mono/Directory.Build.props __CMakeArgs="-DCMAKE_OSX_SYSROOT=iphoneos -DCMAKE_OSX_DEPLOYMENT_TARGET=8.0 -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" @@ -125,14 +135,25 @@ elif [[ "$__TargetOS" == iOS ]]; then echo "Error: Unknown iOS architecture $__BuildArch." exit 1 fi -elif [[ "$__TargetOS" == tvOS ]]; then +elif [[ "$__TargetOS" == tvOSSimulator ]]; then __CMakeArgs="-DCMAKE_SYSTEM_NAME=tvOS $__CMakeArgs" - # set default tvOS device deployment target + # set default tvOS device deployment target # keep in sync with tvOSVersionMin in src/mono/Directory.Build.props __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=9.0 $__CMakeArgs" if [[ "$__BuildArch" == x64 ]]; then __CMakeArgs="-DCMAKE_OSX_SYSROOT=appletvsimulator -DCMAKE_OSX_ARCHITECTURES=\"x86_64\" $__CMakeArgs" elif [[ "$__BuildArch" == arm64 ]]; then + __CMakeArgs="-DCMAKE_OSX_SYSROOT=appletvsimulator -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" + else + echo "Error: Unknown tvOSSimulator architecture $__BuildArch." + exit 1 + fi +elif [[ "$__TargetOS" == tvOS ]]; then + __CMakeArgs="-DCMAKE_SYSTEM_NAME=tvOS $__CMakeArgs" + # set default tvOS device deployment target + # keep in sync with tvOSVersionMin in src/mono/Directory.Build.props + __CMakeArgs="-DCMAKE_OSX_DEPLOYMENT_TARGET=9.0 $__CMakeArgs" + if [[ "$__BuildArch" == arm64 ]]; then __CMakeArgs="-DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=\"arm64\" $__CMakeArgs" else echo "Error: Unknown tvOS architecture $__BuildArch." @@ -157,4 +178,4 @@ setup_dirs check_prereqs # Build the corefx native components. -build_native "$__TargetOS" "$__BuildArch" "$__nativeroot" "$__IntermediatesDir" "$__CMakeArgs" "native libraries component" +build_native "$__TargetOS" "$__BuildArch" "$__nativeroot" "$__IntermediatesDir" "install" "$__CMakeArgs" "native libraries component" diff --git a/src/libraries/OSGroups.json b/src/libraries/OSGroups.json index 6abdb84c5192b..df367e07b744e 100644 --- a/src/libraries/OSGroups.json +++ b/src/libraries/OSGroups.json @@ -24,11 +24,21 @@ "Unix" ] }, + "iOSSimulator": { + "#import": [ + "iOS" + ] + }, "tvOS": { "#import": [ "Unix" ] }, + "tvOSSimulator": { + "#import": [ + "tvOS" + ] + }, "Android": { "#import": [ "Linux" diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/BlockingCollection.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/BlockingCollection.cs index 3640d0d4e829b..dde45b73a07e1 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/BlockingCollection.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/BlockingCollection.cs @@ -221,7 +221,7 @@ private void Initialize(IProducerConsumerCollection collection, int boundedCa Debug.Assert(boundedCapacity > 0 || boundedCapacity == NON_BOUNDED); _collection = collection; - _boundedCapacity = boundedCapacity; ; + _boundedCapacity = boundedCapacity; _isDisposed = false; _consumersCancellationTokenSource = new CancellationTokenSource(); _producersCancellationTokenSource = new CancellationTokenSource(); @@ -1144,7 +1144,7 @@ private static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTim } // Subtract the elapsed time from the current wait time - int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ; + int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; if (currentWaitTimeout <= 0) { return 0; diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs index a1f0fcc617e05..04cb4e244f707 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs @@ -41,7 +41,7 @@ public class ConcurrentStack : IProducerConsumerCollection, IReadOnlyColle /// /// A simple (internal) node type used to store elements of concurrent stacks and queues. /// - private class Node + private sealed class Node { internal readonly T _value; // Value of the node. internal Node? _next; // Next pointer. @@ -606,7 +606,6 @@ private int TryPopCore(int count, out Node? poppedHead) Node? head; Node next; int backoff = 1; - Random? r = null; while (true) { head = _head; @@ -649,11 +648,7 @@ private int TryPopCore(int count, out Node? poppedHead) if (spin.NextSpinWillYield) { - if (r == null) - { - r = new Random(); - } - backoff = r.Next(1, BACKOFF_MAX_YIELDS); + backoff = Random.Shared.Next(1, BACKOFF_MAX_YIELDS); } else { diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/OrderablePartitioner.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/OrderablePartitioner.cs index b4ce771f34c35..77a3356188173 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/OrderablePartitioner.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/OrderablePartitioner.cs @@ -212,7 +212,7 @@ public override IEnumerable GetDynamicPartitions() /// /// Converts an enumerable over key-value pairs to an enumerable over values. /// - private class EnumerableDropIndices : IEnumerable, IDisposable + private sealed class EnumerableDropIndices : IEnumerable, IDisposable { private readonly IEnumerable> _source; public EnumerableDropIndices(IEnumerable> source) @@ -237,7 +237,7 @@ public void Dispose() } } - private class EnumeratorDropIndices : IEnumerator + private sealed class EnumeratorDropIndices : IEnumerator { private readonly IEnumerator> _source; public EnumeratorDropIndices(IEnumerator> source) diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs index 84b664e999111..1433e38cf9568 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/PartitionerStatic.cs @@ -486,7 +486,7 @@ public bool MoveNext() /// of EnumerableOfPartitionsForIEnumerator defined internally /// /// Type of elements in the source data - private class DynamicPartitionerForIEnumerable : OrderablePartitioner + private sealed class DynamicPartitionerForIEnumerable : OrderablePartitioner { private readonly IEnumerable _source; private readonly bool _useSingleChunking; @@ -546,7 +546,7 @@ public override bool SupportsDynamicPartitions /// shared by the partitions it owns, including a boolean "_hasNoElementsLef", a shared lock, and a /// shared count "_activePartitionCount" used to track active partitions when they were created statically /// - private class InternalPartitionEnumerable : IEnumerable>, IDisposable + private sealed class InternalPartitionEnumerable : IEnumerable>, IDisposable { //reader through which we access the source data private readonly IEnumerator _sharedReader; @@ -880,7 +880,7 @@ public void Dispose() /// Inherits from DynamicPartitionEnumerator_Abstract directly /// Provides customized implementation for: GrabNextChunk, HasNoElementsLeft, Current, Dispose /// - private class InternalPartitionEnumerator : DynamicPartitionEnumerator_Abstract> + private sealed class InternalPartitionEnumerator : DynamicPartitionEnumerator_Abstract> { //---- fields ---- //cached local copy of the current chunk @@ -1169,7 +1169,7 @@ public override void Dispose() /// of EnumerableOfPartitionsForIList defined internally /// /// Type of elements in the source data - private class DynamicPartitionerForIList : DynamicPartitionerForIndexRange_Abstract> + private sealed class DynamicPartitionerForIList : DynamicPartitionerForIndexRange_Abstract> { //constructor internal DynamicPartitionerForIList(IList source) @@ -1187,7 +1187,7 @@ protected override IEnumerable> GetOrderableDynamicP /// Inherits from PartitionList_Abstract /// Provides customized implementation for source data of IList /// - private class InternalPartitionEnumerable : IEnumerable> + private sealed class InternalPartitionEnumerable : IEnumerable> { //reader through which we access the source data private readonly IList _sharedReader; @@ -1214,7 +1214,7 @@ IEnumerator IEnumerable.GetEnumerator() /// Inherits from DynamicPartitionEnumeratorForIndexRange_Abstract /// Provides customized implementation of SourceCount property and Current property for IList /// - private class InternalPartitionEnumerator : DynamicPartitionEnumeratorForIndexRange_Abstract> + private sealed class InternalPartitionEnumerator : DynamicPartitionEnumeratorForIndexRange_Abstract> { //constructor internal InternalPartitionEnumerator(IList sharedReader, SharedLong sharedIndex) @@ -1255,7 +1255,7 @@ public override KeyValuePair Current /// of EnumerableOfPartitionsForArray defined internally /// /// Type of elements in the source data - private class DynamicPartitionerForArray : DynamicPartitionerForIndexRange_Abstract + private sealed class DynamicPartitionerForArray : DynamicPartitionerForIndexRange_Abstract { //constructor internal DynamicPartitionerForArray(TSource[] source) @@ -1272,7 +1272,7 @@ protected override IEnumerable> GetOrderableDynamicP /// Inherits from PartitionList_Abstract /// Provides customized implementation for source data of Array /// - private class InternalPartitionEnumerable : IEnumerable> + private sealed class InternalPartitionEnumerable : IEnumerable> { //reader through which we access the source data private readonly TSource[] _sharedReader; @@ -1300,7 +1300,7 @@ public IEnumerator> GetEnumerator() /// Inherits from DynamicPartitionEnumeratorForIndexRange_Abstract /// Provides customized implementation of SourceCount property and Current property for Array /// - private class InternalPartitionEnumerator : DynamicPartitionEnumeratorForIndexRange_Abstract + private sealed class InternalPartitionEnumerator : DynamicPartitionEnumeratorForIndexRange_Abstract { //constructor internal InternalPartitionEnumerator(TSource[] sharedReader, SharedLong sharedIndex) @@ -1505,7 +1505,7 @@ public bool MoveNext() /// Provides customized implementation of SourceCount and CreatePartition /// /// - private class StaticIndexRangePartitionerForIList : StaticIndexRangePartitioner> + private sealed class StaticIndexRangePartitionerForIList : StaticIndexRangePartitioner> { private readonly IList _list; internal StaticIndexRangePartitionerForIList(IList list) @@ -1529,7 +1529,7 @@ protected override IEnumerator> CreatePartition(int /// Provides customized implementation of Current property /// /// - private class StaticIndexRangePartitionForIList : StaticIndexRangePartition + private sealed class StaticIndexRangePartitionForIList : StaticIndexRangePartition { //the source collection shared by all partitions private readonly IList _list; @@ -1563,7 +1563,7 @@ public override KeyValuePair Current /// Inherits from StaticIndexRangePartitioner /// Provides customized implementation of SourceCount and CreatePartition for Array /// - private class StaticIndexRangePartitionerForArray : StaticIndexRangePartitioner + private sealed class StaticIndexRangePartitionerForArray : StaticIndexRangePartitioner { private readonly TSource[] _array; internal StaticIndexRangePartitionerForArray(TSource[] array) @@ -1586,7 +1586,7 @@ protected override IEnumerator> CreatePartition(int /// Inherits from StaticIndexRangePartitioner /// Provides customized implementation of SourceCount and CreatePartition /// - private class StaticIndexRangePartitionForArray : StaticIndexRangePartition + private sealed class StaticIndexRangePartitionForArray : StaticIndexRangePartition { //the source collection shared by all partitions private readonly TSource[] _array; @@ -1620,7 +1620,7 @@ public override KeyValuePair Current /// /// A very simple primitive that allows us to share a value across multiple threads. /// - private class SharedInt + private sealed class SharedInt { internal volatile int Value; @@ -1633,7 +1633,7 @@ internal SharedInt(int value) /// /// A very simple primitive that allows us to share a value across multiple threads. /// - private class SharedBool + private sealed class SharedBool { internal volatile bool Value; @@ -1646,7 +1646,7 @@ internal SharedBool(bool value) /// /// A very simple primitive that allows us to share a value across multiple threads. /// - private class SharedLong + private sealed class SharedLong { internal long Value; internal SharedLong(long value) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Enumerator.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Enumerator.cs index e2e70f0c3a195..81235cce308a6 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Enumerator.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Enumerator.cs @@ -68,7 +68,7 @@ public bool MoveNext() /// /// An array enumerator that implements pattern (including ). /// - private class EnumeratorObject : IEnumerator + private sealed class EnumeratorObject : IEnumerator { /// /// A shareable singleton for enumerating empty arrays. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs index 2a9ca8744d339..31e093c67b7e1 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs @@ -217,7 +217,7 @@ private string DebuggerDisplay get { var self = this; - return self.IsDefault ? "Uninitialized" : string.Format(CultureInfo.CurrentCulture, "Length = {0}", self.Length); + return self.IsDefault ? "Uninitialized" : $"Length = {self.Length}"; } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs index eb94a5bfd27c0..1c892450ae43b 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs @@ -718,7 +718,7 @@ private bool Apply(MutationResult result) /// /// A simple view of the immutable collection that the debugger can show to the developer. /// - internal class ImmutableDictionaryBuilderDebuggerProxy where TKey : notnull + internal sealed class ImmutableDictionaryBuilderDebuggerProxy where TKey : notnull { /// /// The collection to be enumerated. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs index f96b5b33314c3..13ba55038c424 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableEnumerableDebuggerProxy.cs @@ -12,7 +12,7 @@ namespace System.Collections.Immutable /// /// The type of the dictionary's keys. /// The type of the dictionary's values. - internal class ImmutableDictionaryDebuggerProxy : ImmutableEnumerableDebuggerProxy> where TKey : notnull + internal sealed class ImmutableDictionaryDebuggerProxy : ImmutableEnumerableDebuggerProxy> where TKey : notnull { /// /// Initializes a new instance of the class. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableExtensions.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableExtensions.cs index 34b40e4a83eee..428c71f13768b 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableExtensions.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableExtensions.cs @@ -108,7 +108,7 @@ internal static DisposableEnumeratorAdapter GetEnumerableDisposa /// Wraps a as an ordered collection. /// /// The type of element in the collection. - private class ListOfTWrapper : IOrderedCollection + private sealed class ListOfTWrapper : IOrderedCollection { /// /// The list being exposed. @@ -168,7 +168,7 @@ IEnumerator IEnumerable.GetEnumerator() /// Wraps any as an ordered, indexable list. /// /// The type of element in the collection. - private class FallbackWrapper : IOrderedCollection + private sealed class FallbackWrapper : IOrderedCollection { /// /// The original sequence. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByRefEqualityComparer.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByRefEqualityComparer.cs index eaa7e7a47eb93..05cd5ddc78608 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByRefEqualityComparer.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByRefEqualityComparer.cs @@ -14,7 +14,7 @@ public sealed partial class ImmutableHashSet : IImmutableSet, IHashKeyColl /// Compares equality between two instances /// by reference. /// - private class HashBucketByRefEqualityComparer : IEqualityComparer + private sealed class HashBucketByRefEqualityComparer : IEqualityComparer { /// /// The singleton instance. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByValueEqualityComparer.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByValueEqualityComparer.cs index 5e0a66088b637..3cac674f7065c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByValueEqualityComparer.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucketByValueEqualityComparer.cs @@ -14,7 +14,7 @@ public sealed partial class ImmutableHashSet : IImmutableSet, IHashKeyColl /// Compares equality between two instances /// by value. /// - private class HashBucketByValueEqualityComparer : IEqualityComparer + private sealed class HashBucketByValueEqualityComparer : IEqualityComparer { /// /// The instance to use when the value comparer is . diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs index ee6b888241d25..3395d3cc81b0e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs @@ -1100,7 +1100,7 @@ object ICollection.SyncRoot /// /// A simple view of the immutable list that the debugger can show to the developer. /// - internal class ImmutableListBuilderDebuggerProxy + internal sealed class ImmutableListBuilderDebuggerProxy { /// /// The collection to be enumerated. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.Enumerator.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.Enumerator.cs index 844a414a2e8b7..ac6ed27afec02 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.Enumerator.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue_1.Enumerator.cs @@ -101,7 +101,7 @@ public bool MoveNext() /// /// A memory allocation-free enumerator of . /// - private class EnumeratorObject : IEnumerator + private sealed class EnumeratorObject : IEnumerator { /// /// The original queue being enumerated. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs index 2f8fafe67b298..911208abcac79 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Builder.cs @@ -655,7 +655,7 @@ public ImmutableSortedDictionary ToImmutable() /// /// A simple view of the immutable collection that the debugger can show to the developer. /// - internal class ImmutableSortedDictionaryBuilderDebuggerProxy where TKey : notnull + internal sealed class ImmutableSortedDictionaryBuilderDebuggerProxy where TKey : notnull { /// /// The collection to be enumerated. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.DebuggerProxy.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.DebuggerProxy.cs index db3c4d0b12b5d..c2e019c33c679 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.DebuggerProxy.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Builder.DebuggerProxy.cs @@ -8,7 +8,7 @@ namespace System.Collections.Immutable /// /// A simple view of the immutable collection that the debugger can show to the developer. /// - internal class ImmutableSortedSetBuilderDebuggerProxy + internal sealed class ImmutableSortedSetBuilderDebuggerProxy { /// /// The collection to be enumerated. @@ -33,7 +33,7 @@ public T[] Contents { get { - return _set.ToArray(_set.Count); ; + return _set.ToArray(_set.Count); } } } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs index b74674d8e2718..08dbda353bc60 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.cs @@ -1132,7 +1132,7 @@ private ImmutableSortedSet LeafToRootRefill(IEnumerable addedItems) /// /// An reverse enumerable of a sorted set. /// - private class ReverseEnumerable : IEnumerable + private sealed class ReverseEnumerable : IEnumerable { /// /// The root node to enumerate. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.Enumerator.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.Enumerator.cs index daabed2227f14..556e7f54d9658 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.Enumerator.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack_1.Enumerator.cs @@ -76,7 +76,7 @@ public bool MoveNext() /// /// Enumerates a stack with no memory allocations. /// - private class EnumeratorObject : IEnumerator + private sealed class EnumeratorObject : IEnumerator { /// /// The original stack being enumerated. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/SecureObjectPool.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/SecureObjectPool.cs index 6d194da50e5db..363d69e0199c7 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/SecureObjectPool.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/SecureObjectPool.cs @@ -10,7 +10,7 @@ namespace System.Collections.Immutable /// /// Object pooling utilities. /// - internal class SecureObjectPool + internal static class SecureObjectPool { /// /// The ever-incrementing (and wrap-on-overflow) integer for owner id's. @@ -38,7 +38,7 @@ internal static int NewId() } } - internal class SecureObjectPool + internal sealed class SecureObjectPool where TCaller : ISecurePooledObjectUser { public void TryAdd(TCaller caller, SecurePooledObject item) @@ -79,7 +79,7 @@ internal interface ISecurePooledObjectUser int PoolUserId { get; } } - internal class SecurePooledObject + internal sealed class SecurePooledObject { private readonly T _value; private int _owner; diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs index 7349cefa67b5c..14a42fba17956 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs @@ -1984,6 +1984,7 @@ public void IStructuralEquatableEqualsNullComparerInvalid() [Theory] [MemberData(nameof(IStructuralEquatableGetHashCodeData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android)] public void IStructuralEquatableGetHashCode(IEnumerable source, IEqualityComparer comparer) { var array = source.ToImmutableArray(); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.nonnetstandard.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.nonnetstandard.cs index 3ea83edeba4f6..36615bb91963e 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.nonnetstandard.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.nonnetstandard.cs @@ -75,6 +75,7 @@ public void EqualsTest() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void AddRangeTest() { var map = Empty(); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs index 0cd60735872e6..d87635ef826d3 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs @@ -102,6 +102,7 @@ public void AddExistingKeyDifferentValueTest() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ToUnorderedTest() { var sortedMap = Empty().AddRange(Enumerable.Range(1, 100).Select(n => new KeyValuePair(n, new GenericParameterHelper(n)))); diff --git a/src/libraries/System.Collections.NonGeneric/src/System/Collections/Queue.cs b/src/libraries/System.Collections.NonGeneric/src/System/Collections/Queue.cs index 99c0e15761f2c..748729d3615ab 100644 --- a/src/libraries/System.Collections.NonGeneric/src/System/Collections/Queue.cs +++ b/src/libraries/System.Collections.NonGeneric/src/System/Collections/Queue.cs @@ -303,7 +303,7 @@ public virtual void TrimToSize() // Implements a synchronization wrapper around a queue. - private class SynchronizedQueue : Queue + private sealed class SynchronizedQueue : Queue { private readonly Queue _q; private readonly object _root; @@ -423,7 +423,7 @@ public override void TrimToSize() // Implements an enumerator for a Queue. The enumerator uses the // internal version number of the list to ensure that no modifications are // made to the list while an enumeration is in progress. - private class QueueEnumerator : IEnumerator, ICloneable + private sealed class QueueEnumerator : IEnumerator, ICloneable { private readonly Queue _q; private int _index; @@ -442,7 +442,7 @@ internal QueueEnumerator(Queue q) public object Clone() => MemberwiseClone(); - public virtual bool MoveNext() + public bool MoveNext() { if (_version != _q._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); @@ -460,7 +460,7 @@ public virtual bool MoveNext() return true; } - public virtual object? Current + public object? Current { get { @@ -475,7 +475,7 @@ public virtual object? Current } } - public virtual void Reset() + public void Reset() { if (_version != _q._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); if (_q._size == 0) @@ -486,7 +486,7 @@ public virtual void Reset() } } - internal class QueueDebugView + internal sealed class QueueDebugView { private readonly Queue _queue; diff --git a/src/libraries/System.Collections.NonGeneric/src/System/Collections/SortedList.cs b/src/libraries/System.Collections.NonGeneric/src/System/Collections/SortedList.cs index f50a1d5603152..709bf2d0c9d4c 100644 --- a/src/libraries/System.Collections.NonGeneric/src/System/Collections/SortedList.cs +++ b/src/libraries/System.Collections.NonGeneric/src/System/Collections/SortedList.cs @@ -584,7 +584,7 @@ public virtual void TrimToSize() [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - private class SyncSortedList : SortedList + private sealed class SyncSortedList : SortedList { private readonly SortedList _list; // Do not rename (binary serialization) private readonly object _root; // Do not rename (binary serialization) @@ -797,7 +797,7 @@ public override void TrimToSize() } } - private class SortedListEnumerator : IDictionaryEnumerator, ICloneable + private sealed class SortedListEnumerator : IDictionaryEnumerator, ICloneable { private readonly SortedList _sortedList; private object _key = null!; @@ -827,7 +827,7 @@ internal SortedListEnumerator(SortedList sortedList, int index, int count, public object Clone() => MemberwiseClone(); - public virtual object Key + public object Key { get { @@ -837,7 +837,7 @@ public virtual object Key } } - public virtual bool MoveNext() + public bool MoveNext() { if (_version != _sortedList.version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); if (_index < _endIndex) @@ -854,7 +854,7 @@ public virtual bool MoveNext() return false; } - public virtual DictionaryEntry Entry + public DictionaryEntry Entry { get { @@ -864,7 +864,7 @@ public virtual DictionaryEntry Entry } } - public virtual object? Current + public object? Current { get { @@ -879,7 +879,7 @@ public virtual object? Current } } - public virtual object? Value + public object? Value { get { @@ -889,7 +889,7 @@ public virtual object? Value } } - public virtual void Reset() + public void Reset() { if (_version != _sortedList.version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); _index = _startIndex; @@ -901,7 +901,7 @@ public virtual void Reset() [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - private class KeyList : IList + private sealed class KeyList : IList { private readonly SortedList sortedList; // Do not rename (binary serialization) @@ -910,48 +910,48 @@ internal KeyList(SortedList sortedList) this.sortedList = sortedList; } - public virtual int Count + public int Count { get { return sortedList._size; } } - public virtual bool IsReadOnly + public bool IsReadOnly { get { return true; } } - public virtual bool IsFixedSize + public bool IsFixedSize { get { return true; } } - public virtual bool IsSynchronized + public bool IsSynchronized { get { return sortedList.IsSynchronized; } } - public virtual object SyncRoot + public object SyncRoot { get { return sortedList.SyncRoot; } } - public virtual int Add(object? key) + public int Add(object? key) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); // return 0; // suppress compiler warning } - public virtual void Clear() + public void Clear() { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual bool Contains(object? key) + public bool Contains(object? key) { return sortedList.Contains(key!); } - public virtual void CopyTo(Array array, int arrayIndex) + public void CopyTo(Array array, int arrayIndex) { if (array != null && array.Rank != 1) throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); @@ -960,12 +960,12 @@ public virtual void CopyTo(Array array, int arrayIndex) Array.Copy(sortedList.keys, 0, array!, arrayIndex, sortedList.Count); } - public virtual void Insert(int index, object? value) + public void Insert(int index, object? value) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual object? this[int index] + public object? this[int index] { get { @@ -977,12 +977,12 @@ public virtual object? this[int index] } } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return new SortedListEnumerator(sortedList, 0, sortedList.Count, SortedListEnumerator.Keys); } - public virtual int IndexOf(object? key) + public int IndexOf(object? key) { if (key == null) throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key); @@ -993,12 +993,12 @@ public virtual int IndexOf(object? key) return -1; } - public virtual void Remove(object? key) + public void Remove(object? key) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual void RemoveAt(int index) + public void RemoveAt(int index) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } @@ -1006,7 +1006,7 @@ public virtual void RemoveAt(int index) [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - private class ValueList : IList + private sealed class ValueList : IList { private readonly SortedList sortedList; // Do not rename (binary serialization) @@ -1015,47 +1015,47 @@ internal ValueList(SortedList sortedList) this.sortedList = sortedList; } - public virtual int Count + public int Count { get { return sortedList._size; } } - public virtual bool IsReadOnly + public bool IsReadOnly { get { return true; } } - public virtual bool IsFixedSize + public bool IsFixedSize { get { return true; } } - public virtual bool IsSynchronized + public bool IsSynchronized { get { return sortedList.IsSynchronized; } } - public virtual object SyncRoot + public object SyncRoot { get { return sortedList.SyncRoot; } } - public virtual int Add(object? key) + public int Add(object? key) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual void Clear() + public void Clear() { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual bool Contains(object? value) + public bool Contains(object? value) { return sortedList.ContainsValue(value); } - public virtual void CopyTo(Array array, int arrayIndex) + public void CopyTo(Array array, int arrayIndex) { if (array != null && array.Rank != 1) throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array)); @@ -1064,12 +1064,12 @@ public virtual void CopyTo(Array array, int arrayIndex) Array.Copy(sortedList.values, 0, array!, arrayIndex, sortedList.Count); } - public virtual void Insert(int index, object? value) + public void Insert(int index, object? value) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual object? this[int index] + public object? this[int index] { get { @@ -1081,29 +1081,29 @@ public virtual object? this[int index] } } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return new SortedListEnumerator(sortedList, 0, sortedList.Count, SortedListEnumerator.Values); } - public virtual int IndexOf(object? value) + public int IndexOf(object? value) { return Array.IndexOf(sortedList.values, value, 0, sortedList.Count); } - public virtual void Remove(object? value) + public void Remove(object? value) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } - public virtual void RemoveAt(int index) + public void RemoveAt(int index) { throw new NotSupportedException(SR.NotSupported_SortedListNestedWrite); } } // internal debug view class for sorted list - internal class SortedListDebugView + internal sealed class SortedListDebugView { private readonly SortedList _sortedList; diff --git a/src/libraries/System.Collections.NonGeneric/src/System/Collections/Stack.cs b/src/libraries/System.Collections.NonGeneric/src/System/Collections/Stack.cs index fa9e96d889856..28b88fb8860e0 100644 --- a/src/libraries/System.Collections.NonGeneric/src/System/Collections/Stack.cs +++ b/src/libraries/System.Collections.NonGeneric/src/System/Collections/Stack.cs @@ -216,7 +216,7 @@ public static Stack Synchronized(Stack stack) return objArray; } - private class SyncStack : Stack + private sealed class SyncStack : Stack { private readonly Stack _s; private readonly object _root; @@ -324,7 +324,7 @@ public override IEnumerator GetEnumerator() } } - private class StackEnumerator : IEnumerator, ICloneable + private sealed class StackEnumerator : IEnumerator, ICloneable { private readonly Stack _stack; private int _index; @@ -341,7 +341,7 @@ internal StackEnumerator(Stack stack) public object Clone() => MemberwiseClone(); - public virtual bool MoveNext() + public bool MoveNext() { bool retval; if (_version != _stack._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); @@ -366,7 +366,7 @@ public virtual bool MoveNext() return retval; } - public virtual object? Current + public object? Current { get { @@ -376,7 +376,7 @@ public virtual object? Current } } - public virtual void Reset() + public void Reset() { if (_version != _stack._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); _index = -2; @@ -384,7 +384,7 @@ public virtual void Reset() } } - internal class StackDebugView + internal sealed class StackDebugView { private readonly Stack _stack; diff --git a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs index 6a0428686cb38..14261e4c8c031 100644 --- a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs +++ b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveComparerTests.cs @@ -65,6 +65,7 @@ public void Ctor_CultureInfo_Compare(object a, object b, int expected) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android)] public void Ctor_CultureInfo_Compare_TurkishI() { var cultureNames = Helpers.TestCultureNames; diff --git a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs index f41a9225e0246..e9120f915aaf3 100644 --- a/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs +++ b/src/libraries/System.Collections.NonGeneric/tests/CaseInsensitiveHashCodeProviderTests.cs @@ -95,6 +95,7 @@ public void Ctor_CultureInfo_ChangeCurrentCulture_GetHashCodeCompare(object a, o } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android)] public void Ctor_CultureInfo_GetHashCodeCompare_TurkishI() { var cultureNames = Helpers.TestCultureNames; @@ -150,6 +151,7 @@ public void Default_GetHashCodeCompare(object a, object b, bool expected) } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/37069", TestPlatforms.Android)] public void Default_Compare_TurkishI() { // Turkish has lower-case and upper-case version of the dotted "i", so the upper case of "i" (U+0069) isn't "I" (U+0049) diff --git a/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/ListDictionary.cs b/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/ListDictionary.cs index bf5184b869969..daa68a87bdb6c 100644 --- a/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/ListDictionary.cs +++ b/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/ListDictionary.cs @@ -273,7 +273,7 @@ public void Remove(object key) count--; } - private class NodeEnumerator : IDictionaryEnumerator + private sealed class NodeEnumerator : IDictionaryEnumerator { private readonly ListDictionary _list; private DictionaryNode? _current; @@ -362,7 +362,7 @@ public void Reset() } } - private class NodeKeyValueCollection : ICollection + private sealed class NodeKeyValueCollection : ICollection { private readonly ListDictionary _list; private readonly bool _isKeys; @@ -422,7 +422,7 @@ IEnumerator IEnumerable.GetEnumerator() } - private class NodeKeyValueEnumerator : IEnumerator + private sealed class NodeKeyValueEnumerator : IEnumerator { private readonly ListDictionary _list; private DictionaryNode? _current; diff --git a/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/NameObjectCollectionBase.cs b/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/NameObjectCollectionBase.cs index afa8b7a1cfbde..2d04189582726 100644 --- a/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/NameObjectCollectionBase.cs +++ b/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/NameObjectCollectionBase.cs @@ -473,7 +473,7 @@ public virtual KeysCollection Keys // Simple entry class to allow substitution of values and indexed access to keys // - internal class NameObjectEntry + internal sealed class NameObjectEntry { internal NameObjectEntry(string? name, object? value) { @@ -489,7 +489,7 @@ internal NameObjectEntry(string? name, object? value) // Enumerator over keys of NameObjectCollection // - internal class NameObjectKeysEnumerator : IEnumerator + internal sealed class NameObjectKeysEnumerator : IEnumerator { private int _pos; private readonly NameObjectCollectionBase _coll; diff --git a/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/OrderedDictionary.cs b/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/OrderedDictionary.cs index db4a023525a79..12efef763cc5a 100644 --- a/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/OrderedDictionary.cs +++ b/src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/OrderedDictionary.cs @@ -453,7 +453,7 @@ protected virtual void OnDeserialization(object? sender) /// OrderedDictionaryEnumerator works just like any other IDictionaryEnumerator, but it retrieves DictionaryEntries /// in the order by index. /// - private class OrderedDictionaryEnumerator : IDictionaryEnumerator + private sealed class OrderedDictionaryEnumerator : IDictionaryEnumerator { private readonly int _objectReturnType; internal const int Keys = 1; @@ -545,7 +545,7 @@ public void Reset() /// that is "live"- it will reflect changes to the OrderedDictionary on the collection made after the getter /// was called. /// - private class OrderedDictionaryKeyValueCollection : ICollection + private sealed class OrderedDictionaryKeyValueCollection : ICollection { private readonly ArrayList _objects; private readonly bool _isKeys; diff --git a/src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs b/src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs index 5918bc1a62915..ae4b3c34ff9d3 100644 --- a/src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs +++ b/src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs @@ -358,9 +358,40 @@ public void EnqueueRange(IEnumerable<(TElement Element, TPriority Priority)> ite throw new ArgumentNullException(nameof(items)); } + int count = 0; + var collection = items as ICollection<(TElement Element, TPriority Priority)>; + if (collection is not null && (count = collection.Count) > _nodes.Length - _size) + { + Grow(_size + count); + } + if (_size == 0) { - _nodes = EnumerableHelpers.ToArray(items, out _size); + // build using Heapify() if the queue is empty. + + if (collection is not null) + { + collection.CopyTo(_nodes, 0); + _size = count; + } + else + { + int i = 0; + (TElement, TPriority)[] nodes = _nodes; + foreach ((TElement element, TPriority priority) in items) + { + if (nodes.Length == i) + { + Grow(i + 1); + nodes = _nodes; + } + + nodes[i++] = (element, priority); + } + + _size = i; + } + _version++; if (_size > 1) @@ -370,11 +401,6 @@ public void EnqueueRange(IEnumerable<(TElement Element, TPriority Priority)> ite } else { - if (EnumerableHelpers.TryGetCount(items, out int count) && _nodes.Length - _size < count) - { - Grow(_size + count); - } - foreach ((TElement element, TPriority priority) in items) { Enqueue(element, priority); @@ -398,22 +424,28 @@ public void EnqueueRange(IEnumerable elements, TPriority priority) throw new ArgumentNullException(nameof(elements)); } - if (EnumerableHelpers.TryGetCount(elements, out int count) && _nodes.Length - _size < count) + int count; + if (elements is ICollection<(TElement Element, TPriority Priority)> collection && + (count = collection.Count) > _nodes.Length - _size) { Grow(_size + count); } if (_size == 0) { + // build using Heapify() if the queue is empty. + int i = 0; + (TElement, TPriority)[] nodes = _nodes; foreach (TElement element in elements) { - if (_nodes.Length == i) + if (nodes.Length == i) { Grow(i + 1); + nodes = _nodes; } - _nodes[i++] = (element, priority); + nodes[i++] = (element, priority); } _size = i; @@ -523,25 +555,25 @@ private void Grow(int minCapacity) /// private void RemoveRootNode() { - // The idea is to replace the root node by the very last - // node and shorten the array by one. - int lastNodeIndex = _size - 1; - (TElement Element, TPriority Priority) lastNode = _nodes[lastNodeIndex]; - if (RuntimeHelpers.IsReferenceOrContainsReferences<(TElement, TPriority)>()) - { - _nodes[lastNodeIndex] = default; - } - - _size--; + int lastNodeIndex = --_size; _version++; - if (_comparer == null) + if (lastNodeIndex > 0) { - MoveDownDefaultComparer(lastNode, 0); + (TElement Element, TPriority Priority) lastNode = _nodes[lastNodeIndex]; + if (_comparer == null) + { + MoveDownDefaultComparer(lastNode, 0); + } + else + { + MoveDownCustomComparer(lastNode, 0); + } } - else + + if (RuntimeHelpers.IsReferenceOrContainsReferences<(TElement, TPriority)>()) { - MoveDownCustomComparer(lastNode, 0); + _nodes[lastNodeIndex] = default; } } @@ -593,6 +625,7 @@ private void MoveUpDefaultComparer((TElement Element, TPriority Priority) node, // a similar optimization as in the insertion sort. Debug.Assert(_comparer is null); + Debug.Assert(0 <= nodeIndex && nodeIndex < _size); (TElement Element, TPriority Priority)[] nodes = _nodes; @@ -624,6 +657,7 @@ private void MoveUpCustomComparer((TElement Element, TPriority Priority) node, i // a similar optimization as in the insertion sort. Debug.Assert(_comparer is not null); + Debug.Assert(0 <= nodeIndex && nodeIndex < _size); IComparer comparer = _comparer; (TElement Element, TPriority Priority)[] nodes = _nodes; @@ -657,6 +691,7 @@ private void MoveDownDefaultComparer((TElement Element, TPriority Priority) node // for this value to drop in. Similar optimization as in the insertion sort. Debug.Assert(_comparer is null); + Debug.Assert(0 <= nodeIndex && nodeIndex < _size); (TElement Element, TPriority Priority)[] nodes = _nodes; int size = _size; @@ -704,6 +739,7 @@ private void MoveDownCustomComparer((TElement Element, TPriority Priority) node, // for this value to drop in. Similar optimization as in the insertion sort. Debug.Assert(_comparer is not null); + Debug.Assert(0 <= nodeIndex && nodeIndex < _size); IComparer comparer = _comparer; (TElement Element, TPriority Priority)[] nodes = _nodes; @@ -876,10 +912,7 @@ private bool MoveNextRare() /// Gets the element at the current position of the enumerator. /// public (TElement Element, TPriority Priority) Current => _current; - - object IEnumerator.Current => - _index == 0 || _index == _queue._size + 1 ? throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen) : - Current; + object IEnumerator.Current => _current; void IEnumerator.Reset() { diff --git a/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.Tests.cs b/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.Tests.cs index 5a0ac9e0b80c3..72169ac22bd26 100644 --- a/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.Tests.cs +++ b/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.Tests.cs @@ -7,29 +7,29 @@ namespace System.Collections.Tests { - public abstract class PriorityQueue_Generic_Tests : TestBase + public abstract class PriorityQueue_Generic_Tests : IGenericSharedAPI_Tests<(TElement Element, TPriority Priority)> { - protected abstract TElement CreateElement(int seed); + #region PriorityQueue Helper methods + protected virtual IComparer? GetPriorityComparer() => Comparer.Default; - #region Helper methods protected IEnumerable<(TElement, TPriority)> CreateItems(int count) { const int MagicValue = 34; int seed = count * MagicValue; for (int i = 0; i < count; i++) { - yield return (CreateElement(seed++), CreateT(seed++)); + yield return CreateT(seed++); } } protected PriorityQueue CreateEmptyPriorityQueue(int initialCapacity = 0) - => new PriorityQueue(initialCapacity, GetIComparer()); + => new PriorityQueue(initialCapacity, GetPriorityComparer()); protected PriorityQueue CreatePriorityQueue( int initialCapacity, int countOfItemsToGenerate, out List<(TElement element, TPriority priority)> generatedItems) { generatedItems = CreateItems(countOfItemsToGenerate).ToList(); - var queue = new PriorityQueue(initialCapacity, GetIComparer()); + var queue = new PriorityQueue(initialCapacity, GetPriorityComparer()); queue.EnqueueRange(generatedItems); return queue; } @@ -59,7 +59,7 @@ public void PriorityQueue_EmptyCollection_UnorderedItemsIsEmpty(int initialCapac [Fact] public void PriorityQueue_ComparerConstructor_ComparerShouldEqualParameter() { - IComparer comparer = GetIComparer(); + IComparer comparer = GetPriorityComparer(); var queue = new PriorityQueue(comparer); Assert.Equal(comparer, queue.Comparer); } @@ -86,7 +86,7 @@ public void PriorityQueue_CapacityConstructor_ComparerShouldEqualDefaultComparer public void PriorityQueue_EnumerableConstructor_ShouldContainAllElements(int count) { (TElement, TPriority)[] itemsToEnqueue = CreateItems(count).ToArray(); - PriorityQueue queue = new PriorityQueue(itemsToEnqueue); + PriorityQueue queue = new PriorityQueue(itemsToEnqueue, GetPriorityComparer()); Assert.Equal(itemsToEnqueue.Length, queue.Count); AssertExtensions.CollectionEqual(itemsToEnqueue, queue.UnorderedItems, EqualityComparer<(TElement, TPriority)>.Default); } @@ -232,5 +232,32 @@ public void PriorityQueue_Enumeration_OrderingIsConsistent(int count) } #endregion + + #region IGenericSharedAPI Helper Methods + + /// + /// requires collections that implement IEnumerable. + /// Since PriorityQueue does not we use a subclass that delegates to . + /// + protected class EnumerablePriorityQueue : PriorityQueue, IEnumerable<(TElement Element, TPriority Priority)> + { + public EnumerablePriorityQueue(IComparer? comparer) : base(comparer) + { + } + + IEnumerator<(TElement Element, TPriority Priority)> IEnumerable<(TElement Element, TPriority Priority)>.GetEnumerator() => UnorderedItems.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => UnorderedItems.GetEnumerator(); + } + + protected override IEnumerable<(TElement Element, TPriority Priority)> GenericIEnumerableFactory() => new EnumerablePriorityQueue(GetPriorityComparer()); + protected override int Count(IEnumerable<(TElement Element, TPriority Priority)> enumerable) => ((EnumerablePriorityQueue)enumerable).Count; + protected override void Add(IEnumerable<(TElement Element, TPriority Priority)> enumerable, (TElement Element, TPriority Priority) value) => ((EnumerablePriorityQueue)enumerable).Enqueue(value.Element, value.Priority); + protected override void Clear(IEnumerable<(TElement Element, TPriority Priority)> enumerable) => ((EnumerablePriorityQueue)enumerable).Clear(); + protected override bool Contains(IEnumerable<(TElement Element, TPriority Priority)> enumerable, (TElement Element, TPriority Priority) value) => ((EnumerablePriorityQueue)enumerable).Any(elem => elem.Equals(value)); + protected override void CopyTo(IEnumerable<(TElement Element, TPriority Priority)> enumerable, (TElement Element, TPriority Priority)[] array, int index) => ((ICollection)((EnumerablePriorityQueue)enumerable).UnorderedItems).CopyTo(array, index); + protected override bool Remove(IEnumerable<(TElement Element, TPriority Priority)> enumerable) => ((EnumerablePriorityQueue)enumerable).TryDequeue(out _, out _); + protected override Type IGenericSharedAPI_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException); + + #endregion } } diff --git a/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.cs b/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.cs index b7f964f1fd77a..2697971e50026 100644 --- a/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.cs +++ b/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Generic.cs @@ -7,34 +7,37 @@ namespace System.Collections.Tests { public class PriorityQueue_Generic_Tests_string_string : PriorityQueue_Generic_Tests { - protected override string CreateT(int seed) => CreateString(seed); - protected override string CreateElement(int seed) => CreateString(seed); - - protected string CreateString(int seed) + protected override (string, string) CreateT(int seed) { - int stringLength = seed % 10 + 5; - Random rand = new Random(seed); - byte[] bytes = new byte[stringLength]; - rand.NextBytes(bytes); - return Convert.ToBase64String(bytes); + var random = new Random(seed); + return (CreateString(random), CreateString(random)); + + static string CreateString(Random random) + { + int stringLength = random.Next(5, 15); + byte[] bytes = new byte[stringLength]; + random.NextBytes(bytes); + return Convert.ToBase64String(bytes); + } } } public class PriorityQueue_Generic_Tests_int_int : PriorityQueue_Generic_Tests { - protected override int CreateT(int seed) => CreateInt(seed); - protected override int CreateElement(int seed) => CreateInt(seed); - - protected int CreateInt(int seed) => new Random(seed).Next(); + protected override (int, int) CreateT(int seed) + { + var random = new Random(seed); + return (random.Next(),random.Next()); + } } public class PriorityQueue_Generic_Tests_string_string_CustomComparer : PriorityQueue_Generic_Tests_string_string { - protected override IComparer GetIComparer() => StringComparer.InvariantCultureIgnoreCase; + protected override IComparer GetPriorityComparer() => StringComparer.InvariantCultureIgnoreCase; } public class PriorityQueue_Generic_Tests_int_int_CustomComparer : PriorityQueue_Generic_Tests_int_int { - protected override IComparer GetIComparer() => Comparer.Create((x, y) => -x.CompareTo(y)); + protected override IComparer GetPriorityComparer() => Comparer.Create((x, y) => -x.CompareTo(y)); } } diff --git a/src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs b/src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs index a4b61f45be185..2aa40a0549de3 100644 --- a/src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs +++ b/src/libraries/System.Collections/tests/Generic/Queue/Queue.Generic.Tests.cs @@ -47,7 +47,7 @@ protected override IEnumerable GenericIEnumerableFactory(int count) protected override void Clear(IEnumerable enumerable) => ((Queue)enumerable).Clear(); protected override bool Contains(IEnumerable enumerable, T value) => ((Queue)enumerable).Contains(value); protected override void CopyTo(IEnumerable enumerable, T[] array, int index) => ((Queue)enumerable).CopyTo(array, index); - protected override bool Remove(IEnumerable enumerable) { ((Queue)enumerable).Dequeue(); return true; } + protected override bool Remove(IEnumerable enumerable) => ((Queue)enumerable).TryDequeue(out _); protected override bool Enumerator_Current_UndefinedOperation_Throws => true; protected override Type IGenericSharedAPI_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException); @@ -212,7 +212,7 @@ public void Queue_Generic_TrimExcess_OnValidQueueThatHasntBeenRemovedFrom(int co [MemberData(nameof(ValidCollectionSizes))] public void Queue_Generic_TrimExcess_Repeatedly(int count) { - Queue queue = GenericQueueFactory(count); ; + Queue queue = GenericQueueFactory(count); List expected = queue.ToList(); queue.TrimExcess(); queue.TrimExcess(); @@ -226,7 +226,7 @@ public void Queue_Generic_TrimExcess_AfterRemovingOneElement(int count) { if (count > 0) { - Queue queue = GenericQueueFactory(count); ; + Queue queue = GenericQueueFactory(count); List expected = queue.ToList(); queue.TrimExcess(); T removed = queue.Dequeue(); @@ -243,7 +243,7 @@ public void Queue_Generic_TrimExcess_AfterClearingAndAddingSomeElementsBack(int { if (count > 0) { - Queue queue = GenericQueueFactory(count); ; + Queue queue = GenericQueueFactory(count); queue.TrimExcess(); queue.Clear(); queue.TrimExcess(); @@ -261,7 +261,7 @@ public void Queue_Generic_TrimExcess_AfterClearingAndAddingAllElementsBack(int c { if (count > 0) { - Queue queue = GenericQueueFactory(count); ; + Queue queue = GenericQueueFactory(count); queue.TrimExcess(); queue.Clear(); queue.TrimExcess(); diff --git a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs index e50b88250a2f6..2bc7c17b3415a 100644 --- a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs +++ b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs @@ -11,8 +11,8 @@ namespace System.ComponentModel.DataAnnotations public partial class AssociatedMetadataTypeTypeDescriptionProvider : System.ComponentModel.TypeDescriptionProvider { public AssociatedMetadataTypeTypeDescriptionProvider(System.Type type) { } - public AssociatedMetadataTypeTypeDescriptionProvider(System.Type type, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type associatedMetadataType) { } - public override System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType, object instance) { throw null; } + public AssociatedMetadataTypeTypeDescriptionProvider(System.Type type, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type associatedMetadataType) { } + public override System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType, object instance) { throw null; } } #nullable enable [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple=false, Inherited=true)] @@ -30,6 +30,7 @@ public AssociationAttribute(string name, string thisKey, string otherKey) { } [System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false)] public partial class CompareAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The property referenced by 'otherProperty' may be trimmed. Ensure it is preserved.")] public CompareAttribute(string otherProperty) { } public string OtherProperty { get { throw null; } } public string? OtherPropertyDisplayName { get { throw null; } } @@ -51,8 +52,9 @@ public CreditCardAttribute() : base (default(System.ComponentModel.DataAnnotatio [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Field | System.AttributeTargets.Method | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple=true)] public sealed partial class CustomValidationAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute { - public CustomValidationAttribute(System.Type validatorType, string method) { } + public CustomValidationAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type validatorType, string method) { } public string Method { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] public System.Type ValidatorType { get { throw null; } } public override string FormatErrorMessage(string name) { throw null; } protected override System.ComponentModel.DataAnnotations.ValidationResult? IsValid(object? value, System.ComponentModel.DataAnnotations.ValidationContext validationContext) { throw null; } @@ -99,6 +101,7 @@ public DisplayAttribute() { } public string? Name { get { throw null; } set { } } public int Order { get { throw null; } set { } } public string? Prompt { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] public System.Type? ResourceType { get { throw null; } set { } } public string? ShortName { get { throw null; } set { } } public bool? GetAutoGenerateField() { throw null; } @@ -129,6 +132,7 @@ public DisplayFormatAttribute() { } public string? DataFormatString { get { throw null; } set { } } public bool HtmlEncode { get { throw null; } set { } } public string? NullDisplayText { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] public System.Type? NullDisplayTextResourceType { get { throw null; } set { } } public string? GetNullDisplayText() { throw null; } } @@ -185,7 +189,9 @@ public KeyAttribute() { } [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple=false)] public partial class MaxLengthAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Uses reflection to get the 'Count' property on types that don't implement ICollection. This 'Count' property may be trimmed. Ensure it is preserved.")] public MaxLengthAttribute() { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Uses reflection to get the 'Count' property on types that don't implement ICollection. This 'Count' property may be trimmed. Ensure it is preserved.")] public MaxLengthAttribute(int length) { } public int Length { get { throw null; } } public override string FormatErrorMessage(string name) { throw null; } @@ -194,13 +200,14 @@ public MaxLengthAttribute(int length) { } [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=false)] public sealed partial class MetadataTypeAttribute : System.Attribute { - public MetadataTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type metadataClassType) { } - [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] + public MetadataTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type metadataClassType) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] public System.Type MetadataClassType { get { throw null; } } } [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, AllowMultiple=false)] public partial class MinLengthAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Uses reflection to get the 'Count' property on types that don't implement ICollection. This 'Count' property may be trimmed. Ensure it is preserved.")] public MinLengthAttribute(int length) { } public int Length { get { throw null; } } public override string FormatErrorMessage(string name) { throw null; } @@ -217,11 +224,12 @@ public partial class RangeAttribute : System.ComponentModel.DataAnnotations.Vali { public RangeAttribute(double minimum, double maximum) { } public RangeAttribute(int minimum, int maximum) { } - public RangeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string minimum, string maximum) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] + public RangeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string minimum, string maximum) { } public bool ConvertValueInInvariantCulture { get { throw null; } set { } } public object Maximum { get { throw null; } } public object Minimum { get { throw null; } } - [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] public System.Type OperandType { get { throw null; } } public bool ParseLimitsInInvariantCulture { get { throw null; } set { } } public override string FormatErrorMessage(string name) { throw null; } @@ -288,6 +296,7 @@ protected ValidationAttribute(System.Func errorMessageAccessor) { } protected ValidationAttribute(string errorMessage) { } public string? ErrorMessage { get { throw null; } set { } } public string? ErrorMessageResourceName { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] public System.Type? ErrorMessageResourceType { get { throw null; } set { } } protected string ErrorMessageString { get { throw null; } } public virtual bool RequiresValidationContext { get { throw null; } } @@ -300,8 +309,11 @@ public void Validate(object? value, string name) { } } public sealed partial class ValidationContext : System.IServiceProvider { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public ValidationContext(object instance) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public ValidationContext(object instance, System.Collections.Generic.IDictionary? items) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public ValidationContext(object instance, System.IServiceProvider? serviceProvider, System.Collections.Generic.IDictionary? items) { } public string DisplayName { get { throw null; } set { } } public System.Collections.Generic.IDictionary Items { get { throw null; } } @@ -335,12 +347,18 @@ public ValidationResult(string? errorMessage, System.Collections.Generic.IEnumer } public static partial class Validator { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public static bool TryValidateObject(object instance, System.ComponentModel.DataAnnotations.ValidationContext validationContext, System.Collections.Generic.ICollection? validationResults) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public static bool TryValidateObject(object instance, System.ComponentModel.DataAnnotations.ValidationContext validationContext, System.Collections.Generic.ICollection? validationResults, bool validateAllProperties) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of validationContext.ObjectType cannot be statically discovered.")] public static bool TryValidateProperty(object? value, System.ComponentModel.DataAnnotations.ValidationContext validationContext, System.Collections.Generic.ICollection? validationResults) { throw null; } public static bool TryValidateValue(object value, System.ComponentModel.DataAnnotations.ValidationContext validationContext, System.Collections.Generic.ICollection? validationResults, System.Collections.Generic.IEnumerable validationAttributes) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public static void ValidateObject(object instance, System.ComponentModel.DataAnnotations.ValidationContext validationContext) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public static void ValidateObject(object instance, System.ComponentModel.DataAnnotations.ValidationContext validationContext, bool validateAllProperties) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of validationContext.ObjectType cannot be statically discovered.")] public static void ValidateProperty(object? value, System.ComponentModel.DataAnnotations.ValidationContext validationContext) { } public static void ValidateValue(object value, System.ComponentModel.DataAnnotations.ValidationContext validationContext, System.Collections.Generic.IEnumerable validationAttributes) { } } diff --git a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.csproj b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.csproj index 532f0e6653c48..0f88fe44694e2 100644 --- a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.csproj +++ b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.csproj @@ -7,5 +7,6 @@ + \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.Annotations/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.ComponentModel.Annotations/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index acf0ec22218af..0000000000000 --- a/src/libraries/System.ComponentModel.Annotations/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - ILLink - IL2072 - member - M:System.ComponentModel.DataAnnotations.CompareAttribute.IsValid(System.Object,System.ComponentModel.DataAnnotations.ValidationContext) - - - ILLink - IL2072 - member - M:System.ComponentModel.DataAnnotations.CountPropertyHelper.TryGetCount(System.Object,System.Int32@) - - - ILLink - IL2072 - member - M:System.ComponentModel.DataAnnotations.CustomValidationAttribute.ValidateMethodParameter - - - ILLink - IL2072 - member - M:System.ComponentModel.DataAnnotations.Validator.GetPropertyValues(System.Object,System.ComponentModel.DataAnnotations.ValidationContext) - - - ILLink - IL2077 - member - M:System.ComponentModel.DataAnnotations.LocalizableString.GetLocalizableValue - - - ILLink - IL2077 - member - M:System.ComponentModel.DataAnnotations.ValidationAttributeStore.TypeStoreItem.CreatePropertyStoreItems - - - ILLink - IL2080 - member - M:System.ComponentModel.DataAnnotations.ValidationAttribute.SetResourceAccessorByPropertyLookup - - - diff --git a/src/libraries/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj b/src/libraries/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj index 52c3fff0064fd..bc9a4134bad68 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj +++ b/src/libraries/System.ComponentModel.Annotations/src/System.ComponentModel.Annotations.csproj @@ -64,6 +64,7 @@ + diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptor.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptor.cs index 2a6d206859eef..5c49bd6062f71 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/AssociatedMetadataTypeTypeDescriptor.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel.DataAnnotations { - internal class AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor + internal sealed class AssociatedMetadataTypeTypeDescriptor : CustomTypeDescriptor { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private Type AssociatedMetadataType { get; set; } @@ -33,12 +33,13 @@ public AssociatedMetadataTypeTypeDescriptor( } } - [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { return GetPropertiesWithMetadata(base.GetProperties(attributes)); } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] public override PropertyDescriptorCollection GetProperties() { return GetPropertiesWithMetadata(base.GetProperties()); diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CompareAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CompareAttribute.cs index ec7caa4fce68a..cd995d2358fe0 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CompareAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CompareAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; @@ -10,6 +11,7 @@ namespace System.ComponentModel.DataAnnotations [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class CompareAttribute : ValidationAttribute { + [RequiresUnreferencedCode("The property referenced by 'otherProperty' may be trimmed. Ensure it is preserved.")] public CompareAttribute(string otherProperty) : base(SR.CompareAttribute_MustMatch) { OtherProperty = otherProperty ?? throw new ArgumentNullException(nameof(otherProperty)); @@ -25,6 +27,8 @@ public override string FormatErrorMessage(string name) => string.Format( CultureInfo.CurrentCulture, ErrorMessageString, name, OtherPropertyDisplayName ?? OtherProperty); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The ctor is marked with RequiresUnreferencedCode informing the caller to preserve the other property.")] protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { var otherPropertyInfo = validationContext.ObjectType.GetRuntimeProperty(OtherProperty); diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CustomValidationAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CustomValidationAttribute.cs index 67c7077102be9..73684d71a0861 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CustomValidationAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/CustomValidationAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection; @@ -83,7 +84,7 @@ public sealed class CustomValidationAttribute : ValidationAttribute /// . /// /// The name of the method to invoke in . - public CustomValidationAttribute(Type validatorType, string method) + public CustomValidationAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type validatorType, string method) : base(() => SR.CustomValidationAttribute_ValidationError) { ValidatorType = validatorType; @@ -98,6 +99,7 @@ public CustomValidationAttribute(Type validatorType, string method) /// /// Gets the type that contains the validation method identified by . /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] public Type ValidatorType { get; } /// @@ -250,9 +252,8 @@ public override string FormatErrorMessage(string name) } // Named method must be public and static - var methodInfo = ValidatorType.GetRuntimeMethods() - .SingleOrDefault(m => string.Equals(m.Name, Method, StringComparison.Ordinal) - && m.IsPublic && m.IsStatic); + var methodInfo = ValidatorType.GetMethods(BindingFlags.Public | BindingFlags.Static) + .SingleOrDefault(m => string.Equals(m.Name, Method, StringComparison.Ordinal)); if (methodInfo == null) { return SR.Format(SR.CustomValidationAttribute_Method_Not_Found, Method, ValidatorType.Name); diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs index 2d8b11f00b316..e88df448bc758 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace System.ComponentModel.DataAnnotations @@ -25,6 +26,8 @@ public sealed class DisplayAttribute : Attribute private bool? _autoGenerateField; private bool? _autoGenerateFilter; private int? _order; + + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] private Type? _resourceType; #endregion @@ -167,6 +170,7 @@ public string? GroupName /// , , , and /// methods to return localized values. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] public Type? ResourceType { get => _resourceType; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayFormatAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayFormatAttribute.cs index 96a82b04120e0..9bc765089f2e6 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayFormatAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/DisplayFormatAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel.DataAnnotations { /// @@ -72,6 +74,7 @@ public string? NullDisplayText /// Using along with , allows the /// method to return localized values. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] public Type? NullDisplayTextResourceType { get => _nullDisplayText.ResourceType; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/LocalizableString.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/LocalizableString.cs index 5c8dfca6b56d4..31b4998e99f3c 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/LocalizableString.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/LocalizableString.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.ComponentModel.DataAnnotations @@ -10,13 +10,14 @@ namespace System.ComponentModel.DataAnnotations /// A helper class for providing a localizable string property. /// This class is currently compiled in both System.Web.dll and System.ComponentModel.DataAnnotations.dll. /// - internal class LocalizableString + internal sealed class LocalizableString { #region Member fields private readonly string _propertyName; private Func? _cachedResult; private string? _propertyValue; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] private Type? _resourceType; #endregion @@ -62,6 +63,7 @@ public string? Value /// /// Gets or sets the resource type to be used for localization. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] public Type? ResourceType { get => _resourceType; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs index 903112d41fa84..483d76c4872df 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MaxLengthAttribute.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; @@ -24,6 +25,7 @@ public class MaxLengthAttribute : ValidationAttribute /// The maximum allowable length of collection/string data. /// Value must be greater than zero. /// + [RequiresUnreferencedCode(CountPropertyHelper.RequiresUnreferencedCodeMessage)] public MaxLengthAttribute(int length) : base(() => DefaultErrorMessageString) { @@ -34,6 +36,7 @@ public MaxLengthAttribute(int length) /// Initializes a new instance of the class. /// The maximum allowable length supported by the database will be used. /// + [RequiresUnreferencedCode(CountPropertyHelper.RequiresUnreferencedCodeMessage)] public MaxLengthAttribute() : base(() => DefaultErrorMessageString) { @@ -59,6 +62,7 @@ public MaxLengthAttribute() /// true if the value is null or less than or equal to the specified maximum length, otherwise false /// /// Length is zero or less than negative one. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctors are marked with RequiresUnreferencedCode.")] public override bool IsValid(object? value) { // Check the lengths for legality @@ -110,6 +114,9 @@ private void EnsureLegalLengths() internal static class CountPropertyHelper { + internal const string RequiresUnreferencedCodeMessage = "Uses reflection to get the 'Count' property on types that don't implement ICollection. This 'Count' property may be trimmed. Ensure it is preserved."; + + [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] public static bool TryGetCount(object value, out int count) { Debug.Assert(value != null); diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs index 95df5e1f1a82b..f771e0e340d85 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MetadataPropertyDescriptorWrapper.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.DataAnnotations { - internal class MetadataPropertyDescriptorWrapper : PropertyDescriptor + internal sealed class MetadataPropertyDescriptorWrapper : PropertyDescriptor { private readonly PropertyDescriptor _descriptor; private readonly bool _isReadOnly; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs index 7b0e09f659050..8171f650eae89 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/MinLengthAttribute.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace System.ComponentModel.DataAnnotations @@ -19,6 +20,7 @@ public class MinLengthAttribute : ValidationAttribute /// The minimum allowable length of collection/string data. /// Value must be greater than or equal to zero. /// + [RequiresUnreferencedCode(CountPropertyHelper.RequiresUnreferencedCodeMessage)] public MinLengthAttribute(int length) : base(SR.MinLengthAttribute_ValidationError) { @@ -43,6 +45,7 @@ public MinLengthAttribute(int length) /// false /// /// Length is less than zero. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor is marked with RequiresUnreferencedCode.")] public override bool IsValid(object? value) { // Check the lengths for legality diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs index ce76d2481c486..e20ebdbf8b470 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs @@ -46,6 +46,7 @@ public RangeAttribute(double minimum, double maximum) /// The type of the range parameters. Must implement IComparable. /// The minimum allowable value. /// The maximum allowable value. + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] public RangeAttribute( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, string minimum, @@ -204,8 +205,7 @@ private void SetupConversion() comparableType.FullName)); } - // use OperandType here so the trimmer doesn't warn about the 'type' field on a compiler generated type - TypeConverter converter = TypeDescriptor.GetConverter(OperandType); + TypeConverter converter = GetOperandTypeConverter(); IComparable min = (IComparable)(ParseLimitsInInvariantCulture ? converter.ConvertFromInvariantString((string)minimum) : converter.ConvertFromString((string)minimum)); @@ -229,5 +229,10 @@ private void SetupConversion() } } } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor that allows this code to be called is marked with RequiresUnreferencedCode.")] + private TypeConverter GetOperandTypeConverter() => + TypeDescriptor.GetConverter(OperandType); } } diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs index 4b4819886fd16..b97ad7b7c6550 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/UIHintAttribute.cs @@ -68,7 +68,7 @@ public UIHintAttribute(string uiHint, string? presentationLayer, params object?[ public override bool Equals(object? obj) => obj is UIHintAttribute otherAttribute && _implementation.Equals(otherAttribute._implementation); - internal class UIHintImplementation + internal sealed class UIHintImplementation { private readonly object?[]? _inputControlParameters; private IDictionary? _controlParameters; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs index eedd0de4bf1a4..2202c0d8ddf67 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttribute.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; @@ -25,6 +26,7 @@ public abstract class ValidationAttribute : Attribute private string? _errorMessage; private Func? _errorMessageResourceAccessor; private string? _errorMessageResourceName; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] private Type? _errorMessageResourceType; private volatile bool _hasBaseIsValid; private string? _defaultErrorMessage; @@ -175,6 +177,7 @@ public string? ErrorMessageResourceName /// Use instead of this pair if error messages are not localized. /// /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public Type? ErrorMessageResourceType { get => _errorMessageResourceType; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs index db3b57bd0be18..2f7b02543f0b8 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationAttributeStore.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel.DataAnnotations /// It exists both to help performance as well as to abstract away the differences between /// Reflection and TypeDescriptor. /// - internal class ValidationAttributeStore + internal sealed class ValidationAttributeStore { private readonly Dictionary _typeStoreItems = new Dictionary(); @@ -32,6 +32,7 @@ internal class ValidationAttributeStore /// /// The context that describes the type. It cannot be null. /// The collection of validation attributes. It could be empty. + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] internal IEnumerable GetTypeValidationAttributes(ValidationContext validationContext) { EnsureValidationContext(validationContext); @@ -44,6 +45,7 @@ internal IEnumerable GetTypeValidationAttributes(Validation /// /// The context that describes the type. It cannot be null. /// The display attribute instance, if present. + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] internal DisplayAttribute? GetTypeDisplayAttribute(ValidationContext validationContext) { EnsureValidationContext(validationContext); @@ -56,6 +58,7 @@ internal IEnumerable GetTypeValidationAttributes(Validation /// /// The context that describes the property. It cannot be null. /// The collection of validation attributes. It could be empty. + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] internal IEnumerable GetPropertyValidationAttributes(ValidationContext validationContext) { EnsureValidationContext(validationContext); @@ -69,6 +72,7 @@ internal IEnumerable GetPropertyValidationAttributes(Valida /// /// The context that describes the property. It cannot be null. /// The display attribute instance, if present. + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] internal DisplayAttribute? GetPropertyDisplayAttribute(ValidationContext validationContext) { EnsureValidationContext(validationContext); @@ -82,6 +86,7 @@ internal IEnumerable GetPropertyValidationAttributes(Valida /// /// The context that describes the property. It cannot be null. /// The type of the specified property + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] internal Type GetPropertyType(ValidationContext validationContext) { EnsureValidationContext(validationContext); @@ -97,6 +102,7 @@ internal Type GetPropertyType(ValidationContext validationContext) /// /// The to check. /// true when the represents a property, false otherwise. + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] internal bool IsPropertyContext(ValidationContext validationContext) { EnsureValidationContext(validationContext); @@ -110,7 +116,7 @@ internal bool IsPropertyContext(ValidationContext validationContext) /// /// The type whose store item is needed. It cannot be null /// The type store item. It will not be null. - private TypeStoreItem GetTypeStoreItem(Type type) + private TypeStoreItem GetTypeStoreItem([DynamicallyAccessedMembers(TypeStoreItem.DynamicallyAccessedTypes)] Type type) { Debug.Assert(type != null); @@ -165,13 +171,16 @@ internal StoreItem(IEnumerable attributes) /// /// Private class to store data associated with a type /// - private class TypeStoreItem : StoreItem + private sealed class TypeStoreItem : StoreItem { + internal const DynamicallyAccessedMemberTypes DynamicallyAccessedTypes = DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties; + private readonly object _syncRoot = new object(); + [DynamicallyAccessedMembers(DynamicallyAccessedTypes)] private readonly Type _type; private Dictionary? _propertyStoreItems; - internal TypeStoreItem(Type type, IEnumerable attributes) + internal TypeStoreItem([DynamicallyAccessedMembers(DynamicallyAccessedTypes)] Type type, IEnumerable attributes) : base(attributes) { _type = type; @@ -231,7 +240,7 @@ private Dictionary CreatePropertyStoreItems() /// /// Private class to store data associated with a property /// - private class PropertyStoreItem : StoreItem + private sealed class PropertyStoreItem : StoreItem { internal PropertyStoreItem(Type propertyType, IEnumerable attributes) : base(attributes) diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs index f12578ac9d6ec..836bf43ac9f77 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/ValidationContext.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.ComponentModel.DataAnnotations { @@ -26,6 +27,8 @@ public sealed class ValidationContext // Also we use this ability in Validator.CreateValidationContext()?? : IServiceProvider { + internal const string InstanceTypeNotStaticallyDiscovered = "The Type of instance cannot be statically discovered."; + #region Member Fields private readonly Dictionary _items; @@ -41,6 +44,7 @@ public sealed class ValidationContext /// /// The object instance being validated. It cannot be null. /// When is null + [RequiresUnreferencedCode(InstanceTypeNotStaticallyDiscovered)] public ValidationContext(object instance) : this(instance, null, null) { @@ -57,6 +61,7 @@ public ValidationContext(object instance) /// new dictionary, preventing consumers from modifying the original dictionary. /// /// When is null + [RequiresUnreferencedCode(InstanceTypeNotStaticallyDiscovered)] public ValidationContext(object instance, IDictionary? items) : this(instance, null, items) { @@ -78,6 +83,7 @@ public ValidationContext(object instance, IDictionary? items) /// new dictionary, preventing consumers from modifying the original dictionary. /// /// When is null + [RequiresUnreferencedCode(InstanceTypeNotStaticallyDiscovered)] public ValidationContext(object instance, IServiceProvider? serviceProvider, IDictionary? items) { if (instance == null) @@ -87,7 +93,8 @@ public ValidationContext(object instance, IServiceProvider? serviceProvider, IDi if (serviceProvider != null) { - InitializeServiceProvider(serviceType => serviceProvider.GetService(serviceType)); + IServiceProvider localServiceProvider = serviceProvider; + InitializeServiceProvider(serviceType => localServiceProvider.GetService(serviceType)); } _items = items != null ? new Dictionary(items) : new Dictionary(); @@ -174,6 +181,7 @@ public string DisplayName /// Looks up the display name using the DisplayAttribute attached to the respective type or property. /// /// A display-friendly name of the member represented by the . + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctors are marked with RequiresUnreferencedCode.")] private string? GetDisplayName() { string? displayName = null; diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs index 1363b58d1540c..d9a874eb1dba7 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/Validator.cs @@ -47,6 +47,7 @@ public static class Validator /// When the of is not a valid /// property. /// + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] public static bool TryValidateProperty(object? value, ValidationContext validationContext, ICollection? validationResults) { @@ -92,6 +93,7 @@ public static bool TryValidateProperty(object? value, ValidationContext validati /// When doesn't match the /// on . /// + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] public static bool TryValidateObject( object instance, ValidationContext validationContext, ICollection? validationResults) => TryValidateObject(instance, validationContext, validationResults, false /*validateAllProperties*/); @@ -129,6 +131,7 @@ public static bool TryValidateObject( /// When doesn't match the /// on . /// + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] public static bool TryValidateObject(object instance, ValidationContext validationContext, ICollection? validationResults, bool validateAllProperties) { @@ -212,6 +215,7 @@ var err in /// /// When is null. /// When is invalid for this property. + [RequiresUnreferencedCode("The Type of validationContext.ObjectType cannot be statically discovered.")] public static void ValidateProperty(object? value, ValidationContext validationContext) { // Throw if value cannot be assigned to this property. That is not a validation exception. @@ -242,6 +246,7 @@ public static void ValidateProperty(object? value, ValidationContext validationC /// on . /// /// When is found to be invalid. + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] public static void ValidateObject(object instance, ValidationContext validationContext) { ValidateObject(instance, validationContext, false /*validateAllProperties*/); @@ -267,6 +272,7 @@ public static void ValidateObject(object instance, ValidationContext validationC /// on . /// /// When is found to be invalid. + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] public static void ValidateObject(object instance, ValidationContext validationContext, bool validateAllProperties) { @@ -325,6 +331,7 @@ public static void ValidateValue(object value, ValidationContext validationConte /// /// A new for the provided. /// When is null. + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] private static ValidationContext CreateValidationContext(object instance, ValidationContext validationContext) { Debug.Assert(validationContext != null); @@ -391,6 +398,7 @@ private static void EnsureValidPropertyType(string propertyName, Type propertyTy /// When doesn't match the /// on . /// + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] private static IEnumerable GetObjectValidationErrors(object instance, ValidationContext validationContext, bool validateAllProperties, bool breakOnFirstError) { @@ -450,6 +458,7 @@ private static IEnumerable GetObjectValidationErrors(object ins /// /// Whether to break on the first error or validate everything. /// A list of instances. + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] private static IEnumerable GetObjectPropertyValidationErrors(object instance, ValidationContext validationContext, bool validateAllProperties, bool breakOnFirstError) { @@ -501,6 +510,7 @@ private static IEnumerable GetObjectPropertyValidationErrors(ob /// value. /// /// Ignores indexed properties. + [RequiresUnreferencedCode(ValidationContext.InstanceTypeNotStaticallyDiscovered)] private static ICollection> GetPropertyValues(object instance, ValidationContext validationContext) { @@ -613,7 +623,7 @@ private static bool TryValidate(object? value, ValidationContext validationConte /// Private helper class to encapsulate a ValidationAttribute with the failed value and the user-visible /// target name against which it was validated. /// - private class ValidationError + private sealed class ValidationError { private readonly object? _value; private readonly ValidationAttribute? _validationAttribute; diff --git a/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/PartBuilderOfT.cs b/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/PartBuilderOfT.cs index 9fbc89b096b53..a85c31ef87f60 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/PartBuilderOfT.cs +++ b/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/PartBuilderOfT.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.Registration { public class PartBuilder : PartBuilder { - private class PropertyExpressionAdapter + private sealed class PropertyExpressionAdapter { private readonly PropertyInfo _propertyInfo; private readonly Action _configureImport; @@ -58,7 +58,7 @@ private static PropertyInfo SelectProperties(Expression> propert throw new ArgumentException(SR.Format(SR.Argument_ExpressionMustBePropertyMember, nameof(propertyFilter)), nameof(propertyFilter)); } - protected static Expression> Reduce(Expression> expr) + private static Expression> Reduce(Expression> expr) { while (expr.CanReduce) { @@ -68,7 +68,7 @@ protected static Expression> Reduce(Expression> } } - private class ConstructorExpressionAdapter + private sealed class ConstructorExpressionAdapter { private ConstructorInfo _constructorInfo; private Dictionary> _importBuilders; diff --git a/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/RegistrationBuilder.cs b/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/RegistrationBuilder.cs index 3b3c31d2dd712..9ed87a0ff35e2 100644 --- a/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/RegistrationBuilder.cs +++ b/src/libraries/System.ComponentModel.Composition.Registration/src/System/ComponentModel/Composition/Registration/RegistrationBuilder.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel.Composition.Registration { public class RegistrationBuilder : CustomReflectionContext { - internal class InnerRC : ReflectionContext + internal sealed class InnerRC : ReflectionContext { public override TypeInfo MapType(TypeInfo t) { return t; } public override Assembly MapAssembly(Assembly a) { return a; } diff --git a/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs b/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs index 4a2e6838843f7..55249fc22f7b9 100644 --- a/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs +++ b/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/CollectionServices.CollectionOfObject.cs @@ -41,7 +41,7 @@ public static ICollection GetCollectionWrapper(Type itemType, object col return (ICollection)Activator.CreateInstance(collectionType, collectionObject)!; } - private class CollectionOfObjectList : ICollection + private sealed class CollectionOfObjectList : ICollection { private readonly IList _list; @@ -96,7 +96,7 @@ IEnumerator IEnumerable.GetEnumerator() } } - private class CollectionOfObject : ICollection + private sealed class CollectionOfObject : ICollection { private readonly ICollection _collectionOfT; diff --git a/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs b/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs index e3992eb89afff..bd5cc2b81ade3 100644 --- a/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs +++ b/src/libraries/System.ComponentModel.Composition/src/Microsoft/Internal/Collections/WeakReferenceCollection.cs @@ -6,7 +6,7 @@ namespace Microsoft.Internal.Collections { - internal class WeakReferenceCollection where T : class + internal sealed class WeakReferenceCollection where T : class { private readonly List _items = new List(); diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs index deaedb46ed888..b37ca10ac9ed5 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedExportDefinition.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.AttributedModel { - internal class AttributedExportDefinition : ExportDefinition + internal sealed class AttributedExportDefinition : ExportDefinition { private readonly AttributedPartCreationInfo _partCreationInfo; private readonly MemberInfo _member; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs index af82945c98b6c..e59154c185a5e 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/AttributedModel/AttributedPartCreationInfo.cs @@ -13,7 +13,7 @@ namespace System.ComponentModel.Composition.AttributedModel { - internal class AttributedPartCreationInfo : IReflectionPartCreationInfo + internal sealed class AttributedPartCreationInfo : IReflectionPartCreationInfo { private readonly Type _type; private readonly bool _ignoreConstructorImports; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs index 8cefa34a587c8..5c35b8cd5160e 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionErrorDebuggerProxy.cs @@ -6,7 +6,7 @@ namespace System.ComponentModel.Composition { - internal class CompositionErrorDebuggerProxy + internal sealed class CompositionErrorDebuggerProxy { private readonly CompositionError _error; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs index b06aff734a302..896903c51263f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/CompositionExceptionDebuggerProxy.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition { - internal class CompositionExceptionDebuggerProxy + internal sealed class CompositionExceptionDebuggerProxy { private readonly CompositionException _exception; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs index 09470da51de2f..1f9c8004676f7 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ExportServices.DisposableLazy.cs @@ -5,7 +5,7 @@ namespace System.ComponentModel.Composition { - internal partial class ExportServices + internal static partial class ExportServices { private sealed class DisposableLazy : Lazy, IDisposable { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs index 2443d92f3048f..bb70de3f475b2 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ApplicationCatalog.cs @@ -173,14 +173,8 @@ private void ThrowIfDisposed() } } - private string GetDisplayName() - { - return string.Format(CultureInfo.CurrentCulture, - "{0} (Path=\"{1}\") (PrivateProbingPath=\"{2}\")", // NOLOC - GetType().Name, - AppDomain.CurrentDomain.BaseDirectory, - AppDomain.CurrentDomain.RelativeSearchPath); - } + private string GetDisplayName() => + $"{GetType().Name} (Path=\"{AppDomain.CurrentDomain.BaseDirectory}\") (PrivateProbingPath=\"{AppDomain.CurrentDomain.RelativeSearchPath}\")"; // NOLOC /// /// Returns a string representation of the directory catalog. diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs index 026b2df76de5e..338661efe93db 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs @@ -540,13 +540,8 @@ private void ThrowIfDisposed() } } - private string GetDisplayName() - { - return string.Format(CultureInfo.CurrentCulture, - "{0} (Assembly=\"{1}\")", // NOLOC - GetType().Name, - Assembly.FullName); - } + private string GetDisplayName() => + $"{GetType().Name} (Assembly=\"{Assembly.FullName}\")"; // NOLOC private static Assembly LoadAssembly(string codeBase) { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs index 653bdfd4b043b..6075092aa2e40 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalogDebuggerProxy.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.Hosting { - internal class AssemblyCatalogDebuggerProxy + internal sealed class AssemblyCatalogDebuggerProxy { private readonly AssemblyCatalog _catalog; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs index bf091cbfbcf0b..0afb1b0fd5a66 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.CatalogChangeProxy.cs @@ -10,7 +10,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class CatalogExportProvider : ExportProvider, IDisposable { - private class CatalogChangeProxy : ComposablePartCatalog + private sealed class CatalogChangeProxy : ComposablePartCatalog { private readonly ComposablePartCatalog _originalCatalog; private readonly List _addedParts; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs index da5100beaf70e..3b785e684af28 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.FactoryExport.cs @@ -56,7 +56,7 @@ protected ExportDefinition UnderlyingExportDefinition public abstract Export CreateExportProduct(); - private class FactoryExportPartDefinition : ComposablePartDefinition + private sealed class FactoryExportPartDefinition : ComposablePartDefinition { private readonly FactoryExport _FactoryExport; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs index 570521d988830..db75263f7c974 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.PartCreatorExport.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class CatalogExportProvider { - internal class PartCreatorExport : FactoryExport + internal sealed class PartCreatorExport : FactoryExport { private readonly CatalogExportProvider _catalogExportProvider; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs index 62164694a1dd7..5f49441b566af 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeFactoryExport.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class CatalogExportProvider { - internal class ScopeFactoryExport : FactoryExport + internal sealed class ScopeFactoryExport : FactoryExport { private readonly ScopeManager _scopeManager; private readonly CompositionScopeDefinition _catalog; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.cs index 6c421fcb5a35d..c10f2e48d045a 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.ScopeManager.cs @@ -8,7 +8,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class CatalogExportProvider { - internal class ScopeManager : ExportProvider + internal sealed class ScopeManager : ExportProvider { private readonly CompositionScopeDefinition _scopeDefinition; private readonly CatalogExportProvider _catalogExportProvider; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs index 74132a63a2202..b981f8cd8a7e1 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CatalogExportProvider.cs @@ -18,7 +18,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class CatalogExportProvider : ExportProvider, IDisposable { - private class InnerCatalogExportProvider : ExportProvider + private sealed class InnerCatalogExportProvider : ExportProvider { private readonly CatalogExportProvider _outerExportProvider; @@ -1012,7 +1012,7 @@ public AtomicCompositionQueryState GetQueryState(ComposablePartDefinition defini } } - private class PartEqualsQueryStateNode : PartQueryStateNode + private sealed class PartEqualsQueryStateNode : PartQueryStateNode { private readonly ComposablePartDefinition _part; private readonly int _hashCode; @@ -1033,7 +1033,7 @@ protected override bool IsMatchingDefinition(ComposablePartDefinition part, int } } - private class PartInHashSetQueryStateNode : PartQueryStateNode + private sealed class PartInHashSetQueryStateNode : PartQueryStateNode { private readonly HashSet _parts; public PartInHashSetQueryStateNode(HashSet parts, PartQueryStateNode? previousNode, AtomicCompositionQueryState state) : @@ -1048,7 +1048,7 @@ protected override bool IsMatchingDefinition(ComposablePartDefinition part, int } } - private class CatalogPart + private sealed class CatalogPart { private volatile bool _importsSatisfied; public CatalogPart(ComposablePart part) diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs index f6d9da22f1841..d084df496f51b 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ComposablePartCatalogCollection.cs @@ -17,7 +17,7 @@ namespace System.ComponentModel.Composition.Hosting /// It is threadsafe, notifications are not marshalled using a SynchronizationContext. /// It is Disposable. /// - internal class ComposablePartCatalogCollection : ICollection, INotifyComposablePartCatalogChanged, IDisposable + internal sealed class ComposablePartCatalogCollection : ICollection, INotifyComposablePartCatalogChanged, IDisposable { private readonly Lock _lock = new Lock(); private readonly Action? _onChanged; @@ -251,7 +251,7 @@ public void Dispose() GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposing) { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs index 8e6944034a803..e2f8250d30479 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionBatch.SingleExportComposablePart.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel.Composition.Hosting public partial class CompositionBatch { // Represents a part that exports a single export - private class SingleExportComposablePart : ComposablePart + private sealed class SingleExportComposablePart : ComposablePart { private readonly Export _export; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs index 755a334349ca6..afdc59810599e 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionContainer.CompositionServiceShim.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class CompositionContainer { - private class CompositionServiceShim : ICompositionService + private sealed class CompositionServiceShim : ICompositionService { private readonly CompositionContainer _innerContainer; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs index 9329315fe8332..00c5909b16dab 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionScopeDefinitionDebuggerProxy.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel.Composition.Hosting { // This proxy is needed to pretty up CompositionScopeDefinitionCatalog.Parts; IQueryable // instances are not displayed in a very friendly way in the debugger. - internal class CompositionScopeDefinitionDebuggerProxy + internal sealed class CompositionScopeDefinitionDebuggerProxy { private readonly CompositionScopeDefinition _compositionScopeDefinition; @@ -35,7 +35,7 @@ public IEnumerable PublicSurface } } - public virtual IEnumerable Children + public IEnumerable Children { get { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs index 5fe99342cf96e..3df3463c0b211 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/CompositionServices.cs @@ -367,7 +367,7 @@ private static bool TryContributeMetadataValue(this IDictionary return true; } - private class MetadataList + private sealed class MetadataList { private Type? _arrayType; private bool _containsNulls; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs index 072072f0947ac..3f16776afd9c5 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/DirectoryCatalog.DirectoryCatalogDebuggerProxy.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class DirectoryCatalog { - internal class DirectoryCatalogDebuggerProxy + internal sealed class DirectoryCatalogDebuggerProxy { private readonly DirectoryCatalog _catalog; 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 7088f00aba17f..2b753c150aab0 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 @@ -732,13 +732,8 @@ private void DiffChanges(string[] beforeFiles, string[] afterFiles, } } - private string GetDisplayName() - { - return string.Format(CultureInfo.CurrentCulture, - "{0} (Path=\"{1}\")", // NOLOC - GetType().Name, - _path); - } + private string GetDisplayName() => + $"{GetType().Name} (Path=\"{_path}\")"; // NOLOC private string[] GetFiles() { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs index 9f3b0a41836da..4c991d6542de1 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependenciesTraversal.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel.Composition.Hosting { public partial class FilteredCatalog { - internal class DependenciesTraversal : IComposablePartCatalogTraversal + internal sealed class DependenciesTraversal : IComposablePartCatalogTraversal { private readonly IEnumerable _parts; private readonly Func _importFilter; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs index bb80dafa4026b..b949ec434338b 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/FilteredCatalog.DependentsTraversal.cs @@ -17,7 +17,7 @@ public partial class FilteredCatalog /// if the chains of dependecies are rather small. To achieve that we do a very minimal structure prep upfront - merely creating a contract-based /// index of imports - and the verify the full match of imports during the traversal. Given that most parts have a very few imports this should perform well. /// - internal class DependentsTraversal : IComposablePartCatalogTraversal + internal sealed class DependentsTraversal : IComposablePartCatalogTraversal { private readonly IEnumerable _parts; private readonly Func _importFilter; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs index ccbd268ab29cb..9bd187f889958 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.EngineContext.cs @@ -12,7 +12,7 @@ public partial class ImportEngine /// Used to wrap the start and stop of enforcing export changes don't /// break required imports. This context is stored in a AtomicComposition. /// - private class EngineContext + private sealed class EngineContext { private readonly ImportEngine _importEngine; private readonly List _addedPartManagers = new List(); diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs index 68e91edada55c..ad16329c16110 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.PartManager.cs @@ -15,7 +15,7 @@ public partial class ImportEngine /// It stores things like the list of disposable exports used to satisfy the imports as /// well as the caching of the exports discovered during previewing of a part. /// - private class PartManager + private sealed class PartManager { private Dictionary>? _importedDisposableExports; private Dictionary? _importCache; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.cs index 3d1b942eb24d9..3bb145831b606 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportEngine.RecompositionManager.cs @@ -15,7 +15,7 @@ public partial class ImportEngine /// that will be affected by changes to exports. This allows the to properly /// block breaking changes and also recompose imports as appropriate. /// - private class RecompositionManager + private sealed class RecompositionManager { private readonly WeakReferenceCollection _partsToIndex = new WeakReferenceCollection(); private readonly WeakReferenceCollection _partsToUnindex = new WeakReferenceCollection(); diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs index 825da007d8520..17194f7b9bf6f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/ImportSourceImportDefinitionHelpers.cs @@ -25,7 +25,7 @@ public static ImportDefinition RemoveImportSource(this ImportDefinition definiti } } - internal class NonImportSourceImportDefinition : ContractBasedImportDefinition + internal sealed class NonImportSourceImportDefinition : ContractBasedImportDefinition { private readonly ContractBasedImportDefinition _sourceDefinition; private IDictionary? _metadata; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs index 1c407a72bee54..bd962545c8823 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ImportCardinalityMismatchExceptionDebuggerProxy.cs @@ -5,7 +5,7 @@ namespace System.ComponentModel.Composition { - internal class ImportCardinalityMismatchExceptionDebuggerProxy + internal sealed class ImportCardinalityMismatchExceptionDebuggerProxy { private readonly ImportCardinalityMismatchException _exception; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs index 18c0252279330..b56a1695b12d7 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/MetadataViewGenerator.cs @@ -67,7 +67,7 @@ internal static class MetadataViewGenerator private static readonly Lock _lock = new Lock(); private static readonly Dictionary _metadataViewFactories = new Dictionary(); - private static readonly AssemblyName ProxyAssemblyName = new AssemblyName(string.Format(CultureInfo.InvariantCulture, "MetadataViewProxies_{0}", Guid.NewGuid())); + private static readonly AssemblyName ProxyAssemblyName = new AssemblyName($"MetadataViewProxies_{Guid.NewGuid()}"); private static ModuleBuilder? transparentProxyModuleBuilder; private static readonly Type[] CtorArgumentTypes = new Type[] { typeof(IDictionary) }; @@ -193,7 +193,7 @@ private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBu var proxyModuleBuilder = GetProxyModuleBuilder(requiresCritical); proxyTypeBuilder = proxyModuleBuilder.DefineType( - string.Format(CultureInfo.InvariantCulture, "_proxy_{0}_{1}", viewType.FullName, Guid.NewGuid()), + $"_proxy_{viewType.FullName}_{Guid.NewGuid()}", TypeAttributes.Public, typeof(object), interfaces); @@ -214,7 +214,7 @@ private static void GenerateLocalAssignmentFromFlag(this ILGenerator IL, LocalBu // Implement interface properties foreach (PropertyInfo propertyInfo in viewType.GetAllProperties()) { - string fieldName = string.Format(CultureInfo.InvariantCulture, "_{0}_{1}", propertyInfo.Name, Guid.NewGuid()); + string fieldName = $"_{propertyInfo.Name}_{Guid.NewGuid()}"; // Cache names and type for exception string propertyName = propertyInfo.Name; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs index 7b11ce0ee5abf..564009e41c424 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartCatalogDebuggerProxy.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.Primitives { // This proxy is needed to pretty up ComposablePartCatalog.Parts; IQueryable // instances are not displayed in a very friendly way in the debugger. - internal class ComposablePartCatalogDebuggerProxy + internal sealed class ComposablePartCatalogDebuggerProxy { private readonly ComposablePartCatalog _catalog; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs index c39090c7a652b..e1a7ed05dc83f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ComposablePartExceptionDebuggerProxy.cs @@ -5,7 +5,7 @@ namespace System.ComponentModel.Composition.Primitives { - internal class ComposablePartExceptionDebuggerProxy + internal sealed class ComposablePartExceptionDebuggerProxy { private readonly ComposablePartException _exception; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs index 4a763f651c2dc..b9d01b4a7ca2e 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElement.cs @@ -8,7 +8,7 @@ namespace System.ComponentModel.Composition.Primitives // Represents the ICompositionElement placeholder for an // object that does not implement ICompositionElement [DebuggerTypeProxy(typeof(CompositionElementDebuggerProxy))] - internal class CompositionElement : ICompositionElement + internal sealed class CompositionElement : ICompositionElement { private readonly string _displayName; private readonly ICompositionElement? _origin; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs index 3f5de88c78990..2eb059a51931a 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/CompositionElementDebuggerProxy.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.Primitives // when viewing CompositionError.Element in the watch and data tips windows, we // need this proxy so that the underlying object wrapped by the CompositionElement // placeholder is displayed by default. - internal class CompositionElementDebuggerProxy + internal sealed class CompositionElementDebuggerProxy { private readonly CompositionElement _element; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs index d24699eb664cc..e68780fa14251 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/ContractBasedImportDefinition.cs @@ -371,11 +371,11 @@ public override string ToString() { var sb = new StringBuilder(); - sb.Append(string.Format("\n\tContractName\t{0}", ContractName)); - sb.Append(string.Format("\n\tRequiredTypeIdentity\t{0}", RequiredTypeIdentity)); + sb.Append("\n\tContractName\t").Append(ContractName); + sb.Append("\n\tRequiredTypeIdentity\t").Append(RequiredTypeIdentity); if (_requiredCreationPolicy != CreationPolicy.Any) { - sb.Append(string.Format("\n\tRequiredCreationPolicy\t{0}", RequiredCreationPolicy)); + sb.Append("\n\tRequiredCreationPolicy\t").Append(RequiredCreationPolicy); } if (_requiredMetadata.Any()) @@ -383,7 +383,7 @@ public override string ToString() sb.Append("\n\tRequiredMetadata"); foreach (KeyValuePair metadataItem in _requiredMetadata) { - sb.Append(string.Format("\n\t\t{0}\t({1})", metadataItem.Key, metadataItem.Value)); + sb.Append("\n\t\t").Append(metadataItem.Key).Append("\t(").Append(metadataItem.Value).Append(')'); } } return sb.ToString(); diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/SerializableCompositionElement.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/SerializableCompositionElement.cs index 7e5fd59a6cbb0..cbf531b3bf638 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/SerializableCompositionElement.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Primitives/SerializableCompositionElement.cs @@ -10,7 +10,7 @@ namespace System.ComponentModel.Composition.Primitives { [Serializable] - internal class SerializableCompositionElement : ICompositionElement + internal sealed class SerializableCompositionElement : ICompositionElement { private readonly string _displayName; private readonly ICompositionElement? _origin; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs index 4d24c8f532868..669977a0c1ea1 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportfactoryCreator.LifetimeContext.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { internal sealed partial class ExportFactoryCreator { - private class LifetimeContext + private sealed class LifetimeContext { public Tuple GetExportLifetimeContextFromExport(Export export) { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs index bfd87de6789b3..a0a1d9f38e914 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ExportingMember.cs @@ -8,7 +8,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ExportingMember + internal sealed class ExportingMember { private readonly ExportDefinition _definition; private readonly ReflectionMember _member; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs index 9a12670095cb0..d0e4ac2324edc 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/GenericSpecializationPartCreationInfo.cs @@ -13,7 +13,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class GenericSpecializationPartCreationInfo : IReflectionPartCreationInfo + internal sealed class GenericSpecializationPartCreationInfo : IReflectionPartCreationInfo { private readonly IReflectionPartCreationInfo _originalPartCreationInfo; private readonly ReflectionComposablePartDefinition _originalPart; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs index 18edae8dc4f46..cb4ea1ce43b35 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportType.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { // Describes the import type of a Reflection-based import definition - internal class ImportType + internal sealed class ImportType { private static readonly Type LazyOfTType = typeof(Lazy<>); private static readonly Type LazyOfTMType = typeof(Lazy<,>); diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs index b4ba138c577d8..b0c424d085565 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingMember.cs @@ -12,7 +12,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ImportingMember : ImportingItem + internal sealed class ImportingMember : ImportingItem { private readonly ReflectionWritableMember _member; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs index acdf1eee3b726..0ba911ffbbea9 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ImportingParameter.cs @@ -5,7 +5,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ImportingParameter : ImportingItem + internal sealed class ImportingParameter : ImportingItem { public ImportingParameter(ContractBasedImportDefinition definition, ImportType importType) : base(definition, importType) diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs index 6896cba737dbd..244eccf3df3ed 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorExportDefinition.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class PartCreatorExportDefinition : ExportDefinition + internal sealed class PartCreatorExportDefinition : ExportDefinition { private readonly ExportDefinition _productDefinition; private IDictionary? _metadata; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs index 411975988ebef..483f6bf6f0a37 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorMemberImportDefinition.cs @@ -8,7 +8,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class PartCreatorMemberImportDefinition : ReflectionMemberImportDefinition, IPartCreatorImportDefinition + internal sealed class PartCreatorMemberImportDefinition : ReflectionMemberImportDefinition, IPartCreatorImportDefinition { private readonly ContractBasedImportDefinition _productImportDefinition; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs index 88f5352545dfb..09452db87569f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/PartCreatorParameterImportDefinition.cs @@ -9,7 +9,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class PartCreatorParameterImportDefinition : ReflectionParameterImportDefinition, IPartCreatorImportDefinition + internal sealed class PartCreatorParameterImportDefinition : ReflectionParameterImportDefinition, IPartCreatorImportDefinition { private readonly ContractBasedImportDefinition _productImportDefinition; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs index f8e027358a245..7628378dc5f51 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionComposablePartDefinition.cs @@ -11,7 +11,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ReflectionComposablePartDefinition : ComposablePartDefinition, ICompositionElement + internal sealed class ReflectionComposablePartDefinition : ComposablePartDefinition, ICompositionElement { private readonly IReflectionPartCreationInfo _creationInfo; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs index 8a61292201386..544227712ac35 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionField.cs @@ -6,7 +6,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ReflectionField : ReflectionWritableMember + internal sealed class ReflectionField : ReflectionWritableMember { private readonly FieldInfo _field; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs index 3fd081422962a..0bf6f7f6b2cb4 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberExportDefinition.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ReflectionMemberExportDefinition : ExportDefinition, ICompositionElement + internal sealed class ReflectionMemberExportDefinition : ExportDefinition, ICompositionElement { private readonly LazyMemberInfo _member; private readonly ExportDefinition _exportDefinition; @@ -78,12 +78,7 @@ private ReflectionMember ToReflectionMember() return ExportingLazyMember.ToReflectionMember(); } - private string GetDisplayName() - { - return string.Format(CultureInfo.CurrentCulture, - "{0} (ContractName=\"{1}\")", // NOLOC - ToReflectionMember().GetDisplayName(), - ContractName); - } + private string GetDisplayName() => + $"{ToReflectionMember().GetDisplayName()} (ContractName=\"{ContractName}\")"; // NOLOC } } diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs index f9e437f05a4ab..20657c1d48937 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMemberImportDefinition.cs @@ -43,13 +43,7 @@ public LazyMemberInfo ImportingLazyMember get { return _importingLazyMember; } } - protected override string GetDisplayName() - { - return string.Format( - CultureInfo.CurrentCulture, - "{0} (ContractName=\"{1}\")", // NOLOC - ImportingLazyMember.ToReflectionMember().GetDisplayName(), - ContractName); - } + protected override string GetDisplayName() => + $"{ImportingLazyMember.ToReflectionMember().GetDisplayName()} (ContractName=\"{ContractName}\")"; // NOLOC } } diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs index d27d7d4f28970..a869d34e539ce 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionMethod.cs @@ -6,7 +6,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal partial class ReflectionMethod : ReflectionMember + internal sealed partial class ReflectionMethod : ReflectionMember { private readonly MethodInfo _method; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs index c7a47b3f65fc0..9fabe855f0911 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionModelServices.cs @@ -314,7 +314,7 @@ public static bool TryMakeGenericPartDefinition(ComposablePartDefinition partDef } } - internal class ReflectionPartCreationInfo : IReflectionPartCreationInfo + internal sealed class ReflectionPartCreationInfo : IReflectionPartCreationInfo { private readonly Lazy _partType; private readonly Lazy>? _imports; @@ -463,7 +463,7 @@ public ICompositionElement? Origin } } - internal class LazyExportDefinition : ExportDefinition + internal sealed class LazyExportDefinition : ExportDefinition { private readonly Lazy> _metadata; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs index 69ef96e5dd66e..8124cfdf05b5f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameter.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ReflectionParameter : ReflectionItem + internal sealed class ReflectionParameter : ReflectionItem { private readonly ParameterInfo _parameter; @@ -31,14 +31,8 @@ public override string? Name get { return UnderlyingParameter.Name; } } - public override string GetDisplayName() - { - return string.Format( - CultureInfo.CurrentCulture, - "{0} (Parameter=\"{1}\")", // NOLOC - UnderlyingParameter.Member.GetDisplayName(), - UnderlyingParameter.Name); - } + public override string GetDisplayName() => + $"{UnderlyingParameter.Member.GetDisplayName()} (Parameter=\"{UnderlyingParameter.Name}\")"; // NOLOC public override Type ReturnType { diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs index 5808012f63991..0eb181feb9c26 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionParameterImportDefinition.cs @@ -45,12 +45,7 @@ public Lazy ImportingLazyParameter protected override string GetDisplayName() { ParameterInfo parameter = ImportingLazyParameter.GetNotNullValue("parameter"); - return string.Format( - CultureInfo.CurrentCulture, - "{0} (Parameter=\"{1}\", ContractName=\"{2}\")", // NOLOC - parameter.Member.GetDisplayName(), - parameter.Name, - ContractName); + return $"{parameter.Member.GetDisplayName()} (Parameter=\"{parameter.Name}\", ContractName=\"{ContractName}\")"; // NOLOC } } } diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.cs index 78e4f47873592..c47bb33f17dd0 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionProperty.cs @@ -14,7 +14,7 @@ namespace System.ComponentModel.Composition.ReflectionModel // you to go from a metadata token -> PropertyInfo like it does with types, // fields, and methods. - internal class ReflectionProperty : ReflectionWritableMember + internal sealed class ReflectionProperty : ReflectionWritableMember { private readonly MethodInfo? _getMethod; private readonly MethodInfo? _setMethod; diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs index aa1c154a99299..d06d87eb2525f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/ReflectionModel/ReflectionType.cs @@ -5,7 +5,7 @@ namespace System.ComponentModel.Composition.ReflectionModel { - internal class ReflectionType : ReflectionMember + internal sealed class ReflectionType : ReflectionMember { private readonly Type _type; diff --git a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs index 93432aa8789fe..9fa75345a1534 100644 --- a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs +++ b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/CompositionContainerTests.cs @@ -1699,6 +1699,7 @@ public void GetExports1_TypeAsMetadataViewTypeArgument_IsUsedAsMetadataConstrain } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void GetExports2_TypeAsMetadataViewTypeArgument_IsUsedAsMetadataConstraint() { var metadata = new Dictionary(); diff --git a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs index 3fb02cf6b5e7c..8072d47b979b9 100644 --- a/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs +++ b/src/libraries/System.ComponentModel.Composition/tests/System/ComponentModel/Composition/ExportDefinitionTests.cs @@ -63,7 +63,7 @@ public void Constructor2_ValueAsContractNameArgument_ShouldSetContractNameProper [Fact] public void Constructor2_NullAsMetadataArgument_ShouldSetMetadataPropertyToEmptyDictionary() { - var definition = new ExportDefinition("Contract", (IDictionary)null); ; + var definition = new ExportDefinition("Contract", (IDictionary)null); Assert.Empty(definition.Metadata); } diff --git a/src/libraries/System.ComponentModel.Primitives/ref/System.ComponentModel.Primitives.cs b/src/libraries/System.ComponentModel.Primitives/ref/System.ComponentModel.Primitives.cs index c4b69c84f1e74..45d664d1ddf63 100644 --- a/src/libraries/System.ComponentModel.Primitives/ref/System.ComponentModel.Primitives.cs +++ b/src/libraries/System.ComponentModel.Primitives/ref/System.ComponentModel.Primitives.cs @@ -89,12 +89,14 @@ public DescriptionAttribute(string description) { } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple=true, Inherited=true)] public sealed partial class DesignerAttribute : System.Attribute { - public DesignerAttribute(string designerTypeName) { } - public DesignerAttribute(string designerTypeName, string designerBaseTypeName) { } - public DesignerAttribute(string designerTypeName, System.Type designerBaseType) { } - public DesignerAttribute(System.Type designerType) { } - public DesignerAttribute(System.Type designerType, System.Type designerBaseType) { } + public DesignerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerTypeName) { } + public DesignerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerTypeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerBaseTypeName) { } + public DesignerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerTypeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type designerBaseType) { } + public DesignerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type designerType) { } + public DesignerAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type designerType, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type designerBaseType) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string DesignerBaseTypeName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string DesignerTypeName { get { throw null; } } public override object TypeId { get { throw null; } } public override bool Equals(object? obj) { throw null; } @@ -162,10 +164,12 @@ public DisplayNameAttribute(string displayName) { } public sealed partial class EditorAttribute : System.Attribute { public EditorAttribute() { } - public EditorAttribute(string typeName, string? baseTypeName) { } - public EditorAttribute(string typeName, System.Type baseType) { } - public EditorAttribute(System.Type type, System.Type baseType) { } + public EditorAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string? baseTypeName) { } + public EditorAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type baseType) { } + public EditorAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type baseType) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public string? EditorBaseTypeName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public string EditorTypeName { get { throw null; } } public override object TypeId { get { throw null; } } public override bool Equals(object? obj) { throw null; } diff --git a/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/DesignerAttribute.cs b/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/DesignerAttribute.cs index bde0162bbdf0f..51472cdeed5b6 100644 --- a/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/DesignerAttribute.cs +++ b/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/DesignerAttribute.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel.Design; +using System.Diagnostics.CodeAnalysis; namespace System.ComponentModel { @@ -17,7 +17,7 @@ public sealed class DesignerAttribute : Attribute /// Initializes a new instance of the class using the name of the type that /// provides design-time services. /// - public DesignerAttribute(string designerTypeName) + public DesignerAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerTypeName) { DesignerTypeName = designerTypeName ?? throw new ArgumentNullException(nameof(designerTypeName)); DesignerBaseTypeName = "System.ComponentModel.Design.IDesigner, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; @@ -27,7 +27,7 @@ public DesignerAttribute(string designerTypeName) /// Initializes a new instance of the class using the type that provides /// design-time services. /// - public DesignerAttribute(Type designerType) + public DesignerAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type designerType) { if (designerType == null) { @@ -42,7 +42,9 @@ public DesignerAttribute(Type designerType) /// Initializes a new instance of the class using the designer type and the /// base class for the designer. /// - public DesignerAttribute(string designerTypeName, string designerBaseTypeName) + public DesignerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerTypeName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerBaseTypeName) { DesignerTypeName = designerTypeName ?? throw new ArgumentNullException(nameof(designerTypeName)); DesignerBaseTypeName = designerBaseTypeName; @@ -52,7 +54,9 @@ public DesignerAttribute(string designerTypeName, string designerBaseTypeName) /// Initializes a new instance of the class, using the name of the designer /// class and the base class for the designer. /// - public DesignerAttribute(string designerTypeName, Type designerBaseType) + public DesignerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string designerTypeName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type designerBaseType) { if (designerTypeName == null) { @@ -71,7 +75,9 @@ public DesignerAttribute(string designerTypeName, Type designerBaseType) /// Initializes a new instance of the class using the types of the designer and /// designer base class. /// - public DesignerAttribute(Type designerType, Type designerBaseType) + public DesignerAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type designerType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type designerBaseType) { if (designerType == null) { @@ -89,11 +95,14 @@ public DesignerAttribute(Type designerType, Type designerBaseType) /// /// Gets the name of the base type of this designer. /// + // Using PublicParameterlessConstructor to preserve the type. See https://github.com/mono/linker/issues/1878 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string DesignerBaseTypeName { get; } /// /// Gets the name of the designer type associated with this designer attribute. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string DesignerTypeName { get; } /// diff --git a/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/EditorAttribute.cs b/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/EditorAttribute.cs index d1f3e27f7c777..ce23595bc2b1c 100644 --- a/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/EditorAttribute.cs +++ b/src/libraries/System.ComponentModel.Primitives/src/System/ComponentModel/EditorAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -25,7 +27,9 @@ public EditorAttribute() /// Initializes a new instance of the class with the type name and base type /// name of the editor. /// - public EditorAttribute(string typeName, string? baseTypeName) + public EditorAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string? baseTypeName) { EditorTypeName = typeName ?? throw new ArgumentNullException(nameof(typeName)); EditorBaseTypeName = baseTypeName; @@ -34,7 +38,9 @@ public EditorAttribute(string typeName, string? baseTypeName) /// /// Initializes a new instance of the class. /// - public EditorAttribute(string typeName, Type baseType) + public EditorAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type baseType) { if (typeName == null) { @@ -52,7 +58,9 @@ public EditorAttribute(string typeName, Type baseType) /// /// Initializes a new instance of the class. /// - public EditorAttribute(Type type, Type baseType) + public EditorAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type baseType) { if (type == null) { @@ -70,11 +78,13 @@ public EditorAttribute(Type type, Type baseType) /// /// Gets the name of the base class or interface serving as a lookup key for this editor. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public string? EditorBaseTypeName { get; } /// /// Gets the name of the editor class. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public string EditorTypeName { get; } /// diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index 29397fc17ead1..589bcff5586a7 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -38,7 +38,8 @@ public AmbientValueAttribute(long value) { } public AmbientValueAttribute(object value) { } public AmbientValueAttribute(float value) { } public AmbientValueAttribute(string value) { } - public AmbientValueAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string value) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] + public AmbientValueAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string value) { } public object Value { get { throw null; } } public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } @@ -78,10 +79,11 @@ public void CopyTo(System.Array array, int index) { } [System.AttributeUsageAttribute(System.AttributeTargets.Property)] public partial class AttributeProviderAttribute : System.Attribute { - public AttributeProviderAttribute(string typeName) { } - public AttributeProviderAttribute(string typeName, string propertyName) { } - public AttributeProviderAttribute(System.Type type) { } + public AttributeProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] string typeName) { } + public AttributeProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] string typeName, string propertyName) { } + public AttributeProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] System.Type type) { } public string PropertyName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] public string TypeName { get { throw null; } } } public abstract partial class BaseNumberConverter : System.ComponentModel.TypeConverter @@ -119,9 +121,11 @@ public enum BindingDirection OneWay = 0, TwoWay = 1, } - public partial class BindingList<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] T> : System.Collections.ObjectModel.Collection, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.ComponentModel.IBindingList, System.ComponentModel.ICancelAddNew, System.ComponentModel.IRaiseItemChangedEvents + public partial class BindingList<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] T> : System.Collections.ObjectModel.Collection, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.ComponentModel.IBindingList, System.ComponentModel.ICancelAddNew, System.ComponentModel.IRaiseItemChangedEvents { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] public BindingList() { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] public BindingList(System.Collections.Generic.IList list) { } public bool AllowEdit { get { throw null; } set { } } public bool AllowNew { get { throw null; } set { } } @@ -238,9 +242,9 @@ public partial class ComponentResourceManager : System.Resources.ResourceManager { public ComponentResourceManager() { } public ComponentResourceManager(System.Type t) { } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] public void ApplyResources(object value, string objectName) { } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] public virtual void ApplyResources(object value, string objectName, System.Globalization.CultureInfo culture) { } } public partial class Container : System.ComponentModel.IContainer, System.IDisposable @@ -248,7 +252,7 @@ public partial class Container : System.ComponentModel.IContainer, System.IDispo public Container() { } public virtual System.ComponentModel.ComponentCollection Components { get { throw null; } } public virtual void Add(System.ComponentModel.IComponent component) { } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of components in the container cannot be statically discovered to validate the name.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of components in the container cannot be statically discovered to validate the name.")] public virtual void Add(System.ComponentModel.IComponent component, string name) { } protected virtual System.ComponentModel.ISite CreateSite(System.ComponentModel.IComponent component, string name) { throw null; } public void Dispose() { } @@ -257,7 +261,7 @@ protected virtual void Dispose(bool disposing) { } protected virtual object GetService(System.Type service) { throw null; } public virtual void Remove(System.ComponentModel.IComponent component) { } protected void RemoveWithoutUnsiting(System.ComponentModel.IComponent component) { } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of components in the container cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of components in the container cannot be statically discovered.")] protected virtual void ValidateName(System.ComponentModel.IComponent component, string name) { } } public abstract partial class ContainerFilterService @@ -284,15 +288,20 @@ protected CustomTypeDescriptor(System.ComponentModel.ICustomTypeDescriptor paren public virtual System.ComponentModel.AttributeCollection GetAttributes() { throw null; } public virtual string GetClassName() { throw null; } public virtual string GetComponentName() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] public virtual System.ComponentModel.TypeConverter GetConverter() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] public virtual System.ComponentModel.EventDescriptor GetDefaultEvent() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public virtual System.ComponentModel.PropertyDescriptor GetDefaultProperty() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] public virtual object GetEditor(System.Type editorBaseType) { throw null; } public virtual System.ComponentModel.EventDescriptorCollection GetEvents() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public virtual System.ComponentModel.EventDescriptorCollection GetEvents(System.Attribute[] attributes) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[] attributes) { throw null; } public virtual object GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd) { throw null; } } @@ -562,15 +571,20 @@ public partial interface ICustomTypeDescriptor System.ComponentModel.AttributeCollection GetAttributes(); string GetClassName(); string GetComponentName(); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] System.ComponentModel.TypeConverter GetConverter(); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] System.ComponentModel.EventDescriptor GetDefaultEvent(); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] System.ComponentModel.PropertyDescriptor GetDefaultProperty(); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object GetEditor(System.Type editorBaseType); System.ComponentModel.EventDescriptorCollection GetEvents(); - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.EventDescriptorCollection GetEvents(System.Attribute[] attributes); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] System.ComponentModel.PropertyDescriptorCollection GetProperties(); - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] System.ComponentModel.PropertyDescriptorCollection GetProperties(System.Attribute[] attributes); object GetPropertyOwner(System.ComponentModel.PropertyDescriptor pd); } @@ -628,8 +642,9 @@ public enum InheritanceLevel [System.AttributeUsageAttribute(System.AttributeTargets.Class)] public partial class InstallerTypeAttribute : System.Attribute { - public InstallerTypeAttribute(string typeName) { } - public InstallerTypeAttribute(System.Type installerType) { } + public InstallerTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { } + public InstallerTypeAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type installerType) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public virtual System.Type InstallerType { get { throw null; } } public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } @@ -704,9 +719,9 @@ internal LicenseManager() { } public static System.ComponentModel.LicenseContext CurrentContext { get { throw null; } set { } } public static System.ComponentModel.LicenseUsageMode UsageMode { get { throw null; } } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public static object CreateWithContext(System.Type type, System.ComponentModel.LicenseContext creationContext) { throw null; } + public static object CreateWithContext([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, System.ComponentModel.LicenseContext creationContext) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] - public static object CreateWithContext(System.Type type, System.ComponentModel.LicenseContext creationContext, object[] args) { throw null; } + public static object CreateWithContext([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, System.ComponentModel.LicenseContext creationContext, object[] args) { throw null; } public static bool IsLicensed(System.Type type) { throw null; } public static bool IsValid(System.Type type) { throw null; } public static bool IsValid(System.Type type, object instance, out System.ComponentModel.License license) { throw null; } @@ -725,8 +740,9 @@ public sealed partial class LicenseProviderAttribute : System.Attribute { public static readonly System.ComponentModel.LicenseProviderAttribute Default; public LicenseProviderAttribute() { } - public LicenseProviderAttribute(string typeName) { } - public LicenseProviderAttribute(System.Type type) { } + public LicenseProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) { } + public LicenseProviderAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type type) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public System.Type LicenseProvider { get { throw null; } } public override object TypeId { get { throw null; } } public override bool Equals(object value) { throw null; } @@ -968,8 +984,8 @@ protected MemberDescriptor(string name, System.Attribute[] attributes) { } protected virtual System.ComponentModel.AttributeCollection CreateAttributeCollection() { throw null; } public override bool Equals(object obj) { throw null; } protected virtual void FillAttributes(System.Collections.IList attributeList) { } - protected static System.Reflection.MethodInfo FindMethod(System.Type componentClass, string name, System.Type[] args, System.Type returnType) { throw null; } - protected static System.Reflection.MethodInfo FindMethod(System.Type componentClass, string name, System.Type[] args, System.Type returnType, bool publicOnly) { throw null; } + protected static System.Reflection.MethodInfo FindMethod([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type componentClass, string name, System.Type[] args, System.Type returnType) { throw null; } + protected static System.Reflection.MethodInfo FindMethod([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods)] System.Type componentClass, string name, System.Type[] args, System.Type returnType, bool publicOnly) { throw null; } public override int GetHashCode() { throw null; } protected virtual object GetInvocationTarget(System.Type type, object instance) { throw null; } [System.ObsoleteAttribute("This method has been deprecated. Use GetInvocationTarget instead. https://go.microsoft.com/fwlink/?linkid=14202")] @@ -995,6 +1011,7 @@ protected override void Dispose(bool disposing) { } } public partial class NullableConverter : System.ComponentModel.TypeConverter { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The UnderlyingType cannot be statically discovered.")] public NullableConverter(System.Type type) { } public System.Type NullableType { get { throw null; } } public System.Type UnderlyingType { get { throw null; } } @@ -1032,7 +1049,7 @@ protected PropertyDescriptor(System.ComponentModel.MemberDescriptor descr) : bas protected PropertyDescriptor(System.ComponentModel.MemberDescriptor descr, System.Attribute[] attrs) : base (default(string)) { } protected PropertyDescriptor(string name, System.Attribute[] attrs) : base (default(string)) { } public abstract System.Type ComponentType { get; } - public virtual System.ComponentModel.TypeConverter Converter { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered.")] get { throw null; } } + public virtual System.ComponentModel.TypeConverter Converter { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] get { throw null; } } public virtual bool IsLocalizable { get { throw null; } } public abstract bool IsReadOnly { get; } public abstract System.Type PropertyType { get; } @@ -1040,22 +1057,24 @@ protected PropertyDescriptor(string name, System.Attribute[] attrs) : base (defa public virtual bool SupportsChangeEvents { get { throw null; } } public virtual void AddValueChanged(object component, System.EventHandler handler) { } public abstract bool CanResetValue(object component); - protected object CreateInstance(System.Type type) { throw null; } + protected object CreateInstance([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } public override bool Equals(object obj) { throw null; } protected override void FillAttributes(System.Collections.IList attributeList) { } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] public System.ComponentModel.PropertyDescriptorCollection GetChildProperties() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public System.ComponentModel.PropertyDescriptorCollection GetChildProperties(System.Attribute[] filter) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered.")] public System.ComponentModel.PropertyDescriptorCollection GetChildProperties(object instance) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetChildProperties(object instance, System.Attribute[] filter) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed. PropertyDescriptor's PropertyType cannot be statically discovered.")] public virtual object GetEditor(System.Type editorBaseType) { throw null; } public override int GetHashCode() { throw null; } protected override object GetInvocationTarget(System.Type type, object instance) { throw null; } - protected System.Type GetTypeFromName(string typeName) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Calls ComponentType.Assembly.GetType on the non-fully qualified typeName, which the trimmer cannot recognize.")] + [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] + protected System.Type GetTypeFromName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { throw null; } public abstract object GetValue(object component); protected internal System.EventHandler GetValueChangedHandler(object component) { throw null; } protected virtual void OnValueChanged(object component, System.EventArgs e) { } @@ -1117,8 +1136,8 @@ void System.Collections.IList.RemoveAt(int index) { } public partial class PropertyTabAttribute : System.Attribute { public PropertyTabAttribute() { } - public PropertyTabAttribute(string tabClassName) { } - public PropertyTabAttribute(string tabClassName, System.ComponentModel.PropertyTabScope tabScope) { } + public PropertyTabAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string tabClassName) { } + public PropertyTabAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string tabClassName, System.ComponentModel.PropertyTabScope tabScope) { } public PropertyTabAttribute(System.Type tabClass) { } public PropertyTabAttribute(System.Type tabClass, System.ComponentModel.PropertyTabScope tabScope) { } public System.Type[] TabClasses { get { throw null; } } @@ -1127,6 +1146,7 @@ public PropertyTabAttribute(System.Type tabClass, System.ComponentModel.Property public bool Equals(System.ComponentModel.PropertyTabAttribute other) { throw null; } public override bool Equals(object other) { throw null; } public override int GetHashCode() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Types referenced by tabClassNames may be trimmed.")] protected void InitializeArrays(string[] tabClassNames, System.ComponentModel.PropertyTabScope[] tabScopes) { } protected void InitializeArrays(System.Type[] tabClasses, System.ComponentModel.PropertyTabScope[] tabScopes) { } } @@ -1140,9 +1160,10 @@ public enum PropertyTabScope [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=true)] public sealed partial class ProvidePropertyAttribute : System.Attribute { - public ProvidePropertyAttribute(string propertyName, string receiverTypeName) { } - public ProvidePropertyAttribute(string propertyName, System.Type receiverType) { } + public ProvidePropertyAttribute(string propertyName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string receiverTypeName) { } + public ProvidePropertyAttribute(string propertyName, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type receiverType) { } public string PropertyName { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string ReceiverTypeName { get { throw null; } } public override object TypeId { get { throw null; } } public override bool Equals(object obj) { throw null; } @@ -1236,9 +1257,11 @@ public partial class ToolboxItemAttribute : System.Attribute public static readonly System.ComponentModel.ToolboxItemAttribute Default; public static readonly System.ComponentModel.ToolboxItemAttribute None; public ToolboxItemAttribute(bool defaultType) { } - public ToolboxItemAttribute(string toolboxItemTypeName) { } - public ToolboxItemAttribute(System.Type toolboxItemType) { } + public ToolboxItemAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] string toolboxItemTypeName) { } + public ToolboxItemAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type toolboxItemType) { } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public System.Type ToolboxItemType { get { throw null; } } + [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] public string ToolboxItemTypeName { get { throw null; } } public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } @@ -1291,11 +1314,11 @@ public TypeConverter() { } protected System.Exception GetConvertToException(object value, System.Type destinationType) { throw null; } public bool GetCreateInstanceSupported() { throw null; } public virtual bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] public System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public virtual System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext context, object value, System.Attribute[] attributes) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of value cannot be statically discovered.")] public System.ComponentModel.PropertyDescriptorCollection GetProperties(object value) { throw null; } public bool GetPropertiesSupported() { throw null; } public virtual bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext context) { throw null; } @@ -1334,12 +1357,12 @@ public abstract partial class TypeDescriptionProvider { protected TypeDescriptionProvider() { } protected TypeDescriptionProvider(System.ComponentModel.TypeDescriptionProvider parent) { } - public virtual object CreateInstance(System.IServiceProvider provider, System.Type objectType, System.Type[] argTypes, object[] args) { throw null; } + public virtual object CreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type objectType, System.Type[] argTypes, object[] args) { throw null; } public virtual System.Collections.IDictionary GetCache(object instance) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public virtual System.ComponentModel.ICustomTypeDescriptor GetExtendedTypeDescriptor(object instance) { throw null; } protected internal virtual System.ComponentModel.IExtenderProvider[] GetExtenderProviders(object instance) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public virtual string GetFullComponentName(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("GetReflectionType is not trim compatible because the Type of object cannot be statically discovered.")] public System.Type GetReflectionType(object instance) { throw null; } @@ -1348,10 +1371,10 @@ protected TypeDescriptionProvider(System.ComponentModel.TypeDescriptionProvider [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public virtual System.Type GetReflectionType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type objectType, object instance) { throw null; } public virtual System.Type GetRuntimeType(System.Type reflectionType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of instance cannot be statically discovered.")] public System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor(object instance) { throw null; } - public System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType) { throw null; } - public virtual System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType, object instance) { throw null; } + public System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType) { throw null; } + public virtual System.ComponentModel.ICustomTypeDescriptor GetTypeDescriptor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type objectType, object instance) { throw null; } public virtual bool IsSupportedType(System.Type type) { throw null; } } public sealed partial class TypeDescriptor @@ -1368,6 +1391,7 @@ public static event System.ComponentModel.RefreshEventHandler Refreshed { add { [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static System.ComponentModel.TypeDescriptionProvider AddAttributes(System.Type type, params System.Attribute[] attributes) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Types specified in table may be trimmed, or have their static construtors trimmed.")] public static void AddEditorTable(System.Type editorBaseType, System.Collections.Hashtable table) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void AddProvider(System.ComponentModel.TypeDescriptionProvider provider, object instance) { } @@ -1379,57 +1403,63 @@ public static void AddProviderTransparent(System.ComponentModel.TypeDescriptionP public static void AddProviderTransparent(System.ComponentModel.TypeDescriptionProvider provider, System.Type type) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static void CreateAssociation(object primary, object secondary) { } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.Design.IDesigner CreateDesigner(System.ComponentModel.IComponent component, System.Type designerBaseType) { throw null; } - public static System.ComponentModel.EventDescriptor CreateEvent(System.Type componentType, System.ComponentModel.EventDescriptor oldEventDescriptor, params System.Attribute[] attributes) { throw null; } - public static System.ComponentModel.EventDescriptor CreateEvent(System.Type componentType, string name, System.Type type, params System.Attribute[] attributes) { throw null; } - public static object CreateInstance(System.IServiceProvider provider, System.Type objectType, System.Type[] argTypes, object[] args) { throw null; } - public static System.ComponentModel.PropertyDescriptor CreateProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.ComponentModel.PropertyDescriptor oldPropertyDescriptor, params System.Attribute[] attributes) { throw null; } - public static System.ComponentModel.PropertyDescriptor CreateProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, string name, System.Type type, params System.Attribute[] attributes) { throw null; } + public static System.ComponentModel.EventDescriptor CreateEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.ComponentModel.EventDescriptor oldEventDescriptor, params System.Attribute[] attributes) { throw null; } + public static System.ComponentModel.EventDescriptor CreateEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, string name, System.Type type, params System.Attribute[] attributes) { throw null; } + public static object CreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type objectType, System.Type[] argTypes, object[] args) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] + public static System.ComponentModel.PropertyDescriptor CreateProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.ComponentModel.PropertyDescriptor oldPropertyDescriptor, params System.Attribute[] attributes) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] + public static System.ComponentModel.PropertyDescriptor CreateProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, string name, System.Type type, params System.Attribute[] attributes) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static object GetAssociation(System.Type type, object primary) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.AttributeCollection GetAttributes(object component) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.AttributeCollection GetAttributes(object component, bool noCustomTypeDesc) { throw null; } - public static System.ComponentModel.AttributeCollection GetAttributes([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + public static System.ComponentModel.AttributeCollection GetAttributes([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static string GetClassName(object component) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static string GetClassName(object component, bool noCustomTypeDesc) { throw null; } - public static string GetClassName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + public static string GetClassName([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static string GetComponentName(object component) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static string GetComponentName(object component, bool noCustomTypeDesc) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All. The Type of component cannot be statically discovered.")] public static System.ComponentModel.TypeConverter GetConverter(object component) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All. The Type of component cannot be statically discovered.")] public static System.ComponentModel.TypeConverter GetConverter(object component, bool noCustomTypeDesc) { throw null; } - public static System.ComponentModel.TypeConverter GetConverter([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] + public static System.ComponentModel.TypeConverter GetConverter([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code. The Type of component cannot be statically discovered.")] public static System.ComponentModel.EventDescriptor GetDefaultEvent(object component) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code. The Type of component cannot be statically discovered.")] public static System.ComponentModel.EventDescriptor GetDefaultEvent(object component, bool noCustomTypeDesc) { throw null; } - public static System.ComponentModel.EventDescriptor GetDefaultEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] + public static System.ComponentModel.EventDescriptor GetDefaultEvent([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptor GetDefaultProperty(object component) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptor GetDefaultProperty(object component, bool noCustomTypeDesc) { throw null; } - public static System.ComponentModel.PropertyDescriptor GetDefaultProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] + public static System.ComponentModel.PropertyDescriptor GetDefaultProperty([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed. The Type of component cannot be statically discovered.")] public static object GetEditor(object component, System.Type editorBaseType) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed. The Type of component cannot be statically discovered.")] public static object GetEditor(object component, System.Type editorBaseType, bool noCustomTypeDesc) { throw null; } - public static object GetEditor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, System.Type editorBaseType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] + public static object GetEditor([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, System.Type editorBaseType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[] attributes) { throw null; } @@ -1437,25 +1467,26 @@ public static void CreateAssociation(object primary, object secondary) { } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, System.Attribute[] attributes, bool noCustomTypeDesc) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static System.ComponentModel.EventDescriptorCollection GetEvents(object component, bool noCustomTypeDesc) { throw null; } - public static System.ComponentModel.EventDescriptorCollection GetEvents([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + public static System.ComponentModel.EventDescriptorCollection GetEvents([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public static System.ComponentModel.EventDescriptorCollection GetEvents([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[] attributes) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + public static System.ComponentModel.EventDescriptorCollection GetEvents([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[] attributes) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered.")] public static string GetFullComponentName(object component) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, System.Attribute[] attributes, bool noCustomTypeDesc) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The Type of component cannot be statically discovered.")] public static System.ComponentModel.PropertyDescriptorCollection GetProperties(object component, bool noCustomTypeDesc) { throw null; } - public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[] attributes) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered.")] + public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + public static System.ComponentModel.PropertyDescriptorCollection GetProperties([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type componentType, System.Attribute[] attributes) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] public static System.ComponentModel.TypeDescriptionProvider GetProvider(object instance) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] @@ -1612,9 +1643,9 @@ protected DesignerOptionService() { } protected System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection CreateOptionCollection(System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection parent, string name, object value) { throw null; } protected virtual void PopulateOptionCollection(System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection options) { } protected virtual bool ShowDialog(System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection options, object optionObject) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The option value's Type cannot be statically discovered.")] object System.ComponentModel.Design.IDesignerOptionService.GetOptionValue(string pageName, string valueName) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The option value's Type cannot be statically discovered.")] void System.ComponentModel.Design.IDesignerOptionService.SetOptionValue(string pageName, string valueName, object value) { } [System.ComponentModel.EditorAttribute("", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public sealed partial class DesignerOptionCollection : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList @@ -1625,7 +1656,7 @@ internal DesignerOptionCollection() { } public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection this[string name] { get { throw null; } } public string Name { get { throw null; } } public System.ComponentModel.Design.DesignerOptionService.DesignerOptionCollection Parent { get { throw null; } } - public System.ComponentModel.PropertyDescriptorCollection Properties { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of DesignerOptionCollection's value cannot be statically discovered.")] get { throw null; } } + public System.ComponentModel.PropertyDescriptorCollection Properties { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The Type of DesignerOptionCollection's value cannot be statically discovered.")] get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } bool System.Collections.IList.IsFixedSize { get { throw null; } } @@ -1808,9 +1839,9 @@ public partial interface IDesignerHostTransactionState } public partial interface IDesignerOptionService { - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The option value's Type cannot be statically discovered.")] object GetOptionValue(string pageName, string valueName); - [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The option value's Type cannot be statically discovered.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("The option value's Type cannot be statically discovered.")] void SetOptionValue(string pageName, string valueName, object value); } public partial interface IDictionaryService @@ -1923,9 +1954,12 @@ public partial interface ITypeResolutionService System.Reflection.Assembly GetAssembly(System.Reflection.AssemblyName name); System.Reflection.Assembly GetAssembly(System.Reflection.AssemblyName name, bool throwOnError); string GetPathOfAssembly(System.Reflection.AssemblyName name); - System.Type GetType(string name); - System.Type GetType(string name, bool throwOnError); - System.Type GetType(string name, bool throwOnError, bool ignoreCase); + [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + System.Type GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name); + [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + System.Type GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError); + [return: System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + System.Type GetType([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError, bool ignoreCase); void ReferenceAssembly(System.Reflection.AssemblyName name); } public partial class MenuCommand diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.manual.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.manual.cs index 67789567ccb91..2b276c17aa383 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.manual.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.manual.cs @@ -7,6 +7,6 @@ public abstract partial class DesignerOptionService { [System.ComponentModel.TypeConverter(typeof(DesignerOptionConverter))] public sealed partial class DesignerOptionCollection { } - internal class DesignerOptionConverter { } + internal sealed class DesignerOptionConverter { } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Substitutions.xml new file mode 100644 index 0000000000000..d5df6acfa1cbc --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Substitutions.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.LibraryBuild.xml b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.LibraryBuild.xml new file mode 100644 index 0000000000000..00f9036918d63 --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.LibraryBuild.xml @@ -0,0 +1,19 @@ + + + + + ILLink + IL2026 + member + M:System.ComponentModel.Design.DesigntimeLicenseContextSerializer.SerializeWithBinaryFormatter(System.IO.Stream,System.String,System.ComponentModel.Design.DesigntimeLicenseContext) + This warning is left in the product so developers get an ILLink warning when trimming an app with System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization=true. + + + ILLink + IL2026 + member + M:System.ComponentModel.Design.DesigntimeLicenseContextSerializer.DeserializeUsingBinaryFormatter(System.ComponentModel.Design.DesigntimeLicenseContextSerializer.StreamWrapper,System.String,System.ComponentModel.Design.RuntimeLicenseContext) + This warning is left in the product so developers get an ILLink warning when trimming an app with System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization=true. + + + diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.xml index 558d9f107eb5a..47dfa8195d7be 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.ComponentModel.TypeConverter/src/ILLink/ILLink.Suppressions.xml @@ -1,281 +1,11 @@  - - ILLink - IL2026 - member - M:System.ComponentModel.PropertyDescriptor.GetTypeFromName(System.String) - - - ILLink - IL2026 - member - M:System.ComponentModel.PropertyTabAttribute.get_TabClasses - - - ILLink - IL2026 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.ReflectedTypeData.GetTypeFromName(System.String) - - - ILLink - IL2026 - member - M:System.ComponentModel.TypeDescriptor.ComNativeDescriptorProxy.#ctor - - - ILLink - IL2057 - member - M:System.ComponentModel.InstallerTypeAttribute.get_InstallerType - - - ILLink - IL2057 - member - M:System.ComponentModel.LicenseProviderAttribute.get_LicenseProvider - - - ILLink - IL2057 - member - M:System.ComponentModel.PropertyDescriptor.GetTypeFromName(System.String) - - - ILLink - IL2057 - member - M:System.ComponentModel.PropertyTabAttribute.get_TabClasses - - - ILLink - IL2057 - member - M:System.ComponentModel.ReflectPropertyDescriptor.FillAttributes(System.Collections.IList) - - - ILLink - IL2057 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.GetIntrinsicTypeEditor(System.Collections.Hashtable,System.Type) - - - ILLink - IL2057 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.GetTypeFromName(System.String) - - - ILLink - IL2057 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.ReflectedTypeData.GetEditorAttribute(System.ComponentModel.AttributeCollection,System.Type) - - - ILLink - IL2057 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.ReflectedTypeData.GetTypeFromName(System.String) - - - ILLink - IL2057 - member - M:System.ComponentModel.ToolboxItemAttribute.get_ToolboxItemType - - - ILLink - IL2057 - member - M:System.ComponentModel.TypeDescriptor.CreateDesigner(System.ComponentModel.IComponent,System.Type) - - - ILLink - IL2059 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.GetEditorTable(System.Type) - - - ILLink - IL2067 - member - M:System.ComponentModel.LicenseManager.CreateWithContext(System.Type,System.ComponentModel.LicenseContext,System.Object[]) - - - ILLink - IL2067 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.CreateInstance(System.IServiceProvider,System.Type,System.Type[],System.Object[]) - - - ILLink - IL2067 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.CreateInstance(System.Type,System.Type) - - - ILLink - IL2067 - member - M:System.ComponentModel.TypeDescriptionProvider.CreateInstance(System.IServiceProvider,System.Type,System.Type[],System.Object[]) - - - ILLink - IL2070 - member - M:System.ComponentModel.MemberDescriptor.FindMethod(System.Type,System.String,System.Type[],System.Type,System.Boolean) - - - ILLink - IL2070 - member - M:System.ComponentModel.PropertyDescriptor.CreateInstance(System.Type) - - - ILLink - IL2070 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.CreateInstance(System.IServiceProvider,System.Type,System.Type[],System.Object[]) - - - ILLink - IL2070 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.CreateInstance(System.Type,System.Type) - - - ILLink - IL2072 - member - M:System.ComponentModel.LicenseManager.ValidateInternalRecursive(System.ComponentModel.LicenseContext,System.Type,System.Object,System.Boolean,System.ComponentModel.License@,System.String@) - ILLink IL2072 member M:System.ComponentModel.MaskedTextProvider.Clone - - ILLink - IL2072 - member - M:System.ComponentModel.TypeDescriptor.ComNativeDescriptorProxy.#ctor - - - ILLink - IL2075 - member - M:System.ComponentModel.NullableConverter.ConvertTo(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type) - - - ILLink - IL2075 - member - M:System.ComponentModel.ReflectEventDescriptor.FillEventInfoAttribute(System.Reflection.EventInfo,System.Collections.IList) - - - ILLink - IL2075 - member - M:System.ComponentModel.ReflectEventDescriptor.FillMethods - - - ILLink - IL2075 - member - M:System.ComponentModel.ReflectEventDescriptor.FillSingleMethodAttribute(System.Reflection.MethodInfo,System.Collections.IList) - - - ILLink - IL2075 - member - M:System.ComponentModel.ReflectPropertyDescriptor.FillAttributes(System.Collections.IList) - - - ILLink - IL2075 - member - M:System.ComponentModel.ReflectPropertyDescriptor.get_SetMethodValue - - - ILLink - IL2075 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.GetIntrinsicTypeEditor(System.Collections.Hashtable,System.Type) - - - ILLink - IL2080 - member - M:System.ComponentModel.ReflectEventDescriptor.FillMethods - - - ILLink - IL2080 - member - M:System.ComponentModel.ReflectPropertyDescriptor.FillAttributes(System.Collections.IList) - - - ILLink - IL2080 - member - M:System.ComponentModel.ReflectPropertyDescriptor.get_SetMethodValue - - - ILLink - IL2026 - member - M:System.ComponentModel.Design.DesigntimeLicenseContextSerializer.Deserialize(System.IO.Stream,System.String,System.ComponentModel.Design.RuntimeLicenseContext) - - - ILLink - IL2026 - member - M:System.ComponentModel.Design.DesigntimeLicenseContextSerializer.Serialize(System.IO.Stream,System.String,System.ComponentModel.Design.DesigntimeLicenseContext) - - - ILLink - IL2026 - member - M:System.ComponentModel.TypeDescriptor.TypeDescriptionNode.DefaultTypeDescriptor.System#ComponentModel#ICustomTypeDescriptor#GetConverter - - - ILLink - IL2026 - member - M:System.ComponentModel.TypeDescriptor.TypeDescriptionNode.DefaultTypeDescriptor.System#ComponentModel#ICustomTypeDescriptor#GetDefaultEvent - - - ILLink - IL2026 - member - M:System.ComponentModel.TypeDescriptor.TypeDescriptionNode.DefaultTypeDescriptor.System#ComponentModel#ICustomTypeDescriptor#GetDefaultProperty - - - ILLink - IL2026 - member - M:System.ComponentModel.TypeDescriptor.TypeDescriptionNode.DefaultTypeDescriptor.System#ComponentModel#ICustomTypeDescriptor#GetEditor(System.Type) - - - ILLink - IL2062 - member - M:System.ComponentModel.ReflectTypeDescriptionProvider.ReflectedTypeData.GetAttributes - - - ILLink - IL2072 - member - M:System.ComponentModel.NullableConverter.#ctor(System.Type) - - - ILLink - IL2072 - member - M:System.ComponentModel.ReflectPropertyDescriptor.FillAttributes(System.Collections.IList) - diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs b/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs index d59b6b60c4d1e..67df1e5bbacef 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/MS/Internal/Xml/Linq/ComponentModel/XComponentModel.cs @@ -11,7 +11,7 @@ namespace MS.Internal.Xml.Linq.ComponentModel { - internal class XTypeDescriptionProvider : TypeDescriptionProvider + internal sealed class XTypeDescriptionProvider : TypeDescriptionProvider { public XTypeDescriptionProvider() : base(TypeDescriptor.GetProvider(typeof(T))) { @@ -23,20 +23,19 @@ public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMemb } } - internal class XTypeDescriptor : CustomTypeDescriptor + internal sealed class XTypeDescriptor : CustomTypeDescriptor { public XTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "No attributes are supplied.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public override PropertyDescriptorCollection GetProperties() { return GetProperties(null); } - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { PropertyDescriptorCollection properties = new PropertyDescriptorCollection(null); @@ -142,7 +141,7 @@ protected virtual void OnChanging(object sender, XObjectChangeEventArgs args) } } - internal class XElementAttributePropertyDescriptor : XPropertyDescriptor + internal sealed class XElementAttributePropertyDescriptor : XPropertyDescriptor { private XDeferredSingleton _value; private XAttribute _changeState; @@ -194,7 +193,7 @@ protected override void OnChanging(object sender, XObjectChangeEventArgs args) } } - internal class XElementDescendantsPropertyDescriptor : XPropertyDescriptor> + internal sealed class XElementDescendantsPropertyDescriptor : XPropertyDescriptor> { private XDeferredAxis _value; private XName _changeState; @@ -247,7 +246,7 @@ protected override void OnChanging(object sender, XObjectChangeEventArgs args) } } - internal class XElementElementPropertyDescriptor : XPropertyDescriptor + internal sealed class XElementElementPropertyDescriptor : XPropertyDescriptor { private XDeferredSingleton _value; private XElement _changeState; @@ -315,7 +314,7 @@ protected override void OnChanging(object sender, XObjectChangeEventArgs args) } } - internal class XElementElementsPropertyDescriptor : XPropertyDescriptor> + internal sealed class XElementElementsPropertyDescriptor : XPropertyDescriptor> { private XDeferredAxis _value; private object _changeState; @@ -379,7 +378,7 @@ protected override void OnChanging(object sender, XObjectChangeEventArgs args) } } - internal class XElementValuePropertyDescriptor : XPropertyDescriptor + internal sealed class XElementValuePropertyDescriptor : XPropertyDescriptor { private XElement _element; @@ -431,7 +430,7 @@ protected override void OnChanged(object sender, XObjectChangeEventArgs args) } } - internal class XElementXmlPropertyDescriptor : XPropertyDescriptor + internal sealed class XElementXmlPropertyDescriptor : XPropertyDescriptor { private XElement _element; @@ -455,7 +454,7 @@ protected override void OnChanged(object sender, XObjectChangeEventArgs args) } } - internal class XAttributeValuePropertyDescriptor : XPropertyDescriptor + internal sealed class XAttributeValuePropertyDescriptor : XPropertyDescriptor { private XAttribute _attribute; @@ -495,7 +494,7 @@ protected override void OnChanged(object sender, XObjectChangeEventArgs args) } } - internal class XDeferredAxis : IEnumerable, IEnumerable where T : XObject + internal sealed class XDeferredAxis : IEnumerable, IEnumerable where T : XObject { private readonly Func> _func; internal XElement element; @@ -541,7 +540,7 @@ public IEnumerable this[string expandedName] } } - internal class XDeferredSingleton where T : XObject + internal sealed class XDeferredSingleton where T : XObject { private readonly Func _func; internal XElement element; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/Resources/Strings.resx b/src/libraries/System.ComponentModel.TypeConverter/src/Resources/Strings.resx index 7de9810d591ed..e9dd12b50616d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/Resources/Strings.resx +++ b/src/libraries/System.ComponentModel.TypeConverter/src/Resources/Strings.resx @@ -250,4 +250,7 @@ Relationships between {0}.{1} and {2}.{3} are not supported. + + BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information. + \ No newline at end of file diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj index 7319c74b19b82..a93c8e4aadc9d 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -4,6 +4,9 @@ true $(NetCoreAppCurrent) + + + diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs index 76e19ce56ab10..3a04957915f01 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AmbientValueAttribute.cs @@ -17,6 +17,7 @@ public sealed class AmbientValueAttribute : Attribute /// specified value to the specified type, and using the U.S. English culture as the /// translation context. /// + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] public AmbientValueAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, string value) { // The try/catch here is because attributes should never throw exceptions. We would fail to diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs index c7a0b666fd5a2..b9ae3f3b94e66 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ArrayConverter.cs @@ -59,7 +59,7 @@ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContex /// public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; - private class ArrayPropertyDescriptor : SimplePropertyDescriptor + private sealed class ArrayPropertyDescriptor : SimplePropertyDescriptor { private readonly int _index; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs index df28b8e229096..973771580be1c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/AttributeProviderAttribute.cs @@ -8,10 +8,12 @@ namespace System.ComponentModel [AttributeUsage(AttributeTargets.Property)] public class AttributeProviderAttribute : Attribute { + private const DynamicallyAccessedMemberTypes RequiredMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicEvents; + /// /// Creates a new AttributeProviderAttribute object. /// - public AttributeProviderAttribute(string typeName) + public AttributeProviderAttribute([DynamicallyAccessedMembers(RequiredMemberTypes)] string typeName) { TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName)); } @@ -19,7 +21,7 @@ public AttributeProviderAttribute(string typeName) /// /// Creates a new AttributeProviderAttribute object. /// - public AttributeProviderAttribute(string typeName, string propertyName) + public AttributeProviderAttribute([DynamicallyAccessedMembers(RequiredMemberTypes)] string typeName, string propertyName) { TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName)); PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); @@ -28,7 +30,7 @@ public AttributeProviderAttribute(string typeName, string propertyName) /// /// Creates a new AttributeProviderAttribute object. /// - public AttributeProviderAttribute(Type type) + public AttributeProviderAttribute([DynamicallyAccessedMembers(RequiredMemberTypes)] Type type) { if (type == null) { @@ -42,6 +44,7 @@ public AttributeProviderAttribute(Type type) /// The TypeName property returns the assembly qualified type name /// passed into the constructor. /// + [DynamicallyAccessedMembers(RequiredMemberTypes)] public string TypeName { get; } /// diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs index 6f6925308b50d..74c42161519fa 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/BindingList.cs @@ -41,16 +41,19 @@ public class BindingList<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTy #region Constructors + [RequiresUnreferencedCode("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] public BindingList() => Initialize(); /// /// Constructor that allows substitution of the inner list with a custom list. /// + [RequiresUnreferencedCode("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] public BindingList(IList list) : base(list) { Initialize(); } + [RequiresUnreferencedCode("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] private void Initialize() { // Set the default value of AllowNew based on whether type T has a default constructor @@ -191,6 +194,8 @@ protected override void ClearItems() FireListChanged(ListChangedType.Reset, -1); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "BindingList ctor is marked with RequiresUnreferencedCode.")] protected override void InsertItem(int index, T item) { EndNew(addNewPos); @@ -223,6 +228,8 @@ protected override void RemoveItem(int index) FireListChanged(ListChangedType.ItemDeleted, index); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "BindingList ctor is marked with RequiresUnreferencedCode.")] protected override void SetItem(int index, T item) { if (raiseItemChangedEvents) @@ -444,6 +451,7 @@ void IBindingList.RemoveIndex(PropertyDescriptor prop) #region Property Change Support + [RequiresUnreferencedCode("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] private void HookPropertyChanged(T item) { // Note: inpc may be null if item is null, so always check. @@ -466,6 +474,7 @@ private void UnhookPropertyChanged(T item) } } + [RequiresUnreferencedCode("Raises ListChanged events with PropertyDescriptors. PropertyDescriptors require unreferenced code.")] private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (RaiseListChangedEvents) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs index d966c032c4eaa..3975633bc49bf 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Container.cs @@ -242,7 +242,7 @@ protected virtual void ValidateName(IComponent component, string name) } } - private class Site : ISite + private sealed class Site : ISite { private string _name; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs index 4a8b77db038df..ccc0709679f65 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CultureInfoConverter.cs @@ -224,7 +224,7 @@ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContex /// IComparer object used for sorting CultureInfos /// WARNING: If you change where null is positioned, then you must fix CultureConverter.GetStandardValues! /// - private class CultureComparer : IComparer + private sealed class CultureComparer : IComparer { private readonly CultureInfoConverter _converter; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs index fd6e5f7167774..03de81f643376 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/CustomTypeDescriptor.cs @@ -69,6 +69,7 @@ public virtual AttributeCollection GetAttributes() /// The GetConverter method returns a type converter for the type this type /// descriptor is representing. /// + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] public virtual TypeConverter GetConverter() { if (_parent != null) @@ -83,18 +84,21 @@ public virtual TypeConverter GetConverter() /// The GetDefaultEvent method returns the event descriptor for the default /// event on the object this type descriptor is representing. /// + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] public virtual EventDescriptor GetDefaultEvent() => _parent?.GetDefaultEvent(); /// /// The GetDefaultProperty method returns the property descriptor for the /// default property on the object this type descriptor is representing. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public virtual PropertyDescriptor GetDefaultProperty() => _parent?.GetDefaultProperty(); /// /// The GetEditor method returns an editor of the given type that is /// to be associated with the class this type descriptor is representing. /// + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode)] public virtual object GetEditor(Type editorBaseType) => _parent?.GetEditor(editorBaseType); /// @@ -139,6 +143,7 @@ public virtual EventDescriptorCollection GetEvents(Attribute[] attributes) /// If no parent is provided,this will return an empty /// property collection. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public virtual PropertyDescriptorCollection GetProperties() { if (_parent != null) @@ -156,7 +161,7 @@ public virtual PropertyDescriptorCollection GetProperties() /// If no parent is provided,this will return an empty /// property collection. /// - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes) { if (_parent != null) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs index 5e05f31d18b22..b82e5924ffa59 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/DelegatingTypeDescriptionProvider.cs @@ -32,7 +32,11 @@ internal DelegatingTypeDescriptionProvider(Type type) /// data type. If the method is not interested in providing a substitute /// instance, it should call base. /// - public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args) + public override object CreateInstance( + IServiceProvider provider, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, + Type[] argTypes, + object[] args) { return Provider.CreateInstance(provider, objectType, argTypes, args); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs index 490dded91c36a..620962c694881 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesignerOptionService.cs @@ -484,7 +484,7 @@ public override object ConvertTo(ITypeDescriptorContext cxt, CultureInfo culture return base.ConvertTo(cxt, culture, value, destinationType); } - private class OptionPropertyDescriptor : PropertyDescriptor + private sealed class OptionPropertyDescriptor : PropertyDescriptor { private readonly DesignerOptionCollection _option; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs index c650b38372f75..64a4498443efd 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs @@ -40,7 +40,7 @@ public override void SetSavedLicenseKey(Type type, string key) } } - internal class RuntimeLicenseContext : LicenseContext + internal sealed class RuntimeLicenseContext : LicenseContext { internal Hashtable _savedLicenseKeys; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs index c4e7c24165e77..e9aa1bea911b0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContextSerializer.cs @@ -5,6 +5,7 @@ using System.Runtime.Serialization; using System.Collections; using System.IO; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace System.ComponentModel.Design @@ -14,6 +15,10 @@ namespace System.ComponentModel.Design /// public class DesigntimeLicenseContextSerializer { + internal const byte BinaryWriterMagic = 255; + + private static bool EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization { get; } = AppContext.TryGetSwitch("System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization", out bool isEnabled) ? isEnabled : false; + // Not creatable. private DesigntimeLicenseContextSerializer() { @@ -24,26 +29,151 @@ private DesigntimeLicenseContextSerializer() /// using the specified key and output stream. /// public static void Serialize(Stream o, string cryptoKey, DesigntimeLicenseContext context) + { + if (EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization) + { + SerializeWithBinaryFormatter(o, cryptoKey, context); + } + else + { + using (BinaryWriter writer = new BinaryWriter(o, encoding: Text.Encoding.UTF8, leaveOpen: true)) + { + writer.Write(BinaryWriterMagic); // flag to identify BinaryWriter + writer.Write(cryptoKey); + writer.Write(context._savedLicenseKeys.Count); + foreach (DictionaryEntry keyAndValue in context._savedLicenseKeys) + { + writer.Write(keyAndValue.Key.ToString()); + writer.Write(keyAndValue.Value.ToString()); + } + } + } + } + + private static void SerializeWithBinaryFormatter(Stream o, string cryptoKey, DesigntimeLicenseContext context) { IFormatter formatter = new BinaryFormatter(); -#pragma warning disable SYSLIB0011 // Issue https://github.com/dotnet/runtime/issues/39293 tracks finding an alternative to BinaryFormatter +#pragma warning disable SYSLIB0011 formatter.Serialize(o, new object[] { cryptoKey, context._savedLicenseKeys }); #pragma warning restore SYSLIB0011 } - internal static void Deserialize(Stream o, string cryptoKey, RuntimeLicenseContext context) + private class StreamWrapper : Stream { -#pragma warning disable SYSLIB0011 // Issue https://github.com/dotnet/runtime/issues/39293 tracks finding an alternative to BinaryFormatter - IFormatter formatter = new BinaryFormatter(); + private Stream _stream; + private bool _readFirstByte; + internal byte _firstByte; + public StreamWrapper(Stream stream) + { + _stream = stream; + _readFirstByte = false; + _firstByte = 0; + } + + public override bool CanRead => _stream.CanRead; + + public override bool CanSeek => _stream.CanSeek; + + public override bool CanWrite => _stream.CanWrite; + + public override long Length => _stream.Length; + + public override long Position { get => _stream.Position; set => _stream.Position = value; } + + public override void Flush() => _stream.Flush(); + + public override int Read(byte[] buffer, int offset, int count) + { + Debug.Assert(_stream.Position != 0, "Expected the first byte to be read first"); + if (_stream.Position == 1) + { + Debug.Assert(_readFirstByte == true); + // Add the first byte read by ReadByte into buffer here + buffer[offset] = _firstByte; + return _stream.Read(buffer, offset + 1, count - 1) + 1; + } + return _stream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin); + + public override void SetLength(long value) => _stream.SetLength(value); - object obj = formatter.Deserialize(o); + public override void Write(byte[] buffer, int offset, int count) => _stream.Write(buffer, offset, count); + + public override int ReadByte() + { + byte read = (byte)_stream.ReadByte(); + _firstByte = read; + _readFirstByte = true; + return read; + } + } + + /// + /// During deserialization, the stream passed in may be binary formatted or may have used binary writer. This is a quick test to discern between them. + /// + private static bool StreamIsBinaryFormatted(StreamWrapper stream) + { + // For binary formatter, the first byte is the SerializationHeaderRecord and has a value 0 + int firstByte = stream.ReadByte(); + if (firstByte != 0) + { + return false; + } + + return true; + } + + private static void DeserializeUsingBinaryFormatter(StreamWrapper wrappedStream, string cryptoKey, RuntimeLicenseContext context) + { + if (EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization) + { +#pragma warning disable SYSLIB0011 + IFormatter formatter = new BinaryFormatter(); + + object obj = formatter.Deserialize(wrappedStream); #pragma warning restore SYSLIB0011 - if (obj is object[] value) + if (obj is object[] value) + { + if (value[0] is string && (string)value[0] == cryptoKey) + { + context._savedLicenseKeys = (Hashtable)value[1]; + } + } + } + else + { + throw new NotSupportedException(SR.BinaryFormatterMessage); + } + } + + internal static void Deserialize(Stream o, string cryptoKey, RuntimeLicenseContext context) + { + StreamWrapper wrappedStream = new StreamWrapper(o); + if (StreamIsBinaryFormatted(wrappedStream)) + { + DeserializeUsingBinaryFormatter(wrappedStream, cryptoKey, context); + } + else { - if (value[0] is string && (string)value[0] == cryptoKey) + using (BinaryReader reader = new BinaryReader(wrappedStream, encoding: Text.Encoding.UTF8, leaveOpen: true)) { - context._savedLicenseKeys = (Hashtable)value[1]; + byte binaryWriterIdentifer = wrappedStream._firstByte; + Debug.Assert(binaryWriterIdentifer == BinaryWriterMagic, $"Expected the first byte to be {BinaryWriterMagic}"); + string streamCryptoKey = reader.ReadString(); + int numEntries = reader.ReadInt32(); + if (streamCryptoKey == cryptoKey) + { + context._savedLicenseKeys.Clear(); + for (int i = 0; i < numEntries; i++) + { + string key = reader.ReadString(); + string value = reader.ReadString(); + context._savedLicenseKeys.Add(key, value); + } + } } } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs index db2b1ce147091..b02ea35f3bb51 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/ITypeResolutionService.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.ComponentModel.Design @@ -23,17 +24,20 @@ public interface ITypeResolutionService /// /// Loads a type with the given name. /// - Type GetType(string name); + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name); /// /// Loads a type with the given name. /// - Type GetType(string name, bool throwOnError); + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError); /// /// Loads a type with the given name. /// - Type GetType(string name, bool throwOnError, bool ignoreCase); + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type GetType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string name, bool throwOnError, bool ignoreCase); /// /// References the given assembly name. Once an assembly has diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs index 19038d77f08d5..73eca9e8a4fd2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/PropertyTabAttribute.cs @@ -38,7 +38,10 @@ public PropertyTabAttribute(Type tabClass) : this(tabClass, PropertyTabScope.Com /// Basic constructor that creates a property tab attribute that will create a tab /// of the specified type. /// - public PropertyTabAttribute(string tabClassName) : this(tabClassName, PropertyTabScope.Component) + public PropertyTabAttribute( + // Using PublicParameterlessConstructor to preserve the type. See https://github.com/mono/linker/issues/1878 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string tabClassName) + : this(tabClassName, PropertyTabScope.Component) { } @@ -60,7 +63,10 @@ public PropertyTabAttribute(Type tabClass, PropertyTabScope tabScope) /// Basic constructor that creates a property tab attribute that will create a tab /// of the specified type. /// - public PropertyTabAttribute(string tabClassName, PropertyTabScope tabScope) + public PropertyTabAttribute( + // Using PublicParameterlessConstructor to preserve the type. See https://github.com/mono/linker/issues/1878 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string tabClassName, + PropertyTabScope tabScope) { _tabClassNames = new string[] { tabClassName }; if (tabScope < PropertyTabScope.Document) @@ -79,43 +85,52 @@ public Type[] TabClasses { if (_tabClasses == null && _tabClassNames != null) { - _tabClasses = new Type[_tabClassNames.Length]; - for (int i = 0; i < _tabClassNames.Length; i++) - { - int commaIndex = _tabClassNames[i].IndexOf(','); - string className = null; - string assemblyName = null; + InitializeTabClasses(); + } + return _tabClasses; + } + } - if (commaIndex != -1) - { - className = _tabClassNames[i].AsSpan(0, commaIndex).Trim().ToString(); - assemblyName = _tabClassNames[i].AsSpan(commaIndex + 1).Trim().ToString(); - } - else - { - className = _tabClassNames[i]; - } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The APIs that specify _tabClassNames are either marked with DynamicallyAccessedMembers or RequiresUnreferencedCode.")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", + Justification = "The APIs that specify _tabClassNames are either marked with DynamicallyAccessedMembers or RequiresUnreferencedCode.")] + private void InitializeTabClasses() + { + _tabClasses = new Type[_tabClassNames.Length]; + for (int i = 0; i < _tabClassNames.Length; i++) + { + int commaIndex = _tabClassNames[i].IndexOf(','); + string className = null; + string assemblyName = null; - _tabClasses[i] = Type.GetType(className, false); + if (commaIndex != -1) + { + className = _tabClassNames[i].AsSpan(0, commaIndex).Trim().ToString(); + assemblyName = _tabClassNames[i].AsSpan(commaIndex + 1).Trim().ToString(); + } + else + { + className = _tabClassNames[i]; + } + + _tabClasses[i] = Type.GetType(className, false); - if (_tabClasses[i] == null) + if (_tabClasses[i] == null) + { + if (assemblyName != null) + { + Assembly a = Assembly.Load(assemblyName); + if (a != null) { - if (assemblyName != null) - { - Assembly a = Assembly.Load(assemblyName); - if (a != null) - { - _tabClasses[i] = a.GetType(className, true); - } - } - else - { - throw new TypeLoadException(SR.Format(SR.PropertyTabAttributeTypeLoadException, className)); - } + _tabClasses[i] = a.GetType(className, true); } } + else + { + throw new TypeLoadException(SR.Format(SR.PropertyTabAttributeTypeLoadException, className)); + } } - return _tabClasses; } } @@ -128,9 +143,9 @@ public Type[] TabClasses public override bool Equals(object other) { - if (other is PropertyTabAttribute) + if (other is PropertyTabAttribute propertyTabAttribute) { - return Equals((PropertyTabAttribute)other); + return Equals(propertyTabAttribute); } return false; } @@ -158,7 +173,6 @@ public bool Equals(PropertyTabAttribute other) return true; } - /// /// Returns the hashcode for this object. /// @@ -167,6 +181,7 @@ public bool Equals(PropertyTabAttribute other) /// /// Utiliity function to set the types of tab classes this PropertyTabAttribute specifies. /// + [RequiresUnreferencedCode("The Types referenced by tabClassNames may be trimmed.")] protected void InitializeArrays(string[] tabClassNames, PropertyTabScope[] tabScopes) { InitializeArrays(tabClassNames, null, tabScopes); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs index 8f234906d1e02..3c4ac1998ac01 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptor.cs @@ -8,6 +8,8 @@ namespace System.ComponentModel /// public abstract class EventDescriptor : MemberDescriptor { + internal const string RequiresUnreferencedCodeMessage = "The built-in EventDescriptor implementation uses Reflection which requires unreferenced code."; + /// /// Initializes a new instance of the class with the /// specified name and attribute array. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs index 52631639bc53e..13c6ea9622fdf 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EventDescriptorCollection.cs @@ -410,7 +410,7 @@ object IList.this[int index] bool IList.IsFixedSize => _readOnly; - private class ArraySubsetEnumerator : IEnumerator + private sealed class ArraySubsetEnumerator : IEnumerator { private readonly Array _array; private readonly int _total; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs index f194821f7d36f..699ebc3d22748 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ICustomTypeDescriptor.cs @@ -29,21 +29,25 @@ public interface ICustomTypeDescriptor /// /// Gets a type converter for this object. /// + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] TypeConverter GetConverter(); /// /// Gets the default event for this object. /// + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] EventDescriptor GetDefaultEvent(); /// /// Gets the default property for this object. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptor GetDefaultProperty(); /// /// Gets an editor of the specified type for this object. /// + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode)] object GetEditor(Type editorBaseType); /// @@ -61,12 +65,13 @@ public interface ICustomTypeDescriptor /// /// Gets the properties for this instance of a component. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptorCollection GetProperties(); /// /// Gets the properties for this instance of a component using the attribute array as a filter. /// - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] PropertyDescriptorCollection GetProperties(Attribute[] attributes); /// diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs index 6279f3d33878e..e3b47c9911e07 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InheritanceAttribute.cs @@ -104,6 +104,6 @@ public override bool Equals(object value) /// /// Converts this attribute to a string. /// - public override string ToString() => TypeDescriptor.GetConverter(typeof(InheritanceLevel)).ConvertToString(InheritanceLevel); + public override string ToString() => TypeDescriptor.GetConverterTrimUnsafe(typeof(InheritanceLevel)).ConvertToString(InheritanceLevel); } } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs index d88952b15c829..2ad8d8941c952 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/InstallerTypeAttribute.cs @@ -11,12 +11,13 @@ namespace System.ComponentModel [AttributeUsage(AttributeTargets.Class)] public class InstallerTypeAttribute : Attribute { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] private readonly string _typeName; /// /// Initializes a new instance of the System.Windows.Forms.ComponentModel.InstallerTypeAttribute class. /// - public InstallerTypeAttribute(Type installerType) + public InstallerTypeAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type installerType) { if (installerType == null) { @@ -26,7 +27,7 @@ public InstallerTypeAttribute(Type installerType) _typeName = installerType.AssemblyQualifiedName; } - public InstallerTypeAttribute(string typeName) + public InstallerTypeAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { _typeName = typeName; } @@ -34,6 +35,7 @@ public InstallerTypeAttribute(string typeName) /// /// Gets the type of installer associated with this attribute. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public virtual Type InstallerType => Type.GetType(_typeName); public override bool Equals(object obj) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs index 70e6728d729c5..fa4649c916ff3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicFileLicenseProvider.cs @@ -99,7 +99,7 @@ public override License GetLicense(LicenseContext context, Type type, object ins return lic; } - private class LicFileLicense : License + private sealed class LicFileLicense : License { private readonly LicFileLicenseProvider _owner; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs index 01ef7f2b3dbb7..113b4a60d0e5b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.LicenseInteropHelper.cs @@ -13,7 +13,7 @@ public sealed partial class LicenseManager // A private implementation of a LicenseContext used for instantiating // managed objects exposed to COM. It has memory for the license key // of a single Type. - private class CLRLicenseContext : LicenseContext + private sealed class CLRLicenseContext : LicenseContext { private readonly Type _type; private string _key; @@ -62,7 +62,7 @@ public override void SetSavedLicenseKey(Type type, string key) } // Used from IClassFactory2 when retrieving LicInfo - private class LicInfoHelperLicenseContext : LicenseContext + private sealed class LicInfoHelperLicenseContext : LicenseContext { private readonly Hashtable _savedLicenseKeys = new Hashtable(); @@ -80,7 +80,7 @@ public override void SetSavedLicenseKey(Type type, string key) // This is a helper class that supports the CLR's IClassFactory2 marshaling // support. - private class LicenseInteropHelper + private static class LicenseInteropHelper { // Used to validate a type and retrieve license details // when activating a managed COM server from an IClassFactory2 instance. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs index c340d12247eee..d3acf78aff558 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseManager.cs @@ -4,6 +4,7 @@ using System.Collections; using System.ComponentModel.Design; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.Versioning; using System.Threading; @@ -118,7 +119,9 @@ private static void CacheProvider(Type type, LicenseProvider provider) /// as the context in which the licensed instance can be used. /// [UnsupportedOSPlatform("browser")] - public static object CreateWithContext(Type type, LicenseContext creationContext) + public static object CreateWithContext( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, + LicenseContext creationContext) { return CreateWithContext(type, creationContext, Array.Empty()); } @@ -129,7 +132,10 @@ public static object CreateWithContext(Type type, LicenseContext creationContext /// instance can be used. /// [UnsupportedOSPlatform("browser")] - public static object CreateWithContext(Type type, LicenseContext creationContext, object[] args) + public static object CreateWithContext( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type, + LicenseContext creationContext, + object[] args) { object created = null; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs index 9760e6eeaed5e..6b7fd3615940b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/LicenseProviderAttribute.cs @@ -17,7 +17,9 @@ public sealed class LicenseProviderAttribute : Attribute /// public static readonly LicenseProviderAttribute Default = new LicenseProviderAttribute(); + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] private Type _licenseProviderType; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] private readonly string _licenseProviderName; /// @@ -32,7 +34,7 @@ public LicenseProviderAttribute() : this((string)null) /// Initializes a new instance of the class with /// the specified type. /// - public LicenseProviderAttribute(string typeName) + public LicenseProviderAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) { _licenseProviderName = typeName; } @@ -41,7 +43,7 @@ public LicenseProviderAttribute(string typeName) /// Initializes a new instance of the class with /// the specified type of license provider. /// - public LicenseProviderAttribute(Type type) + public LicenseProviderAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { _licenseProviderType = type; } @@ -49,6 +51,7 @@ public LicenseProviderAttribute(Type type) /// /// Gets the license provider to use with the associated class. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public Type LicenseProvider { get diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs index 46e996946fbab..8f82dc61acc45 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MaskedTextProvider.cs @@ -65,7 +65,7 @@ private enum CharType /// This structure describes some constraints and properties of a character in the test string, as specified /// in the mask. /// - private class CharDescriptor + private sealed class CharDescriptor { // The position the character holds in the mask string. Required for testing the character against the mask. public int MaskPosition; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs index 910d36aeb9077..ade2a381a3cbd 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/MemberDescriptor.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.ComponentModel @@ -390,15 +391,26 @@ private void FilterAttributesIfNeeded() /// /// Finds the given method through reflection. This method only looks for public methods. /// - protected static MethodInfo FindMethod(Type componentClass, string name, Type[] args, Type returnType) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", + Justification = "This method only looks for public methods by hard-coding publicOnly=true")] + protected static MethodInfo FindMethod( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type componentClass, + string name, + Type[] args, + Type returnType) { - return FindMethod(componentClass, name, args, returnType, true); + return FindMethod(componentClass, name, args, returnType, publicOnly: true); } /// /// Finds the given method through reflection. /// - protected static MethodInfo FindMethod(Type componentClass, string name, Type[] args, Type returnType, bool publicOnly) + protected static MethodInfo FindMethod( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type componentClass, + string name, + Type[] args, + Type returnType, + bool publicOnly) { if (componentClass == null) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs index 0261be604f3ed..dee44cf54d8e4 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NestedContainer.cs @@ -101,7 +101,7 @@ protected override object GetService(Type service) /// Simple site implementation. We do some special processing to name the site, but /// that's about it. /// - private class Site : INestedSite + private sealed class Site : INestedSite { private string _name; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs index 336015c00986c..9bcfdc0b03300 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs @@ -18,6 +18,7 @@ public class NullableConverter : TypeConverter /// /// Nullable converter is initialized with the underlying simple type. /// + [RequiresUnreferencedCode("The UnderlyingType cannot be statically discovered.")] public NullableConverter(Type type) { NullableType = type; @@ -94,7 +95,7 @@ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinati /// /// Converts the given value object to the destination type. /// - public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == null) { @@ -107,7 +108,7 @@ public override object ConvertTo(ITypeDescriptorContext context, System.Globaliz } else if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ci = NullableType.GetConstructor(new Type[] { UnderlyingType }); + ConstructorInfo ci = GetNullableConstructor(); Debug.Assert(ci != null, "Couldn't find constructor"); return new InstanceDescriptor(ci, new object[] { value }, true); } @@ -127,6 +128,14 @@ public override object ConvertTo(ITypeDescriptorContext context, System.Globaliz return base.ConvertTo(context, culture, value, destinationType); } + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(Nullable<>))] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "The Nullable ctor will be preserved by the DynamicDependency.")] + private ConstructorInfo GetNullableConstructor() + { + return NullableType.GetConstructor(new Type[] { UnderlyingType })!; + } + /// /// public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs index c9381b807aa61..92da2abeffabc 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs @@ -12,6 +12,8 @@ namespace System.ComponentModel /// public abstract class PropertyDescriptor : MemberDescriptor { + internal const string PropertyDescriptorPropertyTypeMessage = "PropertyDescriptor's PropertyType cannot be statically discovered."; + private TypeConverter _converter; private Hashtable _valueChangedHandlers; private object[] _editors; @@ -57,7 +59,7 @@ protected PropertyDescriptor(MemberDescriptor descr, Attribute[] attrs) : base(d /// public virtual TypeConverter Converter { - [RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage)] get { // Always grab the attribute collection first here, because if the metadata version @@ -184,7 +186,8 @@ public override bool Equals(object obj) /// /// Creates an instance of the specified type. /// - protected object CreateInstance(Type type) + protected object CreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) { Type[] typeArgs = new Type[] { typeof(Type) }; ConstructorInfo ctor = type.GetConstructor(typeArgs); @@ -213,19 +216,19 @@ protected override void FillAttributes(IList attributeList) base.FillAttributes(attributeList); } - [RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage)] public PropertyDescriptorCollection GetChildProperties() => GetChildProperties(null, null); - [RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public PropertyDescriptorCollection GetChildProperties(Attribute[] filter) => GetChildProperties(null, filter); - [RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered.")] public PropertyDescriptorCollection GetChildProperties(object instance) => GetChildProperties(instance, null); /// /// Retrieves the properties /// - [RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered. The Type of instance cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public virtual PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) { if (instance == null) @@ -241,7 +244,7 @@ public virtual PropertyDescriptorCollection GetChildProperties(object instance, /// /// Gets an editor of the specified type. /// - [RequiresUnreferencedCode("The PropertyDescriptor's PropertyType cannot be statically discovered.")] + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " " + PropertyDescriptorPropertyTypeMessage)] public virtual object GetEditor(Type editorBaseType) { object editor = null; @@ -342,7 +345,10 @@ protected override object GetInvocationTarget(Type type, object instance) /// /// Gets a type using its name. /// - protected Type GetTypeFromName(string typeName) + [RequiresUnreferencedCode("Calls ComponentType.Assembly.GetType on the non-fully qualified typeName, which the trimmer cannot recognize.")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + protected Type GetTypeFromName( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { if (typeName == null || typeName.Length == 0) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs index 00ec16d76b6e2..1e9d93f199e59 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptorCollection.cs @@ -586,7 +586,7 @@ object IList.this[int index] } } - private class PropertyDescriptorEnumerator : IDictionaryEnumerator + private sealed class PropertyDescriptorEnumerator : IDictionaryEnumerator { private readonly PropertyDescriptorCollection _owner; private int _index = -1; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs index 1499556226f93..7eca6e49fdac3 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ProvidePropertyAttribute.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; + namespace System.ComponentModel { /// @@ -12,7 +14,9 @@ public sealed class ProvidePropertyAttribute : Attribute /// /// Initializes a new instance of the class. /// - public ProvidePropertyAttribute(string propertyName, Type receiverType) + public ProvidePropertyAttribute( + string propertyName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type receiverType) { if (receiverType == null) { @@ -26,7 +30,9 @@ public ProvidePropertyAttribute(string propertyName, Type receiverType) /// /// Initializes a new instance of the class. /// - public ProvidePropertyAttribute(string propertyName, string receiverTypeName) + public ProvidePropertyAttribute( + string propertyName, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string receiverTypeName) { PropertyName = propertyName; ReceiverTypeName = receiverTypeName; @@ -40,6 +46,8 @@ public ProvidePropertyAttribute(string propertyName, string receiverTypeName) /// /// Gets the name of the data type this property can extend /// + // Using PublicParameterlessConstructor to preserve the type. See https://github.com/mono/linker/issues/1878 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] public string ReceiverTypeName { get; } public override bool Equals(object obj) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs index dcc7fa81d1dfb..e8e3fee165616 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectEventDescriptor.cs @@ -4,6 +4,7 @@ using System.Collections; using System.ComponentModel.Design; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.ComponentModel @@ -58,6 +59,7 @@ namespace System.ComponentModel internal sealed class ReflectEventDescriptor : EventDescriptor { private Type _type; // the delegate type for the event + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _componentClass; // the class of the component this info is for private MethodInfo _addMethod; // the method to use when adding an event @@ -68,7 +70,11 @@ internal sealed class ReflectEventDescriptor : EventDescriptor /// /// This is the main constructor for an ReflectEventDescriptor. /// - public ReflectEventDescriptor(Type componentClass, string name, Type type, Attribute[] attributes) + public ReflectEventDescriptor( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, + string name, + Type type, + Attribute[] attributes) : base(name, attributes) { if (componentClass == null) @@ -84,9 +90,13 @@ public ReflectEventDescriptor(Type componentClass, string name, Type type, Attri _type = type; } - public ReflectEventDescriptor(Type componentClass, EventInfo eventInfo) + public ReflectEventDescriptor( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, + EventInfo eventInfo) : base(eventInfo.Name, Array.Empty()) { + Debug.Assert(eventInfo.ReflectedType.IsAssignableFrom(componentClass), "eventInfo.ReflectedType is used below, but only componentClass is annotated with DynamicallyAccessedMembers. Ensure ReflectedType is in componentClass's hierarchy."); + _componentClass = componentClass ?? throw new ArgumentException(SR.Format(SR.InvalidNullArgument, nameof(componentClass))); _realEvent = eventInfo; } @@ -95,7 +105,10 @@ public ReflectEventDescriptor(Type componentClass, EventInfo eventInfo) /// This constructor takes an existing ReflectEventDescriptor and modifies it by merging in the /// passed-in attributes. /// - public ReflectEventDescriptor(Type componentType, EventDescriptor oldReflectEventDescriptor, Attribute[] attributes) + public ReflectEventDescriptor( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, + EventDescriptor oldReflectEventDescriptor, + Attribute[] attributes) : base(oldReflectEventDescriptor, attributes) { _componentClass = componentType; @@ -234,12 +247,15 @@ protected override void FillAttributes(IList attributes) base.FillAttributes(attributes); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "currentReflectType is in _componentClass's hierarchy. Since _componentClass is annotated with All, this means currentReflectType is annotated with All as well.")] private void FillEventInfoAttribute(EventInfo realEventInfo, IList attributes) { string eventName = realEventInfo.Name; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; Type currentReflectType = realEventInfo.ReflectedType; Debug.Assert(currentReflectType != null, "currentReflectType cannot be null"); + Debug.Assert(currentReflectType.IsAssignableFrom(_componentClass), "currentReflectType must be in _componentClass's hierarchy"); int depth = 0; // First, calculate the depth of the object hierarchy. We do this so we can do a single @@ -351,12 +367,15 @@ private void FillMethods() _filledMethods = true; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", + Justification = "currentReflectType is in _componentClass's hierarchy. Since _componentClass is annotated with All, this means currentReflectType is annotated with All as well.")] private void FillSingleMethodAttribute(MethodInfo realMethodInfo, IList attributes) { string methodName = realMethodInfo.Name; BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; Type currentReflectType = realMethodInfo.ReflectedType; Debug.Assert(currentReflectType != null, "currentReflectType cannot be null"); + Debug.Assert(currentReflectType.IsAssignableFrom(_componentClass), "currentReflectType must be in _componentClass's hierarchy"); // First, calculate the depth of the object hierarchy. We do this so we can do a single // object create for an array of attributes. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs index 49517569e9648..8178284444baf 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectPropertyDescriptor.cs @@ -77,6 +77,7 @@ internal sealed class ReflectPropertyDescriptor : PropertyDescriptor /// /// The main constructor for ReflectPropertyDescriptors. /// + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage)] public ReflectPropertyDescriptor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, string name, @@ -112,6 +113,7 @@ public ReflectPropertyDescriptor( /// /// A constructor for ReflectPropertyDescriptors that have no attributes. /// + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage)] public ReflectPropertyDescriptor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, string name, @@ -134,6 +136,7 @@ public ReflectPropertyDescriptor( /// /// A constructor for ReflectPropertyDescriptors that creates an extender property. /// + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage)] public ReflectPropertyDescriptor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, string name, @@ -154,6 +157,7 @@ public ReflectPropertyDescriptor( /// This constructor takes an existing ReflectPropertyDescriptor and modifies it by merging in the /// passed-in attributes. /// + [RequiresUnreferencedCode(PropertyDescriptorPropertyTypeMessage)] public ReflectPropertyDescriptor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentClass, PropertyDescriptor oldReflectPropertyDescriptor, @@ -404,7 +408,7 @@ private MethodInfo SetMethodValue if (_setMethod == null) { - for (Type t = ComponentType.BaseType; t != null && t != typeof(object); t = t.BaseType) + for (Type t = _componentClass.BaseType; t != null && t != typeof(object); t = t.BaseType) { BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance; PropertyInfo p = t.GetProperty(name, bindingFlags, binder: null, PropertyType, Type.EmptyTypes, null); @@ -727,6 +731,8 @@ public override bool CanResetValue(object component) return false; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "ReflectPropertyDescriptor ctors are all marked as RequiresUnreferencedCode because PropertyType can't be annotated as 'All'.")] protected override void FillAttributes(IList attributes) { Debug.Assert(_componentClass != null, "Must have a component class for FillAttributes"); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs index 3ddd16dd19ea1..230b0ec3eb7be 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.ReflectedTypeData.cs @@ -15,7 +15,7 @@ internal sealed partial class ReflectTypeDescriptionProvider : TypeDescriptionPr /// This class contains all the reflection information for a /// given type. /// - private class ReflectedTypeData + private sealed class ReflectedTypeData { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _type; @@ -41,6 +41,8 @@ internal ReflectedTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMember /// /// Retrieves custom attributes. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2062:UnrecognizedReflectionPattern", + Justification = "_type is annotated as preserve All members, so any Types returned from GetInterfaces should be preserved as well once https://github.com/mono/linker/issues/1731 is fixed.")] internal AttributeCollection GetAttributes() { // Worst case collision scenario: we don't want the perf hit @@ -170,7 +172,7 @@ internal string GetComponentName(object instance) /// it will be used to retrieve attributes. Otherwise, _type /// will be used. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode("NullableConverter's UnderlyingType cannot be statically discovered. The Type of instance cannot be statically discovered.")] internal TypeConverter GetConverter(object instance) { TypeConverterAttribute typeAttr = null; @@ -260,7 +262,7 @@ internal EventDescriptor GetDefaultEvent(object instance) /// /// Return the default property. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered.")] internal PropertyDescriptor GetDefaultProperty(object instance) { AttributeCollection attributes; @@ -293,7 +295,7 @@ internal PropertyDescriptor GetDefaultProperty(object instance) /// /// Retrieves the editor for the given base type. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " The Type of instance cannot be statically discovered.")] internal object GetEditor(object instance, Type editorBaseType) { EditorAttribute typeAttr; @@ -448,6 +450,7 @@ internal EventDescriptorCollection GetEvents() /// /// Retrieves the properties for this type. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] internal PropertyDescriptorCollection GetProperties() { // Worst case collision scenario: we don't want the perf hit @@ -484,7 +487,14 @@ internal PropertyDescriptorCollection GetProperties() /// that this PropertyDescriptor came from is first checked, /// then a global Type.GetType is performed. /// - private Type GetTypeFromName(string typeName) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Calling _type.Assembly.GetType on a non-assembly qualified type will still work. See https://github.com/mono/linker/issues/1895")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", + Justification = "Using the non-assembly qualified type name will still work.")] + private Type GetTypeFromName( + // this method doesn't create the type, but all callers are annotated with PublicConstructors, + // so use that value to ensure the Type will be preserved + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string typeName) { if (string.IsNullOrEmpty(typeName)) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index c2a136f65d5c7..de3b12258cca6 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -96,7 +96,7 @@ internal ReflectTypeDescriptionProvider() /// /// Provides a way to create instances, and cache them where applicable. /// - private class IntrinsicTypeConverterData + private sealed class IntrinsicTypeConverterData { private readonly Func _constructionFunc; @@ -143,40 +143,50 @@ public TypeConverter GetOrCreateConverterInstance(Type innerType) /// appropriate logic to handle this in below. /// private static Dictionary IntrinsicTypeConverters - => LazyInitializer.EnsureInitialized(ref s_intrinsicTypeConverters, () => new Dictionary(27) { - // Add the intrinsics - // - [typeof(bool)] = new IntrinsicTypeConverterData((type) => new BooleanConverter()), - [typeof(byte)] = new IntrinsicTypeConverterData((type) => new ByteConverter()), - [typeof(sbyte)] = new IntrinsicTypeConverterData((type) => new SByteConverter()), - [typeof(char)] = new IntrinsicTypeConverterData((type) => new CharConverter()), - [typeof(double)] = new IntrinsicTypeConverterData((type) => new DoubleConverter()), - [typeof(string)] = new IntrinsicTypeConverterData((type) => new StringConverter()), - [typeof(int)] = new IntrinsicTypeConverterData((type) => new Int32Converter()), - [typeof(short)] = new IntrinsicTypeConverterData((type) => new Int16Converter()), - [typeof(long)] = new IntrinsicTypeConverterData((type) => new Int64Converter()), - [typeof(float)] = new IntrinsicTypeConverterData((type) => new SingleConverter()), - [typeof(ushort)] = new IntrinsicTypeConverterData((type) => new UInt16Converter()), - [typeof(uint)] = new IntrinsicTypeConverterData((type) => new UInt32Converter()), - [typeof(ulong)] = new IntrinsicTypeConverterData((type) => new UInt64Converter()), - [typeof(object)] = new IntrinsicTypeConverterData((type) => new TypeConverter()), - [typeof(CultureInfo)] = new IntrinsicTypeConverterData((type) => new CultureInfoConverter()), - [typeof(DateTime)] = new IntrinsicTypeConverterData((type) => new DateTimeConverter()), - [typeof(DateTimeOffset)] = new IntrinsicTypeConverterData((type) => new DateTimeOffsetConverter()), - [typeof(decimal)] = new IntrinsicTypeConverterData((type) => new DecimalConverter()), - [typeof(TimeSpan)] = new IntrinsicTypeConverterData((type) => new TimeSpanConverter()), - [typeof(Guid)] = new IntrinsicTypeConverterData((type) => new GuidConverter()), - [typeof(Uri)] = new IntrinsicTypeConverterData((type) => new UriTypeConverter()), - [typeof(Version)] = new IntrinsicTypeConverterData((type) => new VersionConverter()), - // Special cases for things that are not bound to a specific type - // - [typeof(Array)] = new IntrinsicTypeConverterData((type) => new ArrayConverter()), - [typeof(ICollection)] = new IntrinsicTypeConverterData((type) => new CollectionConverter()), - [typeof(Enum)] = new IntrinsicTypeConverterData((type) => CreateEnumConverter(type), cacheConverterInstance: false), - [s_intrinsicNullableKey] = new IntrinsicTypeConverterData((type) => new NullableConverter(type), cacheConverterInstance: false), - [s_intrinsicReferenceKey] = new IntrinsicTypeConverterData((type) => new ReferenceConverter(type), cacheConverterInstance: false), - }); + [RequiresUnreferencedCode("NullableConverter's UnderlyingType cannot be statically discovered.")] + get + { + return LazyInitializer.EnsureInitialized(ref s_intrinsicTypeConverters, () => new Dictionary(27) + { + // Add the intrinsics + // + [typeof(bool)] = new IntrinsicTypeConverterData((type) => new BooleanConverter()), + [typeof(byte)] = new IntrinsicTypeConverterData((type) => new ByteConverter()), + [typeof(sbyte)] = new IntrinsicTypeConverterData((type) => new SByteConverter()), + [typeof(char)] = new IntrinsicTypeConverterData((type) => new CharConverter()), + [typeof(double)] = new IntrinsicTypeConverterData((type) => new DoubleConverter()), + [typeof(string)] = new IntrinsicTypeConverterData((type) => new StringConverter()), + [typeof(int)] = new IntrinsicTypeConverterData((type) => new Int32Converter()), + [typeof(short)] = new IntrinsicTypeConverterData((type) => new Int16Converter()), + [typeof(long)] = new IntrinsicTypeConverterData((type) => new Int64Converter()), + [typeof(float)] = new IntrinsicTypeConverterData((type) => new SingleConverter()), + [typeof(ushort)] = new IntrinsicTypeConverterData((type) => new UInt16Converter()), + [typeof(uint)] = new IntrinsicTypeConverterData((type) => new UInt32Converter()), + [typeof(ulong)] = new IntrinsicTypeConverterData((type) => new UInt64Converter()), + [typeof(object)] = new IntrinsicTypeConverterData((type) => new TypeConverter()), + [typeof(CultureInfo)] = new IntrinsicTypeConverterData((type) => new CultureInfoConverter()), + [typeof(DateTime)] = new IntrinsicTypeConverterData((type) => new DateTimeConverter()), + [typeof(DateTimeOffset)] = new IntrinsicTypeConverterData((type) => new DateTimeOffsetConverter()), + [typeof(decimal)] = new IntrinsicTypeConverterData((type) => new DecimalConverter()), + [typeof(TimeSpan)] = new IntrinsicTypeConverterData((type) => new TimeSpanConverter()), + [typeof(Guid)] = new IntrinsicTypeConverterData((type) => new GuidConverter()), + [typeof(Uri)] = new IntrinsicTypeConverterData((type) => new UriTypeConverter()), + [typeof(Version)] = new IntrinsicTypeConverterData((type) => new VersionConverter()), + // Special cases for things that are not bound to a specific type + // + [typeof(Array)] = new IntrinsicTypeConverterData((type) => new ArrayConverter()), + [typeof(ICollection)] = new IntrinsicTypeConverterData((type) => new CollectionConverter()), + [typeof(Enum)] = new IntrinsicTypeConverterData((type) => CreateEnumConverter(type), cacheConverterInstance: false), + [s_intrinsicNullableKey] = new IntrinsicTypeConverterData((type) => CreateNullableConverter(type), cacheConverterInstance: false), + [s_intrinsicReferenceKey] = new IntrinsicTypeConverterData((type) => new ReferenceConverter(type), cacheConverterInstance: false), + }); + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "IntrinsicTypeConverters is marked with RequiresUnreferencedCode. It is the only place that should call this.")] + private static NullableConverter CreateNullableConverter(Type type) => new NullableConverter(type); [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern", Justification = "Trimmer does not trim enums")] @@ -201,6 +211,7 @@ private static EnumConverter CreateEnumConverter(Type type) /// TypeDescriptor will search an editor /// table for the editor type, if one can be found. /// + [RequiresUnreferencedCode("The Types specified in table may be trimmed, or have their static construtors trimmed.")] internal static void AddEditorTable(Type editorBaseType, Hashtable table) { if (editorBaseType == null) @@ -227,7 +238,11 @@ internal static void AddEditorTable(Type editorBaseType, Hashtable table) /// /// CreateInstance implementation. We delegate to Activator. /// - public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args) + public override object CreateInstance( + IServiceProvider provider, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, + Type[] argTypes, + object[] args) { Debug.Assert(objectType != null, "Should have arg-checked before coming in here"); @@ -271,7 +286,9 @@ public override object CreateInstance(IServiceProvider provider, Type objectType /// type implements a Type constructor, and if it does it invokes that ctor. /// Otherwise, it just tries to create the type. /// - private static object CreateInstance(Type objectType, Type callingType) + private static object CreateInstance( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, + Type callingType) { return objectType.GetConstructor(s_typeConstructor)?.Invoke(new object[] { callingType }) ?? Activator.CreateInstance(objectType); @@ -333,7 +350,7 @@ internal string GetComponentName([DynamicallyAccessedMembers(DynamicallyAccessed /// it will be used to retrieve attributes. Otherwise, _type /// will be used. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode("NullableConverter's UnderlyingType cannot be statically discovered. The Type of instance cannot be statically discovered.")] internal TypeConverter GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) { ReflectedTypeData td = GetTypeData(type, true); @@ -354,7 +371,7 @@ internal EventDescriptor GetDefaultEvent([DynamicallyAccessedMembers(Dynamically /// /// Return the default property. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of instance cannot be statically discovered.")] internal PropertyDescriptor GetDefaultProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance) { ReflectedTypeData td = GetTypeData(type, true); @@ -364,7 +381,7 @@ internal PropertyDescriptor GetDefaultProperty([DynamicallyAccessedMembers(Dynam /// /// Retrieves the editor for the given base type. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " The Type of instance cannot be statically discovered.")] internal object GetEditor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, object instance, Type editorBaseType) { ReflectedTypeData td = GetTypeData(type, true); @@ -374,6 +391,7 @@ internal object GetEditor([DynamicallyAccessedMembers(DynamicallyAccessedMemberT /// /// Retrieves a default editor table for the given editor base type. /// + [RequiresUnreferencedCode("The Types specified in EditorTables may be trimmed, or have their static construtors trimmed.")] private static Hashtable GetEditorTable(Type editorBaseType) { Hashtable editorTables = EditorTables; @@ -458,7 +476,7 @@ internal string GetExtendedComponentName(object instance) /// it will be used to retrieve attributes. Otherwise, _type /// will be used. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode("The Type of instance cannot be statically discovered. NullableConverter's UnderlyingType cannot be statically discovered.")] internal TypeConverter GetExtendedConverter(object instance) { return GetConverter(instance.GetType(), instance); @@ -484,7 +502,7 @@ internal PropertyDescriptor GetExtendedDefaultProperty(object instance) /// /// Retrieves the editor for the given base type. /// - [RequiresUnreferencedCode("The Type of instance cannot be statically discovered.")] + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode + " The Type of instance cannot be statically discovered.")] internal object GetExtendedEditor(object instance, Type editorBaseType) { return GetEditor(instance.GetType(), instance, editorBaseType); @@ -831,6 +849,7 @@ internal Type[] GetPopulatedTypes(Module module) /// /// Retrieves the properties for this type. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] internal PropertyDescriptorCollection GetProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { ReflectedTypeData td = GetTypeData(type, true); @@ -914,7 +933,12 @@ public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMemb /// /// Retrieves a type from a name. /// - private static Type GetTypeFromName(string typeName) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2057:TypeGetType", + Justification = "typeName is annotated with DynamicallyAccessedMembers, which will preserve the type. " + + "Using the non-assembly qualified type name will still work.")] + private static Type GetTypeFromName( + // Using PublicParameterlessConstructor to preserve the type. See https://github.com/mono/linker/issues/1878 + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) { Type t = Type.GetType(typeName); @@ -1195,6 +1219,7 @@ private static PropertyDescriptor[] ReflectGetExtendedProperties(IExtenderProvid /// Static helper API around reflection to get and cache /// properties. This does not recurse to the base class. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] private static PropertyDescriptor[] ReflectGetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { @@ -1295,6 +1320,7 @@ internal void Refresh(Type type) /// for types as needed. These instances are stored back into the table /// for the base type, and for the original component type, for fast access. /// + [RequiresUnreferencedCode(TypeDescriptor.EditorRequiresUnreferencedCode)] private static object GetIntrinsicTypeEditor(Hashtable table, Type callingType) { object hashEntry = null; @@ -1416,6 +1442,7 @@ private static object GetIntrinsicTypeEditor(Hashtable table, Type callingType) /// The strongly-typed dictionary maps object types to converter data objects which lazily /// creates (and caches for re-use, where applicable) converter instances. /// + [RequiresUnreferencedCode("NullableConverter's UnderlyingType cannot be statically discovered.")] private static TypeConverter GetIntrinsicTypeConverter(Type callingType) { TypeConverter converter; diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs index 8b7a63387a5f1..ce6f1eb1acd95 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ToolboxItemAttribute.cs @@ -11,7 +11,9 @@ namespace System.ComponentModel [AttributeUsage(AttributeTargets.All)] public class ToolboxItemAttribute : Attribute { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] private Type _toolboxItemType; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] private readonly string _toolboxItemTypeName; /// @@ -44,7 +46,7 @@ public ToolboxItemAttribute(bool defaultType) /// /// Initializes a new instance of ToolboxItemAttribute and specifies the name of the type. /// - public ToolboxItemAttribute(string toolboxItemTypeName) + public ToolboxItemAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] string toolboxItemTypeName) { _toolboxItemTypeName = toolboxItemTypeName ?? throw new ArgumentNullException(nameof(toolboxItemTypeName)); } @@ -52,7 +54,7 @@ public ToolboxItemAttribute(string toolboxItemTypeName) /// /// Initializes a new instance of ToolboxItemAttribute and specifies the type of the toolbox item. /// - public ToolboxItemAttribute(Type toolboxItemType) + public ToolboxItemAttribute([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type toolboxItemType) { if (toolboxItemType == null) { @@ -66,6 +68,7 @@ public ToolboxItemAttribute(Type toolboxItemType) /// /// Gets the toolbox item's type. /// + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public Type ToolboxItemType { get @@ -88,6 +91,7 @@ public Type ToolboxItemType } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] public string ToolboxItemTypeName => _toolboxItemTypeName ?? string.Empty; public override bool Equals(object obj) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs index f16fb9efac11f..2c3c09367605c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeConverter.cs @@ -13,6 +13,8 @@ namespace System.ComponentModel /// public class TypeConverter { + internal const string RequiresUnreferencedCodeMessage = "Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All."; + /// /// Gets a value indicating whether this converter can convert an object in the /// given source type to the native type of the converter. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs index 2c4ad1fcbdba1..125a3f16ee061 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptionProvider.cs @@ -50,7 +50,11 @@ protected TypeDescriptionProvider(TypeDescriptionProvider parent) /// parent provider was passed. If a parent provider was passed, this /// method will invoke the parent provider's CreateInstance method. /// - public virtual object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args) + public virtual object CreateInstance( + IServiceProvider provider, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, + Type[] argTypes, + object[] args) { if (_parent != null) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index d4136ddb87fbe..63ad4b6208ef4 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -19,6 +19,7 @@ namespace System.ComponentModel public sealed class TypeDescriptor { internal const DynamicallyAccessedMemberTypes ReflectTypesDynamicallyAccessedMembers = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields; + internal const string EditorRequiresUnreferencedCode = "Editors registered in TypeDescriptor.AddEditorTable may be trimmed."; // Note: this is initialized at class load because we // lock on it for thread safety. It is used from nearly @@ -167,6 +168,7 @@ public static TypeDescriptionProvider AddAttributes(object instance, params Attr /// an editor table for the editor type, if one can be found. /// [EditorBrowsable(EditorBrowsableState.Advanced)] + [RequiresUnreferencedCode("The Types specified in table may be trimmed, or have their static construtors trimmed.")] public static void AddEditorTable(Type editorBaseType, Hashtable table) { ReflectTypeDescriptionProvider.AddEditorTable(editorBaseType, table); @@ -411,7 +413,11 @@ public static void CreateAssociation(object primary, object secondary) /// /// This dynamically binds an EventDescriptor to a type. /// - public static EventDescriptor CreateEvent(Type componentType, string name, Type type, params Attribute[] attributes) + public static EventDescriptor CreateEvent( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, + string name, + Type type, + params Attribute[] attributes) { return new ReflectEventDescriptor(componentType, name, type, attributes); } @@ -420,7 +426,10 @@ public static EventDescriptor CreateEvent(Type componentType, string name, Type /// This creates a new event descriptor identical to an existing event descriptor. The new event descriptor /// has the specified metadata attributes merged with the existing metadata attributes. /// - public static EventDescriptor CreateEvent(Type componentType, EventDescriptor oldEventDescriptor, params Attribute[] attributes) + public static EventDescriptor CreateEvent( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, + EventDescriptor oldEventDescriptor, + params Attribute[] attributes) { return new ReflectEventDescriptor(componentType, oldEventDescriptor, attributes); } @@ -430,7 +439,11 @@ public static EventDescriptor CreateEvent(Type componentType, EventDescriptor ol /// a TypeDescriptionProvider object that is associated with the given /// data type. If it finds one, it will delegate the call to that object. /// - public static object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args) + public static object CreateInstance( + IServiceProvider provider, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, + Type[] argTypes, + object[] args) { if (objectType == null) { @@ -465,6 +478,7 @@ public static object CreateInstance(IServiceProvider provider, Type objectType, /// /// This dynamically binds a PropertyDescriptor to a type. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public static PropertyDescriptor CreateProperty( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, string name, @@ -478,6 +492,7 @@ public static PropertyDescriptor CreateProperty( /// This creates a new property descriptor identical to an existing property descriptor. The new property descriptor /// has the specified metadata attributes merged with the existing metadata attributes. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public static PropertyDescriptor CreateProperty( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, PropertyDescriptor oldPropertyDescriptor, @@ -793,14 +808,14 @@ public static string GetComponentName(object component, bool noCustomTypeDesc) /// /// Gets a type converter for the type of the specified component. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] public static TypeConverter GetConverter(object component) => GetConverter(component, false); /// /// Gets a type converter for the type of the specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] public static TypeConverter GetConverter(object component, bool noCustomTypeDesc) { TypeConverter converter = GetDescriptor(component, noCustomTypeDesc).GetConverter(); @@ -810,12 +825,19 @@ public static TypeConverter GetConverter(object component, bool noCustomTypeDesc /// /// Gets a type converter for the specified type. /// + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] public static TypeConverter GetConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { return GetDescriptor(type, nameof(type)).GetConverter(); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The callers of this method ensure getting the converter is trim compatible - i.e. the type is not Nullable.")] + internal static TypeConverter GetConverterTrimUnsafe([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) => + GetConverter(type); + // This is called by System.ComponentModel.DefaultValueAttribute via reflection. + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] private static object ConvertFromInvariantString([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, string stringValue) { return GetConverter(type).ConvertFromInvariantString(stringValue); @@ -824,6 +846,7 @@ private static object ConvertFromInvariantString([DynamicallyAccessedMembers(Dyn /// /// Gets the default event for the specified type of component. /// + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] public static EventDescriptor GetDefaultEvent( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType) { @@ -839,14 +862,14 @@ public static EventDescriptor GetDefaultEvent( /// /// Gets the default event for the specified component. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] public static EventDescriptor GetDefaultEvent(object component) => GetDefaultEvent(component, false); /// /// Gets the default event for a component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage + " The Type of component cannot be statically discovered.")] public static EventDescriptor GetDefaultEvent(object component, bool noCustomTypeDesc) { if (component == null) @@ -861,6 +884,7 @@ public static EventDescriptor GetDefaultEvent(object component, bool noCustomTyp /// /// Gets the default property for the specified type of component. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public static PropertyDescriptor GetDefaultProperty( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType) { @@ -876,14 +900,14 @@ public static PropertyDescriptor GetDefaultProperty( /// /// Gets the default property for the specified component. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered.")] public static PropertyDescriptor GetDefaultProperty(object component) => GetDefaultProperty(component, false); /// /// Gets the default property for the specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered.")] public static PropertyDescriptor GetDefaultProperty(object component, bool noCustomTypeDesc) { if (component == null) @@ -953,7 +977,7 @@ internal static ICustomTypeDescriptor GetExtendedDescriptor(object component) /// Gets an editor with the specified base type for the /// specified component. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode + " The Type of component cannot be statically discovered.")] public static object GetEditor(object component, Type editorBaseType) { return GetEditor(component, editorBaseType, false); @@ -964,7 +988,7 @@ public static object GetEditor(object component, Type editorBaseType) /// specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode + " The Type of component cannot be statically discovered.")] public static object GetEditor(object component, Type editorBaseType, bool noCustomTypeDesc) { if (editorBaseType == null) @@ -978,6 +1002,7 @@ public static object GetEditor(object component, Type editorBaseType, bool noCus /// /// Gets an editor with the specified base type for the specified type. /// + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] public static object GetEditor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, Type editorBaseType) @@ -1221,6 +1246,7 @@ private static Type GetNodeForBaseType(Type searchType) /// /// Gets a collection of properties for a specified type of component. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] public static PropertyDescriptorCollection GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType) { @@ -1237,7 +1263,7 @@ public static PropertyDescriptorCollection GetProperties( /// Gets a collection of properties for a specified type of /// component using a specified array of attributes as a filter. /// - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public static PropertyDescriptorCollection GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type componentType, Attribute[] attributes) @@ -1265,7 +1291,7 @@ public static PropertyDescriptorCollection GetProperties( /// /// Gets a collection of properties for a specified component. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered.")] public static PropertyDescriptorCollection GetProperties(object component) { return GetProperties(component, false); @@ -1275,7 +1301,7 @@ public static PropertyDescriptorCollection GetProperties(object component) /// Gets a collection of properties for a specified component. /// [EditorBrowsable(EditorBrowsableState.Advanced)] - [RequiresUnreferencedCode("The Type of component cannot be statically discovered.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered.")] public static PropertyDescriptorCollection GetProperties(object component, bool noCustomTypeDesc) { return GetPropertiesImpl(component, null, noCustomTypeDesc, true); @@ -1286,7 +1312,7 @@ public static PropertyDescriptorCollection GetProperties(object component, bool /// component using a specified array of attributes /// as a filter. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public static PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes) { return GetProperties(component, attributes, false); @@ -1297,7 +1323,7 @@ public static PropertyDescriptorCollection GetProperties(object component, Attri /// component using a specified array of attributes /// as a filter. /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] public static PropertyDescriptorCollection GetProperties(object component, Attribute[] attributes, bool noCustomTypeDesc) { return GetPropertiesImpl(component, attributes, noCustomTypeDesc, false); @@ -1308,7 +1334,7 @@ public static PropertyDescriptorCollection GetProperties(object component, Attri /// only if noAttributes is false. This is to preserve backward compat for the case when /// no attribute filter was passed in (as against passing in null). /// - [RequiresUnreferencedCode("The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " The Type of component cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] private static PropertyDescriptorCollection GetPropertiesImpl(object component, Attribute[] attributes, bool noCustomTypeDesc, bool noAttributes) { if (component == null) @@ -2444,7 +2470,6 @@ public static IComNativeDescriptorHandler ComNativeDescriptorHandler } } - /// /// The RemoveAssociation method removes an association with an object. /// @@ -2696,18 +2721,22 @@ internal ComNativeTypeDescriptor(IComNativeDescriptorHandler handler, object ins string ICustomTypeDescriptor.GetComponentName() => null; + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] TypeConverter ICustomTypeDescriptor.GetConverter() => _handler.GetConverter(_instance); + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return _handler.GetDefaultEvent(_instance); } + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return _handler.GetDefaultProperty(_instance); } + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return _handler.GetEditor(_instance, editorBaseType); @@ -2724,12 +2753,13 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes return _handler.GetEvents(_instance, attributes); } + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return _handler.GetProperties(_instance, null); } - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { return _handler.GetProperties(_instance, attributes); @@ -2767,7 +2797,7 @@ public override ICustomTypeDescriptor GetTypeDescriptor([DynamicallyAccessedMemb /// /// Our custom type descriptor. /// - private class AttributeTypeDescriptor : CustomTypeDescriptor + private sealed class AttributeTypeDescriptor : CustomTypeDescriptor { private readonly Attribute[] _attributeArray; @@ -2919,10 +2949,12 @@ private sealed class ComNativeDescriptorProxy : TypeDescriptionProvider { private readonly TypeDescriptionProvider _comNativeDescriptor; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The trimmer can't find the ComNativeDescriptor type when System.Windows.Forms isn't available. " + + "When System.Windows.Forms is available, the type will be seen by the trimmer and the ctor will be preserved.")] public ComNativeDescriptorProxy() { - Assembly assembly = Assembly.Load("System.Windows.Forms"); - Type realComNativeDescriptor = assembly.GetType("System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor", throwOnError: true); + Type realComNativeDescriptor = Type.GetType("System.Windows.Forms.ComponentModel.Com2Interop.ComNativeDescriptor, System.Windows.Forms", throwOnError: true); _comNativeDescriptor = (TypeDescriptionProvider)Activator.CreateInstance(realComNativeDescriptor); } @@ -2985,6 +3017,7 @@ string ICustomTypeDescriptor.GetComponentName() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] TypeConverter ICustomTypeDescriptor.GetConverter() { TypeConverter converter = _primary.GetConverter() ?? _secondary.GetConverter(); @@ -2996,6 +3029,7 @@ TypeConverter ICustomTypeDescriptor.GetConverter() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return _primary.GetDefaultEvent() ?? _secondary.GetDefaultEvent(); @@ -3004,6 +3038,7 @@ EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return _primary.GetDefaultProperty() ?? _secondary.GetDefaultProperty(); @@ -3012,6 +3047,7 @@ PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (editorBaseType == null) @@ -3050,6 +3086,7 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { PropertyDescriptorCollection properties = _primary.GetProperties() ?? _secondary.GetProperties(); @@ -3061,7 +3098,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() /// /// ICustomTypeDescriptor implementation. /// - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { PropertyDescriptorCollection properties = _primary.GetProperties(attributes); @@ -3109,7 +3146,11 @@ internal TypeDescriptionNode(TypeDescriptionProvider provider) /// Implements CreateInstance. This just walks the linked list /// looking for someone who implements the call. /// - public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args) + public override object CreateInstance( + IServiceProvider provider, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, + Type[] argTypes, + object[] args) { if (objectType == null) { @@ -3334,7 +3375,7 @@ string ICustomTypeDescriptor.GetComponentName() /// /// ICustomTypeDescriptor implementation. /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] TypeConverter ICustomTypeDescriptor.GetConverter() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3357,7 +3398,7 @@ TypeConverter ICustomTypeDescriptor.GetConverter() /// /// ICustomTypeDescriptor implementation. /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3378,7 +3419,7 @@ EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() /// /// ICustomTypeDescriptor implementation. /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3398,7 +3439,7 @@ PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() /// /// ICustomTypeDescriptor implementation. /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (editorBaseType == null) @@ -3472,7 +3513,7 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes /// /// ICustomTypeDescriptor implementation. /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The ctor of this Type has RequiresUnreferencedCode.")] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3494,7 +3535,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() /// /// ICustomTypeDescriptor implementation. /// - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3645,6 +3686,7 @@ string ICustomTypeDescriptor.GetComponentName() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(TypeConverter.RequiresUnreferencedCodeMessage)] TypeConverter ICustomTypeDescriptor.GetConverter() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3672,6 +3714,7 @@ TypeConverter ICustomTypeDescriptor.GetConverter() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(EventDescriptor.RequiresUnreferencedCodeMessage)] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3697,6 +3740,7 @@ EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3722,6 +3766,7 @@ PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(EditorRequiresUnreferencedCode)] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (editorBaseType == null) @@ -3807,6 +3852,7 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes /// /// ICustomTypeDescriptor implementation. /// + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. @@ -3834,7 +3880,7 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() /// /// ICustomTypeDescriptor implementation. /// - [RequiresUnreferencedCode(AttributeCollection.FilterRequiresUnreferencedCodeMessage)] + [RequiresUnreferencedCode(PropertyDescriptor.PropertyDescriptorPropertyTypeMessage + " " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { // Check to see if the provider we get is a ReflectTypeDescriptionProvider. diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs index 56618993e49ad..4234963d248de 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/WeakHashtable.cs @@ -101,7 +101,7 @@ private void ScavengeKeys() _lastHashCount = hashCount; } - private class WeakKeyComparer : IEqualityComparer + private sealed class WeakKeyComparer : IEqualityComparer { bool IEqualityComparer.Equals(object x, object y) { diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs index 1dcd8567f2283..b1358ee9a615e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/ColorConverter.cs @@ -78,7 +78,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul } string sep = culture.TextInfo.ListSeparator + " "; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); string[] args; int nArg = 0; @@ -150,7 +150,7 @@ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContex public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true; - private class ColorComparer : IComparer + private sealed class ColorComparer : IComparer { public int Compare(Color left, Color right) => string.CompareOrdinal(left.Name, right.Name); } diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs index ca62378f801e5..fbabbeef3383f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/PointConverter.cs @@ -41,7 +41,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c char sep = culture.TextInfo.ListSeparator[0]; string[] tokens = text.Split(sep); int[] values = new int[tokens.Length]; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. @@ -78,7 +78,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul } string sep = culture.TextInfo.ListSeparator + " "; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); // Note: ConvertFromString will raise exception if value cannot be converted. var args = new string[] diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs index 8a4df6c0b0d51..6b5f2090e0b50 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/RectangleConverter.cs @@ -41,7 +41,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c char sep = culture.TextInfo.ListSeparator[0]; string[] tokens = text.Split(sep); int[] values = new int[tokens.Length]; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. @@ -76,7 +76,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul } string sep = culture.TextInfo.ListSeparator + " "; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); // Note: ConvertToString will raise exception if value cannot be converted. var args = new string[] diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs index d71fee467c97b..c2884b04b7994 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeConverter.cs @@ -41,7 +41,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c char sep = culture.TextInfo.ListSeparator[0]; string[] tokens = text.Split(sep); int[] values = new int[tokens.Length]; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); for (int i = 0; i < values.Length; i++) { // Note: ConvertFromString will raise exception if value cannot be converted. @@ -76,7 +76,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul } string sep = culture.TextInfo.ListSeparator + " "; - TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + TypeConverter intConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(int)); // Note: ConvertToString will raise exception if value cannot be converted. var args = new string[] diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs index 61bb9c5beeb99..25f038d73efa2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Drawing/SizeFConverter.cs @@ -41,7 +41,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c char sep = culture.TextInfo.ListSeparator[0]; string[] tokens = text.Split(sep); float[] values = new float[tokens.Length]; - TypeConverter floatConverter = TypeDescriptor.GetConverter(typeof(float)); + TypeConverter floatConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(float)); for (int i = 0; i < values.Length; i++) { values[i] = (float)floatConverter.ConvertFromString(context, culture, tokens[i]); @@ -75,7 +75,7 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul } string sep = culture.TextInfo.ListSeparator + " "; - TypeConverter floatConverter = TypeDescriptor.GetConverter(typeof(float)); + TypeConverter floatConverter = TypeDescriptor.GetConverterTrimUnsafe(typeof(float)); var args = new string[] { floatConverter.ConvertToString(context, culture, size.Width), diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs index 2ca5f86258412..241b41bc0f604 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/InvariantComparer.cs @@ -6,7 +6,7 @@ namespace System { - internal class InvariantComparer : IComparer + internal sealed class InvariantComparer : IComparer { private readonly CompareInfo _compareInfo; internal static readonly InvariantComparer Default = new InvariantComparer(); diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/Design/DesigntimeLicenseContextSerializerTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/Design/DesigntimeLicenseContextSerializerTests.cs new file mode 100644 index 0000000000000..131d6f73c4e28 --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/Design/DesigntimeLicenseContextSerializerTests.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.IO; +using System.Reflection; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.ComponentModel.Design.Tests +{ + public static class DesigntimeLicenseContextSerializerTests + { + private const string enableBinaryFormatterInTypeConverter = "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization"; + private const string enableBinaryFormatter = "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization"; + + public static bool AreBinaryFormatterAndRemoteExecutorSupportedOnThisPlatform => PlatformDetection.IsBinaryFormatterSupported && RemoteExecutor.IsSupported; + + private static void VerifyStreamFormatting(Stream stream) + { + AppContext.TryGetSwitch(enableBinaryFormatterInTypeConverter, out bool binaryFormatterUsageInTypeConverterIsEnabled); + long position = stream.Position; + int firstByte = stream.ReadByte(); + if (binaryFormatterUsageInTypeConverterIsEnabled) + { + Assert.Equal(0, firstByte); + } + else + { + Assert.Equal(255, firstByte); + } + stream.Seek(position, SeekOrigin.Begin); + } + + [ConditionalTheory(nameof(AreBinaryFormatterAndRemoteExecutorSupportedOnThisPlatform))] + [InlineData(false, "key")] + [InlineData(true, "key")] + [InlineData(false, "")] + [InlineData(true, "")] + public static void SerializeAndDeserialize(bool useBinaryFormatter, string key) + { + RemoteInvokeOptions options = new RemoteInvokeOptions(); + if (useBinaryFormatter) + { + options.RuntimeConfigurationOptions.Add(enableBinaryFormatterInTypeConverter, bool.TrueString); + } + RemoteExecutor.Invoke((key) => + { + var context = new DesigntimeLicenseContext(); + context.SetSavedLicenseKey(typeof(int), key); + var assembly = typeof(DesigntimeLicenseContextSerializer).Assembly; + Type runtimeLicenseContextType = assembly.GetType("System.ComponentModel.Design.RuntimeLicenseContext"); + Assert.NotNull(runtimeLicenseContextType); + object runtimeLicenseContext = Activator.CreateInstance(runtimeLicenseContextType); + FieldInfo _savedLicenseKeys = runtimeLicenseContextType.GetField("_savedLicenseKeys", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(_savedLicenseKeys); + _savedLicenseKeys.SetValue(runtimeLicenseContext, new Hashtable()); + Assert.NotNull(runtimeLicenseContext); + + Type designtimeLicenseContextSerializer = assembly.GetType("System.ComponentModel.Design.DesigntimeLicenseContextSerializer"); + Assert.NotNull(designtimeLicenseContextSerializer); + MethodInfo deserializeMethod = designtimeLicenseContextSerializer.GetMethod("Deserialize", BindingFlags.NonPublic | BindingFlags.Static); + + using (MemoryStream stream = new MemoryStream()) + { + long position = stream.Position; + DesigntimeLicenseContextSerializer.Serialize(stream, key, context); + stream.Seek(position, SeekOrigin.Begin); + VerifyStreamFormatting(stream); + deserializeMethod.Invoke(null, new object[] { stream, key, runtimeLicenseContext }); + Hashtable savedLicenseKeys = runtimeLicenseContext.GetType().GetField("_savedLicenseKeys", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(runtimeLicenseContext) as Hashtable; + Assert.NotNull(savedLicenseKeys); + var value = savedLicenseKeys[typeof(int).AssemblyQualifiedName]; + Assert.True(value is string); + Assert.Equal(key, value); + } + }, key, options).Dispose(); + } + + [ConditionalTheory(nameof(AreBinaryFormatterAndRemoteExecutorSupportedOnThisPlatform))] + [InlineData("key")] + [InlineData("")] + public static void SerializeWithBinaryFormatter_DeserializeWithBinaryWriter(string key) + { + AppContext.SetSwitch(enableBinaryFormatter, true); + AppContext.SetSwitch(enableBinaryFormatterInTypeConverter, true); + var context = new DesigntimeLicenseContext(); + context.SetSavedLicenseKey(typeof(int), key); + string tempPath = Path.GetTempPath(); + try + { + + using (MemoryStream stream = new MemoryStream()) + { + long position = stream.Position; + DesigntimeLicenseContextSerializer.Serialize(stream, key, context); + stream.Seek(position, SeekOrigin.Begin); + VerifyStreamFormatting(stream); + + using (FileStream outStream = File.Create(Path.Combine(tempPath, "_temp_SerializeWithBinaryFormatter_DeserializeWithBinaryWriter"))) + { + stream.Seek(position, SeekOrigin.Begin); + stream.CopyTo(outStream); + } + } + + RemoteInvokeHandle handle = RemoteExecutor.Invoke((key) => + { + var assembly = typeof(DesigntimeLicenseContextSerializer).Assembly; + Type runtimeLicenseContextType = assembly.GetType("System.ComponentModel.Design.RuntimeLicenseContext"); + Assert.NotNull(runtimeLicenseContextType); + object runtimeLicenseContext = Activator.CreateInstance(runtimeLicenseContextType); + Assert.NotNull(runtimeLicenseContext); + FieldInfo _savedLicenseKeys = runtimeLicenseContextType.GetField("_savedLicenseKeys", BindingFlags.NonPublic | BindingFlags.Instance); + Assert.NotNull(_savedLicenseKeys); + _savedLicenseKeys.SetValue(runtimeLicenseContext, new Hashtable()); + + Type designtimeLicenseContextSerializer = assembly.GetType("System.ComponentModel.Design.DesigntimeLicenseContextSerializer"); + Assert.NotNull(designtimeLicenseContextSerializer); + MethodInfo deserializeMethod = designtimeLicenseContextSerializer.GetMethod("Deserialize", BindingFlags.NonPublic | BindingFlags.Static); + Assert.NotNull(deserializeMethod); + + string tempPath = Path.GetTempPath(); + using (FileStream stream = File.Open(Path.Combine(tempPath, "_temp_SerializeWithBinaryFormatter_DeserializeWithBinaryWriter"), FileMode.Open)) + { + TargetInvocationException exception = Assert.Throws(() => deserializeMethod.Invoke(null, new object[] { stream, key, runtimeLicenseContext })); + Assert.IsType(exception.InnerException); + } + }, key); + + handle.Process.WaitForExit(); + handle.Dispose(); + } + finally + { + File.Delete(Path.Combine(tempPath, "_temp_SerializeWithBinaryFormatter_DeserializeWithBinaryWriter")); + } + } + } +} diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj index 49f136f0c0b97..645e898ea5150 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj @@ -74,6 +74,7 @@ + diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/System.ComponentModel.TypeConverter.TrimmingTests.proj b/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/System.ComponentModel.TypeConverter.TrimmingTests.proj index da4a46f2ae141..b60e51f170cb2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/System.ComponentModel.TypeConverter.TrimmingTests.proj +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TrimmingTests/System.ComponentModel.TypeConverter.TrimmingTests.proj @@ -1,5 +1,12 @@ + + + + + + + diff --git a/src/libraries/System.Composition.Convention/src/System/Composition/Convention/PartConventionBuilderOfT.cs b/src/libraries/System.Composition.Convention/src/System/Composition/Convention/PartConventionBuilderOfT.cs index f1f021f9a3f7c..717acbefec684 100644 --- a/src/libraries/System.Composition.Convention/src/System/Composition/Convention/PartConventionBuilderOfT.cs +++ b/src/libraries/System.Composition.Convention/src/System/Composition/Convention/PartConventionBuilderOfT.cs @@ -13,7 +13,7 @@ namespace System.Composition.Convention /// The type of the part, or a type to which the part is assignable. public class PartConventionBuilder : PartConventionBuilder { - private class MethodExpressionAdapter + private sealed class MethodExpressionAdapter { private readonly MethodInfo _methodInfo; @@ -48,7 +48,7 @@ private static MethodInfo SelectMethods(Expression> methodSelector) throw new ArgumentException(SR.Format(SR.Argument_ExpressionMustBeVoidMethodWithNoArguments, nameof(methodSelector)), nameof(methodSelector)); } - protected static Expression> Reduce(Expression> expr) + private static Expression> Reduce(Expression> expr) { while (expr.CanReduce) { @@ -57,7 +57,7 @@ protected static Expression> Reduce(Expression> return expr; } - protected static Expression> Reduce(Expression> expr) + private static Expression> Reduce(Expression> expr) { while (expr.CanReduce) { @@ -67,7 +67,7 @@ protected static Expression> Reduce(Expression> expr) } } - private class PropertyExpressionAdapter + private sealed class PropertyExpressionAdapter { private readonly PropertyInfo _propertyInfo; private readonly Action _configureImport; @@ -119,7 +119,7 @@ private static PropertyInfo SelectProperties(Expression> propert throw new ArgumentException(SR.Format(SR.Argument_ExpressionMustBePropertyMember, nameof(propertySelector)), nameof(propertySelector)); } - protected static Expression> Reduce(Expression> expr) + private static Expression> Reduce(Expression> expr) { while (expr.CanReduce) { @@ -129,7 +129,7 @@ protected static Expression> Reduce(Expression> } } - private class ConstructorExpressionAdapter + private sealed class ConstructorExpressionAdapter { private ConstructorInfo _constructorInfo; private Dictionary> _importBuilders; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingExportDescriptor.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingExportDescriptor.cs index 8d3983a896b23..1d6670a2688b8 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingExportDescriptor.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingExportDescriptor.cs @@ -6,7 +6,7 @@ namespace System.Composition.Hosting.Core { - internal class CycleBreakingExportDescriptor : ExportDescriptor + internal sealed class CycleBreakingExportDescriptor : ExportDescriptor { private readonly Lazy _exportDescriptor; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingMetadataDictionary.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingMetadataDictionary.cs index 20776997558d9..fce39352386e6 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingMetadataDictionary.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/CycleBreakingMetadataDictionary.cs @@ -6,7 +6,7 @@ namespace System.Composition.Hosting.Core { - internal class CycleBreakingMetadataDictionary : IDictionary + internal sealed class CycleBreakingMetadataDictionary : IDictionary { private readonly Lazy _exportDescriptor; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/DirectExportDescriptor.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/DirectExportDescriptor.cs index d0d668712ddcc..f85aa1be1e153 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/DirectExportDescriptor.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/DirectExportDescriptor.cs @@ -5,7 +5,7 @@ namespace System.Composition.Hosting.Core { - internal class DirectExportDescriptor : ExportDescriptor + internal sealed class DirectExportDescriptor : ExportDescriptor { private readonly CompositeActivator _activator; private readonly IDictionary _metadata; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistry.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistry.cs index 7fb765ecbde5a..4a3cc25cdbbda 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistry.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistry.cs @@ -6,7 +6,7 @@ namespace System.Composition.Hosting.Core { - internal class ExportDescriptorRegistry + internal sealed class ExportDescriptorRegistry { private readonly object _thisLock = new object(); private readonly ExportDescriptorProvider[] _exportDescriptorProviders; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistryUpdate.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistryUpdate.cs index 43112a62232a9..2c01ff65bd108 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistryUpdate.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/ExportDescriptorRegistryUpdate.cs @@ -8,7 +8,7 @@ namespace System.Composition.Hosting.Core { - internal class ExportDescriptorRegistryUpdate : DependencyAccessor + internal sealed class ExportDescriptorRegistryUpdate : DependencyAccessor { private readonly IDictionary _partDefinitions; private readonly ExportDescriptorProvider[] _exportDescriptorProviders; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/UpdateResult.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/UpdateResult.cs index d842fdb8a0d10..0be00d63fc9eb 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/UpdateResult.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Core/UpdateResult.cs @@ -9,7 +9,7 @@ namespace System.Composition.Hosting.Core // Update results ensure that providers can query reentrantly for the contract that // they are being queried for. The UpdateResult type keeps a list of remaining // providers, with providers being removed from the list before querying. - internal class UpdateResult + internal sealed class UpdateResult { private static readonly ExportDescriptorPromise[] s_noPromises = Array.Empty(); diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/CurrentScope/CurrentScopeExportDescriptorProvider.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/CurrentScope/CurrentScopeExportDescriptorProvider.cs index 2faf7c232be2d..4e52f5178e505 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/CurrentScope/CurrentScopeExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/CurrentScope/CurrentScopeExportDescriptorProvider.cs @@ -6,7 +6,7 @@ namespace System.Composition.Hosting.Providers.CurrentScope { - internal class CurrentScopeExportDescriptorProvider : ExportDescriptorProvider + internal sealed class CurrentScopeExportDescriptorProvider : ExportDescriptorProvider { private static readonly CompositionContract s_currentScopeContract = new CompositionContract(typeof(CompositionContext)); diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryExportDescriptorProvider.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryExportDescriptorProvider.cs index 0421cb463ddcc..09b980168f1d3 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryExportDescriptorProvider.cs @@ -9,7 +9,7 @@ namespace System.Composition.Hosting.Providers.ExportFactory { - internal class ExportFactoryExportDescriptorProvider : ExportDescriptorProvider + internal sealed class ExportFactoryExportDescriptorProvider : ExportDescriptorProvider { private static readonly MethodInfo s_getExportFactoryDefinitionsMethod = typeof(ExportFactoryExportDescriptorProvider).GetTypeInfo().GetDeclaredMethod("GetExportFactoryDescriptors"); diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryWithMetadataExportDescriptorProvider.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryWithMetadataExportDescriptorProvider.cs index 6e6bb5ab3d951..0b6a07d8b410a 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryWithMetadataExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ExportFactory/ExportFactoryWithMetadataExportDescriptorProvider.cs @@ -10,7 +10,7 @@ namespace System.Composition.Hosting.Providers.ExportFactory { - internal class ExportFactoryWithMetadataExportDescriptorProvider : ExportDescriptorProvider + internal sealed class ExportFactoryWithMetadataExportDescriptorProvider : ExportDescriptorProvider { private static readonly MethodInfo s_getLazyDefinitionsMethod = typeof(ExportFactoryWithMetadataExportDescriptorProvider).GetTypeInfo().GetDeclaredMethod("GetExportFactoryDescriptors"); diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ImportMany/ImportManyExportDescriptorProvider.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ImportMany/ImportManyExportDescriptorProvider.cs index 41c1ff4fb9cc9..254909aac7734 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ImportMany/ImportManyExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/ImportMany/ImportManyExportDescriptorProvider.cs @@ -9,7 +9,7 @@ namespace System.Composition.Hosting.Providers.ImportMany { - internal class ImportManyExportDescriptorProvider : ExportDescriptorProvider + internal sealed class ImportManyExportDescriptorProvider : ExportDescriptorProvider { private static readonly MethodInfo s_getImportManyDefinitionMethod = typeof(ImportManyExportDescriptorProvider).GetTypeInfo().GetDeclaredMethod("GetImportManyDescriptor"); private static readonly Type[] s_supportedContractTypes = new[] { typeof(IList<>), typeof(ICollection<>), typeof(IEnumerable<>) }; diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyExportDescriptorProvider.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyExportDescriptorProvider.cs index fd749ab93bbc9..6bb04a2a8be5c 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyExportDescriptorProvider.cs @@ -9,7 +9,7 @@ namespace System.Composition.Hosting.Providers.Lazy { - internal class LazyExportDescriptorProvider : ExportDescriptorProvider + internal sealed class LazyExportDescriptorProvider : ExportDescriptorProvider { private static readonly MethodInfo s_getLazyDefinitionsMethod = typeof(LazyExportDescriptorProvider) .GetTypeInfo().GetDeclaredMethod("GetLazyDefinitions"); diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyWithMetadataExportDescriptorProvider.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyWithMetadataExportDescriptorProvider.cs index e33e425150f21..e2460c1a84c98 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyWithMetadataExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Providers/Lazy/LazyWithMetadataExportDescriptorProvider.cs @@ -10,7 +10,7 @@ namespace System.Composition.Hosting.Providers.Lazy { - internal class LazyWithMetadataExportDescriptorProvider : ExportDescriptorProvider + internal sealed class LazyWithMetadataExportDescriptorProvider : ExportDescriptorProvider { private static readonly MethodInfo s_getLazyDefinitionsMethod = typeof(LazyWithMetadataExportDescriptorProvider).GetTypeInfo().GetDeclaredMethod("GetLazyDefinitions"); diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/Formatters.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/Formatters.cs index a61eec7d61fc2..d76687440ee46 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/Formatters.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/Formatters.cs @@ -47,7 +47,7 @@ private static string FormatClosedGeneric(Type closedGenericType) var name = closedGenericType.Name.Substring(0, closedGenericType.Name.IndexOf('`')); var args = closedGenericType.GenericTypeArguments.Select(t => Format(t)); - return string.Format("{0}<{1}>", name, string.Join(", ", args)); + return $"{name}<{string.Join(", ", args)}>"; } } } diff --git a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/SmallSparseInitonlyArray.cs b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/SmallSparseInitonlyArray.cs index 2b1abc61c8e44..bd026a95f8c82 100644 --- a/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/SmallSparseInitonlyArray.cs +++ b/src/libraries/System.Composition.Hosting/src/System/Composition/Hosting/Util/SmallSparseInitonlyArray.cs @@ -6,9 +6,9 @@ namespace System.Composition.Hosting.Util // Extremely performance-sensitive. // Always safe for reading, even under concurrent writes, // only one writer at a time allowed. - internal class SmallSparseInitonlyArray + internal sealed class SmallSparseInitonlyArray { - private class Element { public int Index; public object Value; } + private sealed class Element { public int Index; public object Value; } private const int ElementsCapacity = 128; private const int ElementIndexMask = 127; diff --git a/src/libraries/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs b/src/libraries/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs index f703345120442..3024c07ae2072 100644 --- a/src/libraries/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs +++ b/src/libraries/System.Composition.Hosting/tests/System/Composition/Hosting/Core/CompositionHostTests.cs @@ -133,6 +133,7 @@ public override IEnumerable GetExportDescriptors(Compos [InlineData(typeof(Lazy>))] [InlineData(typeof(Lazy))] [InlineData(typeof(Lazy))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void GetExport_InvalidMetadata_ThrowsComposititionFailedException(Type type) { using (CompositionHost host = CompositionHost.CreateCompositionHost(new ExportDescriptorProvider[0])) diff --git a/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs b/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs index 6e8090ac45bdd..c584217d8501e 100644 --- a/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs +++ b/src/libraries/System.Composition.Runtime/src/System/Composition/Hosting/Core/CompositionContract.cs @@ -110,7 +110,7 @@ public override string ToString() if (_metadataConstraints != null) result += string.Format(" {{ {0} }}", string.Join(SR.Formatter_ListSeparatorWithSpace, - _metadataConstraints.Select(kv => string.Format("{0} = {1}", kv.Key, Formatters.Format(kv.Value))))); + _metadataConstraints.Select(kv => $"{kv.Key} = {Formatters.Format(kv.Value)}"))); return result; } diff --git a/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs b/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs index 608aa72d73e0e..bf11f260c8a33 100644 --- a/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs +++ b/src/libraries/System.Composition.Runtime/src/System/Composition/Runtime/Util/Formatters.cs @@ -36,7 +36,7 @@ private static string FormatClosedGeneric(Type closedGenericType) Debug.Assert(closedGenericType.IsConstructedGenericType); var name = closedGenericType.Name.Substring(0, closedGenericType.Name.IndexOf('`')); IEnumerable args = closedGenericType.GenericTypeArguments.Select(t => Format(t)); - return string.Format("{0}<{1}>", name, string.Join(SR.Formatter_ListSeparatorWithSpace, args)); + return $"{name}<{string.Join(SR.Formatter_ListSeparatorWithSpace, args)}>"; } } } diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/ContainerConfigurationDebuggerProxy.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/ContainerConfigurationDebuggerProxy.cs index 5e91bf571c0a7..a899bbef59a63 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/ContainerConfigurationDebuggerProxy.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/ContainerConfigurationDebuggerProxy.cs @@ -12,7 +12,7 @@ namespace System.Composition.Debugging { - internal class ContainerConfigurationDebuggerProxy + internal sealed class ContainerConfigurationDebuggerProxy { private readonly ContainerConfiguration _configuration; private DiscoveredPart[] _discoveredParts; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/DiscoveredPartDebuggerProxy.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/DiscoveredPartDebuggerProxy.cs index d3cf45f301e08..9c6a51f5439e5 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/DiscoveredPartDebuggerProxy.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/Debugging/DiscoveredPartDebuggerProxy.cs @@ -8,7 +8,7 @@ namespace System.Composition.Debugging { - internal class DiscoveredPartDebuggerProxy + internal sealed class DiscoveredPartDebuggerProxy { private readonly DiscoveredPart _discoveredPart; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/DisposalFeature.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/DisposalFeature.cs index 1d5317fb473ec..a6624e45d981b 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/DisposalFeature.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/DisposalFeature.cs @@ -10,7 +10,7 @@ namespace System.Composition.TypedParts.ActivationFeatures /// /// Modifies the activator so that disposable instances are bound to the appropriate scope. /// - internal class DisposalFeature : ActivationFeature + internal sealed class DisposalFeature : ActivationFeature { public override CompositeActivator RewriteActivator( TypeInfo partType, diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/LifetimeFeature.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/LifetimeFeature.cs index 554bfc3552dd1..3735067e3be06 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/LifetimeFeature.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/LifetimeFeature.cs @@ -11,7 +11,7 @@ namespace System.Composition.TypedParts.ActivationFeatures /// Modifies the activators of parts so that they a) get associated with the correct /// scope, and b) obtain their dependencies from the correct scope. /// - internal class LifetimeFeature : ActivationFeature + internal sealed class LifetimeFeature : ActivationFeature { public const string SharingBoundaryPartMetadataName = "SharingBoundary"; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/OnImportsSatisfiedFeature.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/OnImportsSatisfiedFeature.cs index 8d06b69cdf25d..0824f2e318b0b 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/OnImportsSatisfiedFeature.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/OnImportsSatisfiedFeature.cs @@ -15,7 +15,7 @@ namespace System.Composition.TypedParts.ActivationFeatures /// Modifies activators of parts that have so that /// their [OnImportsSatisfied] method is correctly called. /// - internal class OnImportsSatisfiedFeature : ActivationFeature + internal sealed class OnImportsSatisfiedFeature : ActivationFeature { private readonly AttributedModelProvider _attributeContext; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyImportSite.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyImportSite.cs index 980e73b64ed23..29f7f2e735e5d 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyImportSite.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyImportSite.cs @@ -8,7 +8,7 @@ namespace System.Composition.TypedParts.ActivationFeatures /// /// Represents a part property that is configured as an import. /// - internal class PropertyImportSite + internal sealed class PropertyImportSite { private readonly PropertyInfo _pi; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyInjectionFeature.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyInjectionFeature.cs index 558f482b62bed..82f73a87ba5f4 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyInjectionFeature.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ActivationFeatures/PropertyInjectionFeature.cs @@ -14,7 +14,7 @@ namespace System.Composition.TypedParts.ActivationFeatures /// Modifies activators of parts with property imports so that the properties /// are set appropriately. /// - internal class PropertyInjectionFeature : ActivationFeature + internal sealed class PropertyInjectionFeature : ActivationFeature { private readonly AttributedModelProvider _attributeContext; private static readonly MethodInfo s_activatorInvokeMethod = typeof(CompositeActivator).GetTypeInfo().GetDeclaredMethod("Invoke"); diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredInstanceExport.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredInstanceExport.cs index fd21c84e1f47e..0fd6823ad614a 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredInstanceExport.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredInstanceExport.cs @@ -7,7 +7,7 @@ namespace System.Composition.TypedParts.Discovery { - internal class DiscoveredInstanceExport : DiscoveredExport + internal sealed class DiscoveredInstanceExport : DiscoveredExport { public DiscoveredInstanceExport(CompositionContract contract, IDictionary metadata) : base(contract, metadata) diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPart.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPart.cs index 97f66ef898f14..2f3f6dcc923eb 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPart.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPart.cs @@ -17,7 +17,7 @@ namespace System.Composition.TypedParts.Discovery { [DebuggerDisplay("{PartType.Name}")] [DebuggerTypeProxy(typeof(DiscoveredPartDebuggerProxy))] - internal class DiscoveredPart + internal sealed class DiscoveredPart { private readonly TypeInfo _partType; private readonly AttributedModelProvider _attributeContext; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPropertyExport.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPropertyExport.cs index 3367cc7bbe00b..098e39facd328 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPropertyExport.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/DiscoveredPropertyExport.cs @@ -8,7 +8,7 @@ namespace System.Composition.TypedParts.Discovery { - internal class DiscoveredPropertyExport : DiscoveredExport + internal sealed class DiscoveredPropertyExport : DiscoveredExport { private static readonly MethodInfo s_activatorInvoke = typeof(CompositeActivator).GetRuntimeMethod("Invoke", new[] { typeof(LifetimeContext), typeof(CompositionOperation) }); diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/ParameterImportSite.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/ParameterImportSite.cs index 01a120d93ad36..6e0a54013d2d6 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/ParameterImportSite.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/ParameterImportSite.cs @@ -5,7 +5,7 @@ namespace System.Composition.TypedParts.Discovery { - internal class ParameterImportSite + internal sealed class ParameterImportSite { private readonly ParameterInfo _pi; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/TypeInspector.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/TypeInspector.cs index aeab2667cc116..d6d63828d1f2f 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/TypeInspector.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Discovery/TypeInspector.cs @@ -11,7 +11,7 @@ namespace System.Composition.TypedParts.Discovery { - internal class TypeInspector + internal sealed class TypeInspector { private static readonly IDictionary s_noMetadata = new Dictionary(); diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ImportInfo.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ImportInfo.cs index caa7e02d21320..41ce4e1a16c97 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ImportInfo.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/ImportInfo.cs @@ -5,7 +5,7 @@ namespace System.Composition.TypedParts { - internal class ImportInfo + internal sealed class ImportInfo { private readonly CompositionContract _exportKey; private readonly bool _allowDefault; diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/TypedPartExportDescriptorProvider.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/TypedPartExportDescriptorProvider.cs index 68e4b2d687791..7c3a276c9abab 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/TypedPartExportDescriptorProvider.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/TypedPartExportDescriptorProvider.cs @@ -11,7 +11,7 @@ namespace System.Composition.TypedParts { - internal class TypedPartExportDescriptorProvider : ExportDescriptorProvider + internal sealed class TypedPartExportDescriptorProvider : ExportDescriptorProvider { private readonly IDictionary> _discoveredParts = new Dictionary>(); diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs index 7e3e7670f96bb..2b54b8c1785a2 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/TypedParts/Util/DirectAttributeContext.cs @@ -7,7 +7,7 @@ namespace System.Composition.TypedParts.Util { - internal class DirectAttributeContext : AttributedModelProvider + internal sealed class DirectAttributeContext : AttributedModelProvider { public override IEnumerable GetCustomAttributes(Type reflectedType, Reflection.MemberInfo member) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs index 1ca9090e874f4..9adac8c52ded9 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/BaseConfigurationRecord.cs @@ -3739,7 +3739,7 @@ internal void ClearStreamInfos() } } - private class IndirectLocationInputComparer : IComparer + private sealed class IndirectLocationInputComparer : IComparer { public int Compare(SectionInput x, SectionInput y) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs index 6c95205da167a..0f41dd3a3732b 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs @@ -10,7 +10,7 @@ namespace System.Configuration { - internal class ClientConfigPaths + internal sealed class ClientConfigPaths { internal const string UserConfigFilename = "user.config"; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigDefinitionUpdates.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigDefinitionUpdates.cs index 53039f2420a63..277ec9673bda4 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigDefinitionUpdates.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigDefinitionUpdates.cs @@ -6,7 +6,7 @@ namespace System.Configuration using Collections; // Contains all the updates to section definitions across all location sections. - internal class ConfigDefinitionUpdates + internal sealed class ConfigDefinitionUpdates { internal ConfigDefinitionUpdates() { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationElementCollection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationElementCollection.cs index 735cc78756671..7665fc0f56959 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationElementCollection.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationElementCollection.cs @@ -1237,7 +1237,7 @@ private enum EntryType Added, } - private class Entry + private sealed class Entry { private readonly object _key; internal EntryType EntryType; @@ -1257,7 +1257,7 @@ internal object GetKey(ConfigurationElementCollection thisCollection) } } - private class Enumerator : IDictionaryEnumerator + private sealed class Enumerator : IDictionaryEnumerator { private readonly IEnumerator _itemsEnumerator; private readonly ConfigurationElementCollection _thisCollection; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationSchemaErrors.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationSchemaErrors.cs index 4ccdf5721ec73..662c2132f7e18 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationSchemaErrors.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationSchemaErrors.cs @@ -5,7 +5,7 @@ namespace System.Configuration { - internal class ConfigurationSchemaErrors + internal sealed class ConfigurationSchemaErrors { // All errors related to a config file are logged to this list. // This includes all global errors, all non-specific errors, diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValue.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValue.cs index 392354f625791..ae71204a15a87 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValue.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValue.cs @@ -3,7 +3,7 @@ namespace System.Configuration { - internal class ConfigurationValue + internal sealed class ConfigurationValue { internal PropertySourceInfo SourceInfo; internal object Value; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValues.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValues.cs index 4bdda6f749aef..3ee1830a90adb 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValues.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ConfigurationValues.cs @@ -6,7 +6,7 @@ namespace System.Configuration { - internal class ConfigurationValues : NameObjectCollectionBase + internal sealed class ConfigurationValues : NameObjectCollectionBase { private static volatile IEnumerable s_emptyCollection; private BaseConfigurationRecord _configRecord; @@ -140,7 +140,7 @@ internal bool IsInherited(string key) return false; } - private class EmptyCollection : IEnumerable + private sealed class EmptyCollection : IEnumerable { private readonly IEnumerator _emptyEnumerator; @@ -154,7 +154,7 @@ IEnumerator IEnumerable.GetEnumerator() return _emptyEnumerator; } - private class EmptyCollectionEnumerator : IEnumerator + private sealed class EmptyCollectionEnumerator : IEnumerator { bool IEnumerator.MoveNext() { @@ -167,7 +167,7 @@ void IEnumerator.Reset() { } } } - private class ConfigurationElementsCollection : IEnumerable + private sealed class ConfigurationElementsCollection : IEnumerable { private readonly ConfigurationValues _values; @@ -189,7 +189,7 @@ IEnumerator IEnumerable.GetEnumerator() } } - private class InvalidValuesCollection : IEnumerable + private sealed class InvalidValuesCollection : IEnumerable { private readonly ConfigurationValues _values; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DateTimeConfigurationElement.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DateTimeConfigurationElement.cs index 7787c7cfb5230..348b5eacc3e42 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DateTimeConfigurationElement.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DateTimeConfigurationElement.cs @@ -3,7 +3,7 @@ namespace System.Configuration { - internal class DateTimeConfigurationElement : ConfigurationElement + internal sealed class DateTimeConfigurationElement : ConfigurationElement { private static readonly ConfigurationProperty s_propValue = new ConfigurationProperty("value", typeof(DateTime), DateTime.MinValue, ConfigurationPropertyOptions.IsKey); diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DeclarationUpdate.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DeclarationUpdate.cs index bcb95e8711c40..00bfa3b1472f5 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DeclarationUpdate.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DeclarationUpdate.cs @@ -3,7 +3,7 @@ namespace System.Configuration { - internal class DeclarationUpdate : Update + internal sealed class DeclarationUpdate : Update { internal DeclarationUpdate(string configKey, bool moved, string updatedXml) : base(configKey, moved, updatedXml) { } } diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DefinitionUpdate.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DefinitionUpdate.cs index 4e18a36adadb2..f74b0f23bb0d4 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DefinitionUpdate.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/DefinitionUpdate.cs @@ -3,7 +3,7 @@ namespace System.Configuration { - internal class DefinitionUpdate : Update + internal sealed class DefinitionUpdate : Update { internal DefinitionUpdate(string configKey, bool moved, string updatedXml, SectionRecord sectionRecord) : base(configKey, moved, updatedXml) diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/EmptyImpersonationContext.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/EmptyImpersonationContext.cs index b88fa8c0a6c66..28a5972cbcdcd 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/EmptyImpersonationContext.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/EmptyImpersonationContext.cs @@ -4,7 +4,7 @@ namespace System.Configuration { // Used in cases where the Host does not require impersonation. - internal class EmptyImpersonationContext : IDisposable + internal sealed class EmptyImpersonationContext : IDisposable { private static volatile IDisposable s_emptyImpersonationContext; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryId.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryId.cs index 1386922b87d0f..3ccc5b375d07a 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryId.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryId.cs @@ -7,7 +7,7 @@ namespace System.Configuration { // Identifies a factory [DebuggerDisplay("FactoryId {ConfigKey}")] - internal class FactoryId + internal sealed class FactoryId { internal FactoryId(string configKey, string group, string name) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryRecord.cs index d30062e8b3621..01c8b4006f5d1 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/FactoryRecord.cs @@ -8,7 +8,7 @@ namespace System.Configuration { [DebuggerDisplay("FactoryRecord {ConfigKey}")] - internal class FactoryRecord : IConfigErrorInfo + internal sealed class FactoryRecord : IConfigErrorInfo { private const int FlagAllowLocation = 0x0001; // Does the factory allow location directives? private const int FlagRestartOnExternalChanges = 0x0002; // Restart on external changes? diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/HandlerBase.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/HandlerBase.cs index d1601ee198c25..2247623923d22 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/HandlerBase.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/HandlerBase.cs @@ -6,12 +6,8 @@ namespace System.Configuration { - internal class HandlerBase + internal static class HandlerBase { - private HandlerBase() - { - } - private static XmlNode GetAndRemoveAttribute(XmlNode node, string attrib, bool fRequired) { XmlNode a = node.Attributes.RemoveNamedItem(attrib); diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/IdnElement.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/IdnElement.cs index 09ad27a71cd63..96134625ea553 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/IdnElement.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/IdnElement.cs @@ -36,7 +36,7 @@ public UriIdnScope Enabled set { this[_enabled] = value; } } - private class UriIdnScopeTypeConverter : TypeConverter + private sealed class UriIdnScopeTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs index f762d328e584a..36e4cd1786f3b 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ImplicitMachineConfigHost.cs @@ -7,7 +7,7 @@ namespace System.Configuration { - internal class ImplicitMachineConfigHost : DelegatingConfigHost + internal sealed class ImplicitMachineConfigHost : DelegatingConfigHost { private string _machineStreamName; private ConfigurationFileMap _fileMap; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/ConfigSystem.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/ConfigSystem.cs index 27098b83a5838..9de6356fc3bf6 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/ConfigSystem.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/ConfigSystem.cs @@ -4,7 +4,7 @@ namespace System.Configuration.Internal { // The runtime config system - internal class ConfigSystem : IConfigSystem + internal sealed class ConfigSystem : IConfigSystem { private IInternalConfigHost _configHost; private IInternalConfigRoot _configRoot; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/DummyDisposable.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/DummyDisposable.cs index 211c5db394b9a..94282ea1c46ef 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/DummyDisposable.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/DummyDisposable.cs @@ -6,7 +6,7 @@ namespace System.Configuration.Internal /// /// Used to satisfy legacy interfaces. /// - internal class DummyDisposable : IDisposable + internal sealed class DummyDisposable : IDisposable { public void Dispose() { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/FileVersion.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/FileVersion.cs index a572a5246f6be..386e19ccfa1ab 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/FileVersion.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/FileVersion.cs @@ -3,7 +3,7 @@ namespace System.Configuration.Internal { - internal class FileVersion + internal sealed class FileVersion { private readonly bool _exists; private readonly long _fileSize; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/WriteFileContext.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/WriteFileContext.cs index 8033ead725fa2..ae47e20cc90ca 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/WriteFileContext.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/Internal/WriteFileContext.cs @@ -15,7 +15,7 @@ namespace System.Configuration.Internal { - internal class WriteFileContext + internal sealed class WriteFileContext { private const int SavingTimeout = 10000; // 10 seconds private const int SavingRetryInterval = 100; // 100 milliseconds diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/KeyValueInternalCollection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/KeyValueInternalCollection.cs index 77efac671354a..0941d9cbe4376 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/KeyValueInternalCollection.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/KeyValueInternalCollection.cs @@ -5,7 +5,7 @@ namespace System.Configuration { - internal class KeyValueInternalCollection : NameValueCollection + internal sealed class KeyValueInternalCollection : NameValueCollection { private readonly AppSettingsSection _root; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs index b7b3e46664876..e477c06d252ba 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs @@ -530,7 +530,7 @@ private void Upgrade(SettingsContext context, SettingsPropertyCollection propert } } - private class XmlEscaper + private sealed class XmlEscaper { private readonly XmlDocument document; private readonly XmlElement tempElement; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationSectionRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationSectionRecord.cs index 03b50f474b7ad..51a5b856064fe 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationSectionRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationSectionRecord.cs @@ -7,7 +7,7 @@ namespace System.Configuration { [DebuggerDisplay("LocationSectionRecord {ConfigKey}")] - internal class LocationSectionRecord + internal sealed class LocationSectionRecord { private List _errors; // errors diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationUpdates.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationUpdates.cs index 1b9b31eaed49d..63cba1292df9f 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationUpdates.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocationUpdates.cs @@ -4,7 +4,7 @@ namespace System.Configuration { // LocationUpdates contains all the updates that share the same location characteristics. - internal class LocationUpdates + internal sealed class LocationUpdates { internal LocationUpdates(OverrideModeSetting overrideMode, bool inheritInChildApps) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/PropertySourceInfo.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/PropertySourceInfo.cs index 1a3fe6399a99e..7558081e4cf12 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/PropertySourceInfo.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/PropertySourceInfo.cs @@ -6,7 +6,7 @@ namespace System.Configuration { - internal class PropertySourceInfo + internal sealed class PropertySourceInfo { internal PropertySourceInfo(XmlReader reader) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ReadOnlyNameValueCollection.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ReadOnlyNameValueCollection.cs index a086a973bc8a6..65eb97616ad0e 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ReadOnlyNameValueCollection.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ReadOnlyNameValueCollection.cs @@ -6,7 +6,7 @@ namespace System.Configuration { - internal class ReadOnlyNameValueCollection : NameValueCollection + internal sealed class ReadOnlyNameValueCollection : NameValueCollection { internal ReadOnlyNameValueCollection(IEqualityComparer equalityComparer) : base(equalityComparer) diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/RuntimeConfigurationRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/RuntimeConfigurationRecord.cs index 4df65174e3d6a..9f9ae18faecfe 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/RuntimeConfigurationRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/RuntimeConfigurationRecord.cs @@ -79,7 +79,7 @@ protected override object GetRuntimeObject(object result) return runtimeObject; } - private class RuntimeConfigurationFactory + private sealed class RuntimeConfigurationFactory { private ConstructorInfo _sectionCtor; private IConfigurationSectionHandler _sectionHandler; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionInput.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionInput.cs index 32fc227e9e6e0..2a960418087d5 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionInput.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionInput.cs @@ -7,7 +7,7 @@ namespace System.Configuration { [DebuggerDisplay("SectionInput {SectionXmlInfo.ConfigKey}")] - internal class SectionInput + internal sealed class SectionInput { // result can be null, so we use this object to indicate whether it has been evaluated private static readonly object s_unevaluated = new object(); diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionRecord.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionRecord.cs index 744028a98b9da..8697ea131dbc1 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionRecord.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionRecord.cs @@ -7,7 +7,7 @@ namespace System.Configuration { [DebuggerDisplay("SectionRecord {ConfigKey}")] - internal class SectionRecord + internal sealed class SectionRecord { // // Runtime flags below 0x10000 diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionUpdates.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionUpdates.cs index 7dde1e555e2ff..d56275b403756 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionUpdates.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/SectionUpdates.cs @@ -6,7 +6,7 @@ namespace System.Configuration { - internal class SectionUpdates + internal sealed class SectionUpdates { private readonly Hashtable _groups; private readonly string _name; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamInfo.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamInfo.cs index 7caf90c32757b..6400550d00a1a 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamInfo.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamInfo.cs @@ -4,7 +4,7 @@ namespace System.Configuration { // Information about a stream used in configuration - internal class StreamInfo + internal sealed class StreamInfo { // the configSource directive that generated this stream, null for a full config file diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamUpdate.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamUpdate.cs index a942a2edb7528..2f503a6b0e552 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamUpdate.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/StreamUpdate.cs @@ -3,7 +3,7 @@ namespace System.Configuration { - internal class StreamUpdate + internal sealed class StreamUpdate { internal StreamUpdate(string newStreamname) { diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/UpdateConfigHost.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/UpdateConfigHost.cs index 6655e464999a6..f8e7be8f94efa 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/UpdateConfigHost.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/UpdateConfigHost.cs @@ -10,7 +10,7 @@ namespace System.Configuration { // Configuration host that intercepts calls to filename functions // to support SaveAs to an alternate file stream. - internal class UpdateConfigHost : DelegatingConfigHost + internal sealed class UpdateConfigHost : DelegatingConfigHost { private HybridDictionary _streams; // oldStreamname -> StreamUpdate diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs index 161e6336a2d99..9094bc6c44904 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/XmlUtilWriter.cs @@ -19,7 +19,7 @@ namespace System.Configuration // // Note also that this class is used at runtime to copy sections, so performance of all // writing functions directly affects application startup time. - internal class XmlUtilWriter + internal sealed class XmlUtilWriter { private const char Space = ' '; private const string NewLine = "\r\n"; @@ -356,7 +356,7 @@ internal void RestoreStreamCheckpoint(object o) } // Class that contains the state of the writer and its underlying stream. - private class StreamWriterCheckpoint + private sealed class StreamWriterCheckpoint { internal readonly bool _isLastLineBlank; internal readonly int _lineNumber; diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs index e03e0ab3c5a84..349cbe833c80a 100644 --- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs +++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs @@ -973,7 +973,7 @@ private static unsafe void EnsureInitializedCore() } /// Provides format strings and related information for use with the current terminal. - internal class TerminalFormatStrings + internal sealed class TerminalFormatStrings { /// Gets the lazily-initialized terminal information for the terminal. public static TerminalFormatStrings Instance { get { return s_instance.Value; } } diff --git a/src/libraries/System.Console/tests/NonStandardConfiguration.Unix.cs b/src/libraries/System.Console/tests/NonStandardConfiguration.Unix.cs index 35067d2067b09..c9d2b09fe5e1c 100644 --- a/src/libraries/System.Console/tests/NonStandardConfiguration.Unix.cs +++ b/src/libraries/System.Console/tests/NonStandardConfiguration.Unix.cs @@ -12,6 +12,7 @@ public partial class NonStandardConfigurationTests { [PlatformSpecific(TestPlatforms.AnyUnix)] // Uses P/Invokes [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void NonBlockingStdout_AllDataReceived() { RemoteInvokeHandle remote = RemoteExecutor.Invoke(() => diff --git a/src/libraries/System.Console/tests/ReadKey.cs b/src/libraries/System.Console/tests/ReadKey.cs index 42481f367a6a3..e0429983e7a70 100644 --- a/src/libraries/System.Console/tests/ReadKey.cs +++ b/src/libraries/System.Console/tests/ReadKey.cs @@ -23,6 +23,7 @@ public static void KeyAvailable() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void RedirectedConsole_ReadKey() { RunRemote(() => { Assert.Throws(() => Console.ReadKey()); return 42; }, new ProcessStartInfo() { RedirectStandardInput = true }); diff --git a/src/libraries/System.Console/tests/RedirectedStream.cs b/src/libraries/System.Console/tests/RedirectedStream.cs index 578824c8cce01..ee2afb9bbd6e6 100644 --- a/src/libraries/System.Console/tests/RedirectedStream.cs +++ b/src/libraries/System.Console/tests/RedirectedStream.cs @@ -11,6 +11,7 @@ using Microsoft.DotNet.XUnitExtensions; using Xunit; +[ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class RedirectedStream { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] // the CI system redirects stdout, so we can only really test the redirected behavior. diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.cs index 9995c1d06557a..28821d99590dd 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.cs @@ -468,6 +468,7 @@ public void EndEdit() { } System.ComponentModel.TypeConverter System.ComponentModel.ICustomTypeDescriptor.GetConverter() { throw null; } System.ComponentModel.EventDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent() { throw null; } System.ComponentModel.PropertyDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] @@ -2058,6 +2059,7 @@ void System.Collections.IDictionary.Remove(object keyword) { } System.ComponentModel.TypeConverter System.ComponentModel.ICustomTypeDescriptor.GetConverter() { throw null; } System.ComponentModel.EventDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent() { throw null; } System.ComponentModel.PropertyDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] @@ -2235,6 +2237,7 @@ protected DbDataRecord() { } System.ComponentModel.TypeConverter System.ComponentModel.ICustomTypeDescriptor.GetConverter() { throw null; } System.ComponentModel.EventDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent() { throw null; } System.ComponentModel.PropertyDescriptor System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object System.ComponentModel.ICustomTypeDescriptor.GetEditor(System.Type editorBaseType) { throw null; } System.ComponentModel.EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] diff --git a/src/libraries/System.Data.Common/ref/System.Data.Common.manual.cs b/src/libraries/System.Data.Common/ref/System.Data.Common.manual.cs index ecb29300efe59..9a9414de07a42 100644 --- a/src/libraries/System.Data.Common/ref/System.Data.Common.manual.cs +++ b/src/libraries/System.Data.Common/ref/System.Data.Common.manual.cs @@ -9,11 +9,11 @@ namespace System.Data { [System.ComponentModel.TypeConverter(typeof(ConstraintConverter))] public abstract partial class Constraint { } - internal class ConstraintConverter { } + internal sealed class ConstraintConverter { } [System.ComponentModel.TypeConverter(typeof(RelationshipConverter))] public partial class DataRelation { } - internal class RelationshipConverter { } + internal sealed class RelationshipConverter { } public partial class DataColumn { @@ -27,8 +27,8 @@ public partial class DataColumn [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public object DefaultValue { get { throw null; } set { } } } - internal class ColumnTypeConverter { } - internal class DefaultValueTypeConverter { } + internal sealed class ColumnTypeConverter { } + internal sealed class DefaultValueTypeConverter { } public partial class DataTable { @@ -37,7 +37,7 @@ public partial class DataTable [System.Diagnostics.CodeAnalysis.AllowNullAttribute] public System.Data.DataColumn[] PrimaryKey { get { throw null; } set { } } } - internal class PrimaryKeyTypeConverter { } + internal sealed class PrimaryKeyTypeConverter { } public partial class DataView { @@ -46,18 +46,18 @@ public partial class DataView [System.ComponentModel.TypeConverter(typeof(DataTableTypeConverter))] public System.Data.DataTable? Table { get { throw null; } set { } } } - internal class DataTableTypeConverter { } + internal sealed class DataTableTypeConverter { } } namespace System.Data.Common { [System.ComponentModel.TypeConverterAttribute(typeof(DataColumnMapping.DataColumnMappingConverter))] public sealed partial class DataColumnMapping { - internal class DataColumnMappingConverter { } + internal sealed class DataColumnMappingConverter { } } [System.ComponentModel.TypeConverterAttribute(typeof(DataTableMapping.DataTableMappingConverter))] public sealed partial class DataTableMapping { - internal class DataTableMappingConverter { } + internal sealed class DataTableMappingConverter { } } } diff --git a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml index aae5e3cc281c9..a0aae1367cb5f 100644 --- a/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Data.Common/src/ILLink/ILLink.Suppressions.xml @@ -247,5 +247,77 @@ member M:System.Data.XMLSchema.GetConverter(System.Type) + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.ConvertObjectToXml(System.Object) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.ConvertObjectToXml(System.Object,System.Xml.XmlWriter,System.Xml.Serialization.XmlRootAttribute) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.ConvertObjectToXml(System.Object,System.Xml.XmlReader,System.Xml.Serialization.XmlRootAttribute) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.ConvertXmlToObject(System.Xml.XmlReader,System.Xml.Serialization.XmlRootAttribute) + + + ILLink + IL2026 + member + M:System.Data.Common.SqlUdtStorage.ConvertObjectToXml(System.Object) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.ConvertXmlToObject(System.Object) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.ConvertXmlToObject(System.String) + + + ILLink + IL2026 + member + M:System.Data.Common.SqlUdtStorage.ConvertObjectToXml(System.Object,System.Xml.XmlWriter,System.Xml.Serialization.XmlRootAttribute) + + + ILLink + IL2026 + member + M:System.Data.Common.SqlUdtStorage.ConvertXmlToObject(System.String) + + + ILLink + IL2026 + member + M:System.Data.Common.SqlUdtStorage.ConvertXmlToObject(System.Xml.XmlReader,System.Xml.Serialization.XmlRootAttribute) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.GetXmlSerializer(System.Type) + + + ILLink + IL2026 + member + M:System.Data.Common.ObjectStorage.GetXmlSerializer(System.Type,System.Xml.Serialization.XmlRootAttribute) + diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DBCommandBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DBCommandBuilder.cs index dfc46cfcb2185..7472c15a749fd 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DBCommandBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DBCommandBuilder.cs @@ -12,7 +12,7 @@ namespace System.Data.Common { public abstract class DbCommandBuilder : Component { - private class ParameterNames + private sealed class ParameterNames { private const string DefaultOriginalPrefix = "Original_"; private const string DefaultIsNullPrefix = "IsNull_"; diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataCommonEventSource.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataCommonEventSource.cs index d7e6a2e036121..8150768023a94 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataCommonEventSource.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataCommonEventSource.cs @@ -7,7 +7,7 @@ namespace System.Data { [EventSource(Name = "System.Data.DataCommonEventSource")] - internal class DataCommonEventSource : EventSource + internal sealed class DataCommonEventSource : EventSource { internal static readonly DataCommonEventSource Log = new DataCommonEventSource(); private static long s_nextScopeId; diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs index e18d3aff22e3a..e9f240e9d74d9 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DataRecordInternal.cs @@ -318,22 +318,25 @@ string ICustomTypeDescriptor.GetComponentName() return null; } + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] TypeConverter ICustomTypeDescriptor.GetConverter() { return null; } + [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return null; } - + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return null; } + [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return null; @@ -351,13 +354,13 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes return new EventDescriptorCollection(null); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "No Attributes are supplied.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(null); } - [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { if (_propertyDescriptors == null) diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs index 1bd2205a64168..3c3ac63548ef8 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilder.cs @@ -576,27 +576,32 @@ AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } + [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] TypeConverter ICustomTypeDescriptor.GetConverter() { return TypeDescriptor.GetConverter(this, true); } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { return TypeDescriptor.GetDefaultProperty(this, true); } + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return GetProperties(); } - [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { return GetProperties(attributes); } + [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs index 16e792bcdfacc..1ab0b414920ea 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbConnectionStringBuilderDescriptor.cs @@ -5,7 +5,7 @@ namespace System.Data.Common { - internal class DbConnectionStringBuilderDescriptor : PropertyDescriptor + internal sealed class DbConnectionStringBuilderDescriptor : PropertyDescriptor { internal DbConnectionStringBuilderDescriptor(string propertyName, Type componentType, Type propertyType, bool isReadOnly, Attribute[] attributes) : base(propertyName, attributes) { diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs index 11ba1945f3f29..b2ee9ec585767 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/DbDataRecord.cs @@ -77,12 +77,16 @@ protected virtual DbDataReader GetDbDataReader(int i) string ICustomTypeDescriptor.GetComponentName() => null; + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] TypeConverter ICustomTypeDescriptor.GetConverter() => null; + [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; + [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; EventDescriptorCollection ICustomTypeDescriptor.GetEvents() => new EventDescriptorCollection(null); @@ -90,11 +94,11 @@ protected virtual DbDataReader GetDbDataReader(int i) [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => new EventDescriptorCollection(null); - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "No Attributes are supplied.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => ((ICustomTypeDescriptor)this).GetProperties(null); - [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => new PropertyDescriptorCollection(null); diff --git a/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs b/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs index 6c2c947aa0156..a59749d46aa16 100644 --- a/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs +++ b/src/libraries/System.Data.Common/src/System/Data/Common/ObjectStorage.cs @@ -617,7 +617,7 @@ internal static XmlSerializer GetXmlSerializer(Type type, XmlRootAttribute attri return serializer; } - private class TempAssemblyComparer : IEqualityComparer> + private sealed class TempAssemblyComparer : IEqualityComparer> { internal static readonly IEqualityComparer> s_default = new TempAssemblyComparer(); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs index 50e01e4852f9d..4b86f5f59fa8b 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataRowView.cs @@ -236,19 +236,28 @@ public void EndEdit() AttributeCollection ICustomTypeDescriptor.GetAttributes() => new AttributeCollection(null); string ICustomTypeDescriptor.GetClassName() => null; string ICustomTypeDescriptor.GetComponentName() => null; + + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] TypeConverter ICustomTypeDescriptor.GetConverter() => null; + + [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; + + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; + + [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() => new EventDescriptorCollection(null); [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) => new EventDescriptorCollection(null); - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "No Attributes are supplied.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => ((ICustomTypeDescriptor)this).GetProperties(null); - [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => (_dataView.Table != null ? _dataView.Table.GetPropertyDescriptorCollection(attributes) : s_zeroPropertyDescriptorCollection); diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs index 0f76e3d604c85..8850e45f122ae 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewManager.cs @@ -287,7 +287,7 @@ PropertyDescriptorCollection System.ComponentModel.ITypedList.GetItemProperties( if (listAccessors == null || listAccessors.Length == 0) { - return ((ICustomTypeDescriptor)(new DataViewManagerListItemTypeDescriptor(this))).GetProperties(); + return new DataViewManagerListItemTypeDescriptor(this).GetPropertiesInternal(); } else { diff --git a/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs b/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs index 734b4f5d597a8..20b61fc1a3651 100644 --- a/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs +++ b/src/libraries/System.Data.Common/src/System/Data/DataViewManagerListItemTypeDescriptor.cs @@ -51,21 +51,25 @@ internal DataView GetDataView(DataTable table) /// /// Retrieves the type converter for this object. /// + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] TypeConverter ICustomTypeDescriptor.GetConverter() => null; /// /// Retrieves the default event. /// + [RequiresUnreferencedCode("The built-in EventDescriptor implementation uses Reflection which requires unreferenced code.")] EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() => null; /// /// Retrieves the default property. /// + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() => null; /// /// Retrieves the an editor for this object. /// + [RequiresUnreferencedCode("Editors registered in TypeDescriptor.AddEditorTable may be trimmed.")] object ICustomTypeDescriptor.GetEditor(Type editorBaseType) => null; /// @@ -93,9 +97,9 @@ EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes /// provides. If the component is sited, the site may add or remove /// additional properties. /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "No Attributes are supplied.")] + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")] PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => - ((ICustomTypeDescriptor)this).GetProperties(null); + GetPropertiesInternal(); /// /// Retrieves an array of properties that the given component instance @@ -104,8 +108,11 @@ PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() => /// additional properties. The returned array of properties will be /// filtered by the given set of attributes. /// - [RequiresUnreferencedCode("The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] - PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + [RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) => + GetPropertiesInternal(); + + internal PropertyDescriptorCollection GetPropertiesInternal() { if (_propsCollection == null) { diff --git a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLString.cs b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLString.cs index e16012f11647b..1482532b98bff 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLString.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SQLTypes/SQLString.cs @@ -974,7 +974,7 @@ internal struct SLcidOrdMapItem { }; // Class to store map of lcids to ordinal - internal class CBuildLcidOrdMap { + internal sealed class CBuildLcidOrdMap { internal SLcidOrdMapItem[] m_rgLcidOrdMap; internal int m_cValidLocales; internal int m_uiPosEnglish; // Start binary searches here - this is index in array, not ordinal diff --git a/src/libraries/System.Data.Common/src/System/Data/SortExpressionBuilder.cs b/src/libraries/System.Data.Common/src/System/Data/SortExpressionBuilder.cs index c5304d94ef346..ab0fe110c4f92 100644 --- a/src/libraries/System.Data.Common/src/System/Data/SortExpressionBuilder.cs +++ b/src/libraries/System.Data.Common/src/System/Data/SortExpressionBuilder.cs @@ -12,7 +12,7 @@ namespace System.Data /// This class represents a combined sort expression build using multiple sort expressions. /// /// - internal class SortExpressionBuilder : IComparer> + internal sealed class SortExpressionBuilder : IComparer> { /** * This class ensures multiple orderby/thenbys are handled correctly. Its semantics is as follows: diff --git a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs index 634b96e20bc81..1efbb5518dfe6 100644 --- a/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs +++ b/src/libraries/System.Data.Common/src/System/Data/XMLSchema.cs @@ -12,11 +12,15 @@ using System.Collections.Generic; using System.Globalization; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace System.Data { +#pragma warning disable CA1052 // TODO: https://github.com/dotnet/roslyn-analyzers/issues/4968 internal class XMLSchema +#pragma warning restore CA1052 { + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] internal static TypeConverter GetConverter(Type type) { return TypeDescriptor.GetConverter(type); diff --git a/src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs b/src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs index d41afadd52a05..6c179537b64c9 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/Common/DbConnectionStringBuilderTest.cs @@ -1691,17 +1691,17 @@ public void EmbeddedCharTest1() sb["Data Source"] = "testdb"; sb["User ID"] = "someuser"; - sb["Password"] = "abcdef"; - Assert.Equal("Data Source=testdb;User ID=someuser;Password=abcdef", + sb["Password"] = "PLACEHOLDER"; + Assert.Equal("Data Source=testdb;User ID=someuser;Password=PLACEHOLDER", sb.ConnectionString); - sb["Password"] = "abcdef#"; - Assert.Equal("Data Source=testdb;User ID=someuser;Password=abcdef#", + sb["Password"] = "PLACEHOLDER#"; + Assert.Equal("Data Source=testdb;User ID=someuser;Password=PLACEHOLDER#", sb.ConnectionString); // an embedded single-quote value will result in the value being delimieted with double quotes - sb["Password"] = "abc\'def"; - Assert.Equal("Data Source=testdb;User ID=someuser;Password=\"abc\'def\"", + sb["Password"] = "PLACEHOLDER\'def"; + Assert.Equal("Data Source=testdb;User ID=someuser;Password=\"PLACEHOLDER\'def\"", sb.ConnectionString); // an embedded double-quote value will result in the value being delimieted with single quotes @@ -1717,39 +1717,39 @@ public void EmbeddedCharTest1() sb.ConnectionString); sb = new DbConnectionStringBuilder(); - sb["PASSWORD"] = "abcdef1"; + sb["PASSWORD"] = "PLACEHOLDERabcdef1"; sb["user id"] = "someuser"; sb["Data Source"] = "testdb"; - Assert.Equal("PASSWORD=abcdef1;user id=someuser;Data Source=testdb", + Assert.Equal("PASSWORD=PLACEHOLDERabcdef1;user id=someuser;Data Source=testdb", sb.ConnectionString); // case is preserved for a keyword that was added the first time sb = new DbConnectionStringBuilder(); - sb["PassWord"] = "abcdef2"; + sb["PassWord"] = "PLACEHOLDERabcdef2"; sb["uSER iD"] = "someuser"; sb["DaTa SoUrCe"] = "testdb"; - Assert.Equal("PassWord=abcdef2;uSER iD=someuser;DaTa SoUrCe=testdb", + Assert.Equal("PassWord=PLACEHOLDERabcdef2;uSER iD=someuser;DaTa SoUrCe=testdb", sb.ConnectionString); - sb["passWORD"] = "abc123"; - Assert.Equal("PassWord=abc123;uSER iD=someuser;DaTa SoUrCe=testdb", + sb["passWORD"] = "PLACEHOLDERabc123"; + Assert.Equal("PassWord=PLACEHOLDERabc123;uSER iD=someuser;DaTa SoUrCe=testdb", sb.ConnectionString); // embedded equal sign in the value will cause the value to be // delimited with double-quotes sb = new DbConnectionStringBuilder(); - sb["Password"] = "abc=def"; + sb["Password"] = "PLACEHOLDER=def"; sb["Data Source"] = "testdb"; sb["User ID"] = "someuser"; - Assert.Equal("Password=\"abc=def\";Data Source=testdb;User ID=someuser", + Assert.Equal("Password=\"PLACEHOLDER=def\";Data Source=testdb;User ID=someuser", sb.ConnectionString); // embedded semicolon in the value will cause the value to be // delimited with double-quotes sb = new DbConnectionStringBuilder(); - sb["Password"] = "abc;def"; + sb["Password"] = "PLACEHOLDER;def"; sb["Data Source"] = "testdb"; sb["User ID"] = "someuser"; - Assert.Equal("Password=\"abc;def\";Data Source=testdb;User ID=someuser", + Assert.Equal("Password=\"PLACEHOLDER;def\";Data Source=testdb;User ID=someuser", sb.ConnectionString); // more right parentheses then left parentheses - happily takes it @@ -1866,32 +1866,32 @@ public void EmbeddedCharTest3() DbConnectionStringBuilder sb; sb = new DbConnectionStringBuilder(); - sb.ConnectionString = "User ID=SCOTT;Password=TiGeR;Data Source=" + dataSource; + sb.ConnectionString = "User ID=SCOTT;Password=PLACEHOLDER;Data Source=" + dataSource; Assert.Equal(dataSource, sb["Data Source"]); Assert.Equal("SCOTT", sb["User ID"]); - Assert.Equal("TiGeR", sb["Password"]); + Assert.Equal("PLACEHOLDER", sb["Password"]); Assert.Equal( - "user id=SCOTT;password=TiGeR;data source=\"(DESCRIPTION=(ADDRESS=(PROTOCOL=" + + "user id=SCOTT;password=PLACEHOLDER;data source=\"(DESCRIPTION=(ADDRESS=(PROTOCOL=" + "TCP)(HOST=192.168.1.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)" + "(SERVICE_NAME=TESTDB)))\"", sb.ConnectionString); sb = new DbConnectionStringBuilder(false); - sb.ConnectionString = "User ID=SCOTT;Password=TiGeR;Data Source=" + dataSource; + sb.ConnectionString = "User ID=SCOTT;Password=PLACEHOLDER;Data Source=" + dataSource; Assert.Equal(dataSource, sb["Data Source"]); Assert.Equal("SCOTT", sb["User ID"]); - Assert.Equal("TiGeR", sb["Password"]); + Assert.Equal("PLACEHOLDER", sb["Password"]); Assert.Equal( - "user id=SCOTT;password=TiGeR;data source=\"(DESCRIPTION=(ADDRESS=(PROTOCOL=" + + "user id=SCOTT;password=PLACEHOLDER;data source=\"(DESCRIPTION=(ADDRESS=(PROTOCOL=" + "TCP)(HOST=192.168.1.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)" + "(SERVICE_NAME=TESTDB)))\"", sb.ConnectionString); sb = new DbConnectionStringBuilder(true); - sb.ConnectionString = "User ID=SCOTT;Password=TiGeR;Data Source=" + dataSource; + sb.ConnectionString = "User ID=SCOTT;Password=PLACEHOLDER;Data Source=" + dataSource; Assert.Equal(dataSource, sb["Data Source"]); Assert.Equal("SCOTT", sb["User ID"]); - Assert.Equal("TiGeR", sb["Password"]); + Assert.Equal("PLACEHOLDER", sb["Password"]); Assert.Equal( - "user id=SCOTT;password=TiGeR;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=" + + "user id=SCOTT;password=PLACEHOLDER;data source=(DESCRIPTION=(ADDRESS=(PROTOCOL=" + "TCP)(HOST=192.168.1.101)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)" + "(SERVICE_NAME=TESTDB)))", sb.ConnectionString); } @@ -1902,24 +1902,24 @@ public void EmbeddedCharTest4() DbConnectionStringBuilder sb; sb = new DbConnectionStringBuilder(); - sb.ConnectionString = "PassWord=abcdef2;uSER iD=someuser;DaTa SoUrCe=testdb"; + sb.ConnectionString = "PassWord=PLACEHOLDER;user iD=someuser;DaTa SoUrCe=testdb"; sb["Integrated Security"] = "False"; Assert.Equal( - "password=abcdef2;user id=someuser;data source=testdb;Integrated Security=False", + "password=PLACEHOLDER;user id=someuser;data source=testdb;Integrated Security=False", sb.ConnectionString); sb = new DbConnectionStringBuilder(false); - sb.ConnectionString = "PassWord=abcdef2;uSER iD=someuser;DaTa SoUrCe=testdb"; + sb.ConnectionString = "PassWord=PLACEHOLDER;uSER iD=someuser;DaTa SoUrCe=testdb"; sb["Integrated Security"] = "False"; Assert.Equal( - "password=abcdef2;user id=someuser;data source=testdb;Integrated Security=False", + "password=PLACEHOLDER;user id=someuser;data source=testdb;Integrated Security=False", sb.ConnectionString); sb = new DbConnectionStringBuilder(true); - sb.ConnectionString = "PassWord=abcdef2;uSER iD=someuser;DaTa SoUrCe=testdb"; + sb.ConnectionString = "PassWord=PLACEHOLDER;uSER iD=someuser;DaTa SoUrCe=testdb"; sb["Integrated Security"] = "False"; Assert.Equal( - "password=abcdef2;user id=someuser;data source=testdb;Integrated Security=False", + "password=PLACEHOLDER;user id=someuser;data source=testdb;Integrated Security=False", sb.ConnectionString); } diff --git a/src/libraries/System.Data.Common/tests/System/Data/ConstraintCollectionTest2.cs b/src/libraries/System.Data.Common/tests/System/Data/ConstraintCollectionTest2.cs index 69bd811175201..0dfb131aa0295 100644 --- a/src/libraries/System.Data.Common/tests/System/Data/ConstraintCollectionTest2.cs +++ b/src/libraries/System.Data.Common/tests/System/Data/ConstraintCollectionTest2.cs @@ -413,7 +413,7 @@ public void Add_SDB1() { DataTable dt = DataProvider.CreateParentDataTable(); dt.Constraints.Add("UniqueConstraint", dt.Columns["ParentId"], true); - Assert.Equal(1, (double)dt.Constraints.Count); ; + Assert.Equal(1, (double)dt.Constraints.Count); Assert.Equal("UniqueConstraint", dt.Constraints[0].ConstraintName); } diff --git a/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs b/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs index 3cf0da8ce6249..7980064d1897d 100644 --- a/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs +++ b/src/libraries/System.Data.Odbc/src/Common/System/Data/Common/SafeNativeMethods.cs @@ -5,7 +5,7 @@ namespace System.Data { - internal partial class SafeNativeMethods + internal static partial class SafeNativeMethods { internal static IntPtr LocalAlloc(IntPtr initialSize) { diff --git a/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbConnectionPoolProviderInfo.cs b/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbConnectionPoolProviderInfo.cs index 80d1a51b3c2b1..e6c50ba9d3101 100644 --- a/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbConnectionPoolProviderInfo.cs +++ b/src/libraries/System.Data.Odbc/src/Common/System/Data/ProviderBase/DbConnectionPoolProviderInfo.cs @@ -3,7 +3,7 @@ namespace System.Data.ProviderBase { - internal class DbConnectionPoolProviderInfo + internal sealed class DbConnectionPoolProviderInfo { } } diff --git a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs index 33ce321e30486..852265e095d87 100644 --- a/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs +++ b/src/libraries/System.Data.Odbc/src/System/Data/Odbc/OdbcMetaDataFactory.cs @@ -9,7 +9,7 @@ namespace System.Data.Odbc { - internal class OdbcMetaDataFactory : DbMetaDataFactory + internal sealed class OdbcMetaDataFactory : DbMetaDataFactory { private readonly struct SchemaFunctionName { diff --git a/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.Manual.cs b/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.Manual.cs index 238590972dffe..52163899c5481 100644 --- a/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.Manual.cs +++ b/src/libraries/System.Data.OleDb/ref/System.Data.OleDb.Manual.cs @@ -9,7 +9,7 @@ namespace System.Data.OleDb [System.ComponentModel.TypeConverterAttribute(typeof(OleDbParameter.OleDbParameterConverter))] public sealed partial class OleDbParameter : System.Data.Common.DbParameter, System.Data.IDataParameter, System.Data.IDbDataParameter, System.ICloneable { - internal class OleDbParameterConverter : System.ComponentModel.ExpandableObjectConverter + internal sealed class OleDbParameterConverter : System.ComponentModel.ExpandableObjectConverter { } } @@ -25,15 +25,15 @@ public sealed partial class OleDbConnectionStringBuilder : System.Data.Common.Db [System.ComponentModel.TypeConverterAttribute(typeof(OleDbProviderConverter))] public string Provider { get { throw null; } set { } } - internal class OleDbConnectionStringBuilderConverter { } - internal class OleDbServicesConverter { } - internal class OleDbProviderConverter { } + internal sealed class OleDbConnectionStringBuilderConverter { } + internal sealed class OleDbServicesConverter { } + internal sealed class OleDbProviderConverter { } } public sealed partial class OleDbException { [System.ComponentModel.TypeConverterAttribute(typeof(ErrorCodeConverter))] public override int ErrorCode { get { throw null; } } - internal class ErrorCodeConverter { } + internal sealed class ErrorCodeConverter { } } } diff --git a/src/libraries/System.Data.OleDb/src/SafeHandles.cs b/src/libraries/System.Data.OleDb/src/SafeHandles.cs index 879d572447719..d435f7f45bcd1 100644 --- a/src/libraries/System.Data.OleDb/src/SafeHandles.cs +++ b/src/libraries/System.Data.OleDb/src/SafeHandles.cs @@ -659,7 +659,7 @@ internal struct CArray #endregion PROPVARIANT - internal class NativeOledbWrapper + internal static class NativeOledbWrapper { internal static unsafe OleDbHResult IChapteredRowsetReleaseChapter(System.IntPtr ptr, System.IntPtr chapter) { diff --git a/src/libraries/System.Data.OleDb/src/System/Data/Common/DataCommonEventSource.cs b/src/libraries/System.Data.OleDb/src/System/Data/Common/DataCommonEventSource.cs index cd9e735c27e24..5fc0234b3c9b9 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/Common/DataCommonEventSource.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/Common/DataCommonEventSource.cs @@ -7,7 +7,7 @@ namespace System.Data { [EventSource(Name = "System.Data.DataCommonEventSource")] - internal class DataCommonEventSource : EventSource + internal sealed class DataCommonEventSource : EventSource { internal static readonly DataCommonEventSource Log = new DataCommonEventSource(); private const int TraceEventId = 1; diff --git a/src/libraries/System.Data.OleDb/src/System/Data/Common/DbConnectionPoolKey.cs b/src/libraries/System.Data.OleDb/src/System/Data/Common/DbConnectionPoolKey.cs index 80252bde49e87..e790c71d91719 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/Common/DbConnectionPoolKey.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/Common/DbConnectionPoolKey.cs @@ -8,7 +8,7 @@ namespace System.Data.Common { // DbConnectionPoolKey: Base class implementation of a key to connection pool groups // Only connection string is used as a key - internal class DbConnectionPoolKey : ICloneable + internal sealed class DbConnectionPoolKey : ICloneable { private string? _connectionString; @@ -17,7 +17,7 @@ internal DbConnectionPoolKey(string? connectionString) _connectionString = connectionString; } - protected DbConnectionPoolKey(DbConnectionPoolKey key) + private DbConnectionPoolKey(DbConnectionPoolKey key) { _connectionString = key.ConnectionString; } @@ -27,7 +27,7 @@ object ICloneable.Clone() return new DbConnectionPoolKey(this); } - internal virtual string? ConnectionString + internal string? ConnectionString { get { diff --git a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPoolCounters.cs b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPoolCounters.cs index e1c2f3bfca2bb..5a96e7d05f837 100644 --- a/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPoolCounters.cs +++ b/src/libraries/System.Data.OleDb/src/System/Data/ProviderBase/DbConnectionPoolCounters.cs @@ -259,7 +259,7 @@ private string GetInstanceName() // to PERFMON. They recommend that we translate them as shown below, to // prevent problems. - result = string.Format(null, "{0}[{1}]", instanceName, pid); + result = $"{instanceName}[{pid}]"; result = result.Replace('(', '[').Replace(')', ']').Replace('#', '_').Replace('/', '_').Replace('\\', '_'); // counter instance name cannot be greater than 127 @@ -272,13 +272,10 @@ private string GetInstanceName() const string insertString = "[...]"; int firstPartLength = (CounterInstanceNameMaxLength - insertString.Length) / 2; int lastPartLength = CounterInstanceNameMaxLength - firstPartLength - insertString.Length; - result = string.Format(null, "{0}{1}{2}", - result.Substring(0, firstPartLength), - insertString, - result.Substring(result.Length - lastPartLength, lastPartLength)); + result = $"{result.Substring(0, firstPartLength)}{insertString}{result.Substring(result.Length - lastPartLength, lastPartLength)}"; Debug.Assert(result.Length == CounterInstanceNameMaxLength, - string.Format(null, "wrong calculation of the instance name: expected {0}, actual: {1}", CounterInstanceNameMaxLength, result.Length)); + $"wrong calculation of the instance name: expected {CounterInstanceNameMaxLength}, actual: {result.Length}"); } return result; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs index 989d4486ffb1a..1b08291cccd2a 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.cs @@ -19,6 +19,7 @@ public virtual void Dispose() { } public virtual System.IDisposable Subscribe(System.IObserver> observer, System.Func? isEnabled) { throw null; } public virtual System.IDisposable Subscribe(System.IObserver> observer, System.Predicate? isEnabled) { throw null; } public override string ToString() { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public override void Write(string name, object? value) { } } public abstract partial class DiagnosticSource @@ -26,6 +27,7 @@ public abstract partial class DiagnosticSource protected DiagnosticSource() { } public abstract bool IsEnabled(string name); public virtual bool IsEnabled(string name, object? arg1, object? arg2 = null) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public abstract void Write(string name, object? value); } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj index e34e24c6fbf3b..779380efcdefc 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj @@ -12,6 +12,7 @@ + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 73ce2d5eab153..9402388ca45b9 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -192,7 +192,9 @@ public abstract partial class DiagnosticSource { public virtual void OnActivityExport(System.Diagnostics.Activity activity, object? payload) { } public virtual void OnActivityImport(System.Diagnostics.Activity activity, object? payload) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public System.Diagnostics.Activity StartActivity(System.Diagnostics.Activity activity, object? args) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The type of object being written to DiagnosticSource cannot be discovered statically.")] public void StopActivity(System.Diagnostics.Activity activity, object? args) { } } public enum ActivitySamplingResult diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md b/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md index 789f57ad01129..54c436ae89164 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md @@ -1,5 +1,9 @@ # Activity User Guide +**This doc is being obsoleted by: https://docs.microsoft.com/dotnet/core/diagnostics/distributed-tracing +Future doc changes should be done in the official docs, not here. +There is still some information here that is not present in the official docs (yet) so I am preserving it as-is.** + This document describes Activity, a class that allows storing and accessing diagnostics context and consuming it with logging system. This document provides Activity architecture [overview](#overview) and [usage](#activity-usage). diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index b958c7cf8e0c6..0000000000000 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - ILLink - IL2070 - member - M:System.Diagnostics.DiagnosticSourceEventSource.FilterAndTransform.MakeImplicitTransforms(System.Type) - - - ILLink - IL2070 - member - M:System.Diagnostics.DiagnosticSourceEventSource.TransformSpec.PropertySpec.PropertyFetch.FetcherForProperty(System.Type,System.String) - - - \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index d5a7d0ac9ff5a..ab2970430a42c 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -1,4 +1,4 @@ - + true false @@ -31,6 +31,7 @@ + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.corefx.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.corefx.cs index 4f66927dfd5d7..85fc57b2a8788 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.corefx.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.corefx.cs @@ -3,7 +3,9 @@ namespace System.Diagnostics { +#pragma warning disable CA1052 // make class static partial class Activity +#pragma warning restore CA1052 { /// /// Returns high resolution (~1 usec) current UTC DateTime. diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.netfx.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.netfx.cs index de057c60da95b..556a4e3d6d017 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.netfx.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.DateTime.netfx.cs @@ -32,7 +32,7 @@ private static void Sync() timeSync = new TimeSync(); } - private class TimeSync + private sealed class TimeSync { public readonly DateTime SyncUtcNow = DateTime.UtcNow; public readonly long SyncStopwatchTicks = Stopwatch.GetTimestamp(); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index b93a715a10516..b2d2cd0b20264 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -1323,7 +1323,7 @@ public ActivityIdFormat IdFormat private set => _state = (_state & ~State.FormatFlags) | (State)((byte)value & (byte)State.FormatFlags); } - private partial class LinkedListNode + private sealed partial class LinkedListNode { public LinkedListNode(T value) => Value = value; public T Value; @@ -1331,7 +1331,7 @@ private partial class LinkedListNode } // We are not using the public LinkedList because we need to ensure thread safety operation on the list. - private class LinkedList : IEnumerable + private sealed class LinkedList : IEnumerable { private LinkedListNode _first; private LinkedListNode _last; @@ -1379,7 +1379,7 @@ public void AddFront(T value) IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - private class BaggageLinkedList : IEnumerable> + private sealed class BaggageLinkedList : IEnumerable> { private LinkedListNode>? _first; @@ -1461,11 +1461,13 @@ public void Remove(string key) IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - private class TagsLinkedList : IEnumerable> + private sealed class TagsLinkedList : IEnumerable> { private LinkedListNode>? _first; private LinkedListNode>? _last; + private StringBuilder? _stringBuilder; + public TagsLinkedList(KeyValuePair firstValue, bool set = false) => _last = _first = ((set && firstValue.Value == null) ? null : new LinkedListNode>(firstValue)); public TagsLinkedList(IEnumerator> e) @@ -1627,6 +1629,37 @@ public void Set(KeyValuePair value) current = current.Next; }; } + + public override string ToString() + { + lock (this) + { + if (_first == null) + { + return string.Empty; + } + + _stringBuilder ??= new StringBuilder(); + _stringBuilder.Append(_first.Value.Key); + _stringBuilder.Append(':'); + _stringBuilder.Append(_first.Value.Value); + + LinkedListNode>? current = _first.Next; + while (current != null) + { + _stringBuilder.Append(", "); + _stringBuilder.Append(current.Value.Key); + _stringBuilder.Append(':'); + _stringBuilder.Append(current.Value.Value); + + current = current.Next; + } + + string result = _stringBuilder.ToString(); + _stringBuilder.Clear(); + return result; + } + } } // Note: Some consumers use this Enumerator dynamically to avoid allocations. diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs index 900319439fb71..283028fc8d49d 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs @@ -367,7 +367,7 @@ internal void NotifyActivityStop(Activity activity) // and allow enumerating the collection items and execute some action on the enumerated item and can detect any change in the collection // during the enumeration which force restarting the enumeration again. // Caution: We can have the action executed on the same item more than once which is ok in our scenarios. - internal class SynchronizedList + internal sealed class SynchronizedList { private readonly List _list; private uint _version; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs index 99e7f138c6d5c..0960ab03a9a06 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticListener.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { @@ -255,6 +256,7 @@ public override bool IsEnabled(string name, object? arg1, object? arg2 = null) /// /// Override abstract method /// + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public override void Write(string name, object? value) { for (DiagnosticSubscription? curSubscription = _subscriptions; curSubscription != null; curSubscription = curSubscription.Next) @@ -266,11 +268,11 @@ public override void Write(string name, object? value) /// in DiagnosticSubscription so we just define a private type for it here just so things compile. /// #if NETSTANDARD1_1 - private class Activity {} + private sealed class Activity {} #endif // Note that Subscriptions are READ ONLY. This means you never update any fields (even on removal!) - private class DiagnosticSubscription : IDisposable + private sealed class DiagnosticSubscription : IDisposable { internal IObserver> Observer = null!; @@ -345,7 +347,7 @@ public void Dispose() /// a callback when a new listener gets created. When a new DiagnosticListener gets created it should call /// OnNewDiagnosticListener so that AllListenerObservable can forward it on to all the subscribers. /// - private class AllListenerObservable : IObservable + private sealed class AllListenerObservable : IObservable { public IDisposable Subscribe(IObserver observer) { @@ -409,7 +411,7 @@ private bool Remove(AllListenerSubscription subscription) /// One node in the linked list of subscriptions that AllListenerObservable keeps. It is /// IDisposable, and when that is called it removes itself from the list. /// - internal class AllListenerSubscription : IDisposable + internal sealed class AllListenerSubscription : IDisposable { internal AllListenerSubscription(AllListenerObservable owner, IObserver subscriber, AllListenerSubscription? next) { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs index 10ad9e62a2616..2db2e7ff48cf2 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { @@ -15,6 +16,8 @@ namespace System.Diagnostics /// public abstract partial class DiagnosticSource { + internal const string WriteRequiresUnreferencedCode = "The type of object being written to DiagnosticSource cannot be discovered statically."; + /// /// Write is a generic way of logging complex payloads. Each notification /// is given a name, which identifies it as well as a object (typically an anonymous type) @@ -31,6 +34,7 @@ public abstract partial class DiagnosticSource /// The name of the event being written. /// An object that represent the value being passed as a payload for the event. /// This is often an anonymous type which contains several sub-values. + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public abstract void Write(string name, object? value); /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs index 549c4a8274cb2..3a10702ca1400 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceActivity.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace System.Diagnostics { @@ -24,6 +25,7 @@ public abstract partial class DiagnosticSource /// An object that represent the value being passed as a payload for the event. /// Started Activity for convenient chaining /// + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public Activity StartActivity(Activity activity, object? args) { activity.Start(); @@ -41,6 +43,7 @@ public Activity StartActivity(Activity activity, object? args) /// Activity to be stopped /// An object that represent the value being passed as a payload for the event. /// + [RequiresUnreferencedCode(WriteRequiresUnreferencedCode)] public void StopActivity(Activity activity, object? args) { // Stop sets the end time if it was unset, but we want it set before we issue the write diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs index 5c90e7b827cfc..893bef690789d 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs @@ -160,11 +160,11 @@ namespace System.Diagnostics /// See the DiagnosticSourceEventSourceBridgeTest.cs for more explicit examples of using this bridge. /// [EventSource(Name = "Microsoft-Diagnostics-DiagnosticSource")] - internal class DiagnosticSourceEventSource : EventSource + internal sealed class DiagnosticSourceEventSource : EventSource { public static DiagnosticSourceEventSource Logger = new DiagnosticSourceEventSource(); - public class Keywords + public static class Keywords { /// /// Indicates diagnostics messages from DiagnosticSourceEventSource should be included. @@ -506,7 +506,7 @@ internal enum ActivityEvents /// This method also contains that static 'Create/Destroy FilterAndTransformList, which /// simply parse a series of transformation specifications. /// - internal class FilterAndTransform + internal sealed class FilterAndTransform { /// /// Parses filterAndPayloadSpecs which is a list of lines each of which has the from @@ -717,7 +717,9 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx, if (eventNameFilter != null) eventNameFilterPredicate = (string eventName) => eventNameFilter == eventName; - var subscription = newListener.Subscribe(new CallbackObserver>(delegate (KeyValuePair evnt) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "DiagnosticSource.Write is marked with RequiresUnreferencedCode.")] + void OnEventWritten(KeyValuePair evnt) { // The filter given to the DiagnosticSource may not work if users don't is 'IsEnabled' as expected. // Thus we look for any events that may have snuck through and filter them out before forwarding. @@ -727,7 +729,9 @@ public FilterAndTransform(string filterAndPayloadSpec, int startIdx, int endIdx, var outputArgs = this.Morph(evnt.Value); var eventName = evnt.Key; writeEvent(newListener.Name, eventName, outputArgs); - }), eventNameFilterPredicate); + } + + var subscription = newListener.Subscribe(new CallbackObserver>(OnEventWritten), eventNameFilterPredicate); _liveSubscriptions = new Subscriptions(subscription, _liveSubscriptions); } })); @@ -948,41 +952,55 @@ internal static void CreateActivityListener(DiagnosticSourceEventSource eventSou return false; }; - eventSource._activityListener.ActivityStarted = activity => + eventSource._activityListener.ActivityStarted = activity => OnActivityStarted(eventSource, activity); + + eventSource._activityListener.ActivityStopped = activity => OnActivityStopped(eventSource, activity); + + ActivitySource.AddActivityListener(eventSource._activityListener); + } + + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Activity))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityContext))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityEvent))] + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ActivityLink))] + [DynamicDependency(nameof(DateTime.Ticks), typeof(DateTime))] + [DynamicDependency(nameof(TimeSpan.Ticks), typeof(TimeSpan))] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")] + private static void OnActivityStarted(DiagnosticSourceEventSource eventSource, Activity activity) + { + FilterAndTransform? list = eventSource._activitySourceSpecs; + while (list != null) { - FilterAndTransform? list = eventSource._activitySourceSpecs; - while (list != null) + if ((list.Events & ActivityEvents.ActivityStart) != 0 && + (activity.Source.Name == list.SourceName || list.SourceName == "*") && + (list.ActivityName == null || list.ActivityName == activity.OperationName)) { - if ((list.Events & ActivityEvents.ActivityStart) != 0 && - (activity.Source.Name == list.SourceName || list.SourceName == "*") && - (list.ActivityName == null || list.ActivityName == activity.OperationName)) - { - eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity)); - return; - } - - list = list.Next; + eventSource.ActivityStart(activity.Source.Name, activity.OperationName, list.Morph(activity)); + return; } - }; - eventSource._activityListener.ActivityStopped = activity => + list = list.Next; + } + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Activity's properties are being preserved with the DynamicDependencies on OnActivityStarted.")] + private static void OnActivityStopped(DiagnosticSourceEventSource eventSource, Activity activity) + { + FilterAndTransform? list = eventSource._activitySourceSpecs; + while (list != null) { - FilterAndTransform? list = eventSource._activitySourceSpecs; - while (list != null) + if ((list.Events & ActivityEvents.ActivityStop) != 0 && + (activity.Source.Name == list.SourceName || list.SourceName == "*") && + (list.ActivityName == null || list.ActivityName == activity.OperationName)) { - if ((list.Events & ActivityEvents.ActivityStop) != 0 && - (activity.Source.Name == list.SourceName || list.SourceName == "*") && - (list.ActivityName == null || list.ActivityName == activity.OperationName)) - { - eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity)); - return; - } - - list = list.Next; + eventSource.ActivityStop(activity.Source.Name, activity.OperationName, list.Morph(activity)); + return; } - }; - ActivitySource.AddActivityListener(eventSource._activityListener); + list = list.Next; + } } // Move all wildcard nodes at the end of the list. @@ -1067,6 +1085,7 @@ private void Dispose() } } + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public List> Morph(object? args) { // Transform the args into a bag of key-value strings. @@ -1105,7 +1124,11 @@ private void Dispose() Interlocked.CompareExchange(ref _implicitTransformsTable, new ConcurrentDictionary(1, 8), null); } - implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, type => MakeImplicitTransforms(type)); + implicitTransforms = _implicitTransformsTable.GetOrAdd(argType, type => MakeImplicitTransformsWrapper(type)); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The Morph method has RequiresUnreferencedCode, but the trimmer can't see through lamdba calls.")] + static TransformSpec? MakeImplicitTransformsWrapper(Type transformType) => MakeImplicitTransforms(transformType); } // implicitTransformas now fetched from cache or constructed, use it to Fetch all the implicit fields. @@ -1145,6 +1168,7 @@ private void Dispose() // Given a type generate all the implicit transforms for type (that is for every field // generate the spec that fetches it). + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] private static TransformSpec? MakeImplicitTransforms(Type type) { TransformSpec? newSerializableArgs = null; @@ -1185,7 +1209,7 @@ private void Dispose() // This olds one the implicit transform for one type of object. // We remember this type-transform pair in the _firstImplicitTransformsEntry cache. - internal class ImplicitTransformEntry + internal sealed class ImplicitTransformEntry { public Type? Type; public TransformSpec? Transforms; @@ -1196,7 +1220,7 @@ internal class ImplicitTransformEntry /// the DiagnosticSource payload. An example string is OUTSTR=EVENT_VALUE.PROP1.PROP2.PROP3 /// It has a Next field so they can be chained together in a linked list. /// - internal class TransformSpec + internal sealed class TransformSpec { /// /// parse the strings 'spec' from startIdx to endIdx (points just beyond the last considered char) @@ -1239,6 +1263,7 @@ public TransformSpec(string transformSpec, int startIdx, int endIdx, TransformSp /// if the spec is OUTSTR=EVENT_VALUE.PROP1.PROP2.PROP3 and the ultimate value of PROP3 is /// 10 then the return key value pair is KeyValuePair("OUTSTR","10") /// + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public KeyValuePair Morph(object? obj) { for (PropertySpec? cur = _fetches; cur != null; cur = cur.Next) @@ -1261,7 +1286,7 @@ public TransformSpec(string transformSpec, int startIdx, int endIdx, TransformSp /// and efficiently. Thus it represents a '.PROP' in a TransformSpec /// (and a transformSpec has a list of these). /// - internal class PropertySpec + internal sealed class PropertySpec { private const string CurrentActivityPropertyName = "*Activity"; private const string EnumeratePropertyName = "*Enumerate"; @@ -1289,6 +1314,7 @@ public PropertySpec(string propertyName, PropertySpec? next) /// Given an object fetch the property that this PropertySpec represents. /// obj may be null when IsStatic is true, otherwise it must be non-null. /// + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public object? Fetch(object? obj) { PropertyFetch? fetch = _fetchForExpectedType; @@ -1296,10 +1322,12 @@ public PropertySpec(string propertyName, PropertySpec? next) Type? objType = obj?.GetType(); if (fetch == null || fetch.Type != objType) { - _fetchForExpectedType = fetch = PropertyFetch.FetcherForProperty( - objType, _propertyName); + _fetchForExpectedType = fetch = PropertyFetch.FetcherForProperty(objType, _propertyName); } - return fetch!.Fetch(obj); + object? ret = null; + // Avoid the exception which can be thrown during accessing the object properties. + try { ret = fetch!.Fetch(obj); } catch (Exception e) { Logger.Message($"Property {objType}.{_propertyName} threw the exception {e}"); } + return ret; } /// @@ -1329,9 +1357,7 @@ public PropertyFetch(Type? type) /// /// Create a property fetcher for a propertyName /// - [DynamicDependency("#ctor(System.Type)", typeof(EnumeratePropertyFetch<>))] - [DynamicDependency("#ctor(System.Type,System.Reflection.PropertyInfo)", typeof(RefTypedFetchProperty<,>))] - [DynamicDependency("#ctor(System.Type,System.Reflection.PropertyInfo)", typeof(ValueTypedFetchProperty<,>))] + [RequiresUnreferencedCode(DiagnosticSource.WriteRequiresUnreferencedCode)] public static PropertyFetch FetcherForProperty(Type? type, string propertyName) { if (propertyName == null) @@ -1383,7 +1409,7 @@ public static PropertyFetch FetcherForProperty(Type? type, string propertyName) PropertyInfo? propertyInfo = typeInfo.GetDeclaredProperty(propertyName); if (propertyInfo == null) { - Logger.Message($"Property {propertyName} not found on {type}"); + Logger.Message($"Property {propertyName} not found on {type}. Ensure the name is spelled correctly. If you published the application with PublishTrimmed=true, ensure the property was not trimmed away."); return new PropertyFetch(type); } // Delegate creation below is incompatible with static properties. @@ -1490,7 +1516,7 @@ public EnumeratePropertyFetch(Type type) : base(type) { } /// operation on the IObserver happens. /// /// - internal class CallbackObserver : IObserver + internal sealed class CallbackObserver : IObserver { public CallbackObserver(Action callback) { _callback = callback; } @@ -1506,7 +1532,7 @@ public void OnError(Exception error) { } // A linked list of IObservable subscriptions (which are IDisposable). // We use this to keep track of the DiagnosticSource subscriptions. // We use this linked list for thread atomicity - internal class Subscriptions + internal sealed class Subscriptions { public Subscriptions(IDisposable subscription, Subscriptions? next) { diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs index dd151339b5cf4..07cbd932f3ad7 100755 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs @@ -6,7 +6,7 @@ namespace System.Diagnostics /// /// RandomNumberGenerator implementation is the 64-bit random number generator based on the Xoshiro256StarStar algorithm (known as shift-register generators). /// - internal class RandomNumberGenerator + internal sealed class RandomNumberGenerator { [ThreadStatic] private static RandomNumberGenerator? t_random; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs index accbf48fc30a0..ec5e525061e12 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs @@ -889,7 +889,7 @@ public void IdFormat_W3CForcedOverridesParentActivityIdFormat() Activity parent = new Activity("parent").Start(); Activity activity = new Activity("child").Start(); - Assert.Equal(parent.SpanId.ToHexString(), activity.ParentSpanId.ToHexString()); ; + Assert.Equal(parent.SpanId.ToHexString(), activity.ParentSpanId.ToHexString()); }).Dispose(); } @@ -1547,6 +1547,7 @@ public void TestIsAllDataRequested() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void TestTagObjects() { Activity activity = new Activity("TagObjects"); @@ -1607,6 +1608,7 @@ public void TestTagObjects() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void TestGetTagItem() { Activity a = new Activity("GetTagItem"); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs index 6e4301e6ed807..900139449bc50 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs @@ -698,6 +698,38 @@ public void TestWildCardEventName() }).Dispose(); } + public class PropertyThrow + { + public string property1 => "P1"; + public string property2 => throw new Exception("Always throw!"); + public string property3 => "P3"; + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public void TestWithPropertyThrowing() + { + RemoteExecutor.Invoke(() => + { + using (var eventSourceListener = new TestDiagnosticSourceEventListener()) + using (var diagnosticSourceListener = new DiagnosticListener("TestThrows")) + { + eventSourceListener.Enable("TestThrows/TestEvent1"); + + if (diagnosticSourceListener.IsEnabled("TestEvent1")) + diagnosticSourceListener.Write("TestEvent1", new PropertyThrow()); + + Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted. + Assert.Equal("TestThrows", eventSourceListener.LastEvent.SourceName); + Assert.Equal("TestEvent1", eventSourceListener.LastEvent.EventName); + Assert.Equal(3, eventSourceListener.LastEvent.Arguments.Count); + Assert.Equal("P1", eventSourceListener.LastEvent.Arguments["property1"]); + Assert.Equal("P3", eventSourceListener.LastEvent.Arguments["property3"]); + Assert.Equal("", eventSourceListener.LastEvent.Arguments["property2"]); + eventSourceListener.ResetEventCountAndLastEvent(); + } + }).Dispose(); + } + /// /// Test what happens when there are nulls passed in the event payloads /// Basically strings get turned into empty strings and other nulls are typically diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs index 8dd1715334ec4..729966e3bf771 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogEntryCollection.cs @@ -67,7 +67,7 @@ void ICollection.CopyTo(Array array, int index) Array.Copy(entries, 0, array, index, entries.Length); } - private class EntriesEnumerator : IEnumerator + private sealed class EntriesEnumerator : IEnumerator { private readonly EventLogEntryCollection entries; private int num = -1; diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs index 8d10a75d9672f..8ca40469ba095 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLogInternal.cs @@ -15,7 +15,7 @@ namespace System.Diagnostics { - internal class EventLogInternal : IDisposable, ISupportInitialize + internal sealed class EventLogInternal : IDisposable, ISupportInitialize { private EventLogEntryCollection entriesCollection; internal string logName; @@ -1408,7 +1408,7 @@ private void InternalWriteEvent(uint eventID, ushort category, EventLogEntryType } } - private class LogListeningInfo + private sealed class LogListeningInfo { public EventLogInternal handleOwner; public RegisteredWaitHandle registeredWaitHandle; diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/NativeWrapper.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/NativeWrapper.cs index 2c21052f3d819..44020c5e8a0d7 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/NativeWrapper.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/NativeWrapper.cs @@ -17,7 +17,7 @@ namespace System.Diagnostics.Eventing.Reader /// specific to function. Also, all methods of this class expose /// the Link Demand for Unmanaged Permission to callers. /// - internal class NativeWrapper + internal static class NativeWrapper { public class SystemProperties { diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/ProviderMetadataCachedInformation.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/ProviderMetadataCachedInformation.cs index 35c6d63335d33..aa62843117ae0 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/ProviderMetadataCachedInformation.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/Reader/ProviderMetadataCachedInformation.cs @@ -13,14 +13,14 @@ namespace System.Diagnostics.Eventing.Reader /// for this is so the cache can easily Dispose the metadata object without worrying /// about who is using it. /// - internal class ProviderMetadataCachedInformation + internal sealed class ProviderMetadataCachedInformation { private readonly Dictionary _cache; private readonly int _maximumCacheSize; private readonly EventLogSession _session; private readonly string _logfile; - private class ProviderMetadataId + private sealed class ProviderMetadataId { public ProviderMetadataId(string providerName, CultureInfo cultureInfo) { @@ -47,7 +47,7 @@ public override int GetHashCode() public CultureInfo TheCultureInfo { get; } } - private class CacheItem + private sealed class CacheItem { public CacheItem(ProviderMetadata pm) { diff --git a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs index 71ced8be0eb4c..d92bd9471ba6a 100644 --- a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs +++ b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs @@ -126,20 +126,18 @@ private static uint GetVarEntry(IntPtr memPtr) // private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage) { - string template = "\\\\StringFileInfo\\\\{0}\\\\{1}"; - - _companyName = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "CompanyName")); - _fileDescription = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "FileDescription")); - _fileVersion = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "FileVersion")); - _internalName = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "InternalName")); - _legalCopyright = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "LegalCopyright")); - _originalFilename = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "OriginalFilename")); - _productName = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "ProductName")); - _productVersion = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "ProductVersion")); - _comments = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "Comments")); - _legalTrademarks = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "LegalTrademarks")); - _privateBuild = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "PrivateBuild")); - _specialBuild = GetFileVersionString(memIntPtr, string.Format(CultureInfo.InvariantCulture, template, codepage, "SpecialBuild")); + _companyName = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\CompanyName"); + _fileDescription = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\FileDescription"); + _fileVersion = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\FileVersion"); + _internalName = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\InternalName"); + _legalCopyright = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\LegalCopyright"); + _originalFilename = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\OriginalFilename"); + _productName = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\ProductName"); + _productVersion = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\ProductVersion"); + _comments = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\Comments"); + _legalTrademarks = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\LegalTrademarks"); + _privateBuild = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\PrivateBuild"); + _specialBuild = GetFileVersionString(memIntPtr, $"\\\\StringFileInfo\\\\{codepage}\\\\SpecialBuild"); _language = GetFileVersionLanguage(memIntPtr); diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerfCounterSection.cs b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerfCounterSection.cs index d3a7072f77a98..7169e201f65e3 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerfCounterSection.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerfCounterSection.cs @@ -5,7 +5,7 @@ namespace System.Diagnostics { - internal class PerfCounterSection : ConfigurationElement + internal sealed class PerfCounterSection : ConfigurationElement { private static readonly ConfigurationProperty s_propFileMappingSize = new ConfigurationProperty("filemappingsize", typeof(int), 524288, ConfigurationPropertyOptions.None); private static readonly ConfigurationPropertyCollection s_properties = new ConfigurationPropertyCollection { s_propFileMappingSize }; diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs index 17fdccfa37130..938bb8818298e 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/PerformanceCounterLib.cs @@ -20,7 +20,7 @@ namespace System.Diagnostics { - internal class PerformanceCounterLib + internal sealed class PerformanceCounterLib { internal const string PerfShimName = "netfxperf.dll"; private const string PerfShimFullNameSuffix = @"\netfxperf.dll"; @@ -1265,7 +1265,7 @@ internal static void UnregisterCategory(string categoryName) } } - internal class PerformanceMonitor + internal sealed class PerformanceMonitor { private PerformanceDataRegistryKey perfDataKey; private readonly string machineName; @@ -1378,7 +1378,7 @@ internal void ReleaseData(byte[] data) } - internal class CategoryEntry + internal sealed class CategoryEntry { internal int NameIndex; internal int HelpIndex; @@ -1676,7 +1676,7 @@ private void CheckDisposed() } } - internal class CounterDefinitionSample + internal sealed class CounterDefinitionSample { internal readonly int _nameIndex; internal readonly int _counterType; diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs index df454cd9b5ad7..7ef3659bbd3e4 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SharedPerformanceCounter.cs @@ -1646,7 +1646,7 @@ private int ResolveAddress(long address, int sizeToRead) return offset; } - private class FileMapping + private sealed class FileMapping { internal int _fileMappingSize; private SafeMemoryMappedViewHandle _fileViewAddress; @@ -1846,7 +1846,7 @@ private struct ProcessLifetimeEntry public long StartupTime; } - private class CategoryData + private sealed class CategoryData { public FileMapping FileMapping; public bool EnableReuse; @@ -1857,7 +1857,7 @@ private class CategoryData } } - internal class ProcessData + internal sealed class ProcessData { public ProcessData(int pid, long startTime) { diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SystemDiagnosticsSection.cs b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SystemDiagnosticsSection.cs index d13ec58638aad..aa40d83aed03a 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SystemDiagnosticsSection.cs +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/System/Diagnostics/SystemDiagnosticsSection.cs @@ -5,7 +5,7 @@ namespace System.Diagnostics { - internal class SystemDiagnosticsSection : ConfigurationSection + internal sealed class SystemDiagnosticsSection : ConfigurationSection { private static readonly ConfigurationProperty s_propPerfCounters = new ConfigurationProperty("performanceCounters", typeof(PerfCounterSection), new PerfCounterSection(), ConfigurationPropertyOptions.None); private static readonly ConfigurationPropertyCollection s_properties = new ConfigurationPropertyCollection { s_propPerfCounters }; diff --git a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs index 599378b6ea04d..4b536fd86d54e 100644 --- a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs +++ b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs @@ -108,15 +108,29 @@ public static void EnterDebugMode() { } public static System.Diagnostics.Process[] GetProcesses(string machineName) { throw null; } public static System.Diagnostics.Process[] GetProcessesByName(string? processName) { throw null; } public static System.Diagnostics.Process[] GetProcessesByName(string? processName, string machineName) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public void Kill() { } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public void Kill(bool entireProcessTree) { } public static void LeaveDebugMode() { } protected void OnExited() { } public void Refresh() { } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public bool Start() { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static System.Diagnostics.Process? Start(System.Diagnostics.ProcessStartInfo startInfo) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static System.Diagnostics.Process Start(string fileName) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static System.Diagnostics.Process Start(string fileName, string arguments) { throw null; } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static System.Diagnostics.Process Start(string fileName, System.Collections.Generic.IEnumerable arguments) { throw null; } [System.CLSCompliantAttribute(false)] [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index f37055179859c..f0c7d8eb136bf 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -337,6 +337,7 @@ + diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs index 08ff0741e0e1a..15b5220a2621c 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/PerformanceCounterLib.cs @@ -189,7 +189,7 @@ private Dictionary GetStringTable(bool isHelp) #endif } - internal class PerformanceMonitor + internal sealed class PerformanceMonitor { #if FEATURE_REGISTRY private RegistryKey _perfDataKey; diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs index be09b7bb146ef..0dff87802214e 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.NonUap.cs @@ -3,11 +3,14 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Runtime.Versioning; namespace System.Diagnostics { public partial class Process : IDisposable { + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public void Kill(bool entireProcessTree) { if (!entireProcessTree) @@ -30,7 +33,7 @@ public void Kill(bool entireProcessTree) private bool IsSelfOrDescendantOf(Process processOfInterest) { - if (SafePredicateTest(() => Equals(processOfInterest))) + if (Equals(processOfInterest)) return true; Process[] allProcesses = GetProcesses(); @@ -44,7 +47,7 @@ private bool IsSelfOrDescendantOf(Process processOfInterest) { foreach (Process candidate in current.GetChildProcesses(allProcesses)) { - if (SafePredicateTest(() => processOfInterest.Equals(candidate))) + if (processOfInterest.Equals(candidate)) return true; descendantProcesses.Enqueue(candidate); @@ -79,7 +82,7 @@ private IReadOnlyList GetChildProcesses(Process[]? processes = null) try { - if (SafePredicateTest(() => IsParentOf(possibleChildProcess))) + if (IsParentOf(possibleChildProcess)) { childProcesses.Add(possibleChildProcess); dispose = false; @@ -95,19 +98,10 @@ private IReadOnlyList GetChildProcesses(Process[]? processes = null) return childProcesses; } - private bool SafePredicateTest(Func predicate) - { - try - { - return predicate(); - } - catch (Exception e) when (e is InvalidOperationException || e is Win32Exception) - { - // InvalidOperationException signifies conditions such as the process already being dead. - // Win32Exception signifies issues such as insufficient permissions to get details on the process. - // In either case, the predicate couldn't be applied so return the fallback result. - return false; - } - } + private static bool IsProcessInvalidException(Exception e) => + // InvalidOperationException signifies conditions such as the process already being dead. + // Win32Exception signifies issues such as insufficient permissions to get details on the process. + // In either case, the predicate couldn't be applied so return the fallback result. + e is InvalidOperationException || e is Win32Exception; } } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs index 55c5a615a605f..7ae9eeec8d614 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; +using System.IO.Pipes; using System.Security; using System.Text; using System.Threading; @@ -53,8 +54,15 @@ public static Process Start(string fileName, string arguments, string userName, } /// Terminates the associated process immediately. + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public void Kill() { + if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS()) + { + throw new PlatformNotSupportedException(); + } + EnsureState(State.HaveId); // Check if we know the process has exited. This avoids us targetting another @@ -300,11 +308,29 @@ private ProcessPriorityClass PriorityClassCore } /// Checks whether the argument is a direct child of this process. - private bool IsParentOf(Process possibleChildProcess) => - Id == possibleChildProcess.ParentProcessId; + private bool IsParentOf(Process possibleChildProcess) + { + try + { + return Id == possibleChildProcess.ParentProcessId; + } + catch (Exception e) when (IsProcessInvalidException(e)) + { + return false; + } + } - private bool Equals(Process process) => - Id == process.Id; + private bool Equals(Process process) + { + try + { + return Id == process.Id; + } + catch (Exception e) when (IsProcessInvalidException(e)) + { + return false; + } + } partial void ThrowIfExited(bool refresh) { @@ -344,6 +370,11 @@ private SafeProcessHandle GetProcessHandle() /// The start info with which to start the process. private bool StartCore(ProcessStartInfo startInfo) { + if (OperatingSystem.IsIOS() || OperatingSystem.IsTvOS()) + { + throw new PlatformNotSupportedException(); + } + EnsureInitialized(); string? filename; @@ -439,20 +470,20 @@ private bool StartCore(ProcessStartInfo startInfo) if (startInfo.RedirectStandardInput) { Debug.Assert(stdinFd >= 0); - _standardInput = new StreamWriter(OpenStream(stdinFd, FileAccess.Write), + _standardInput = new StreamWriter(OpenStream(stdinFd, PipeDirection.Out), startInfo.StandardInputEncoding ?? Encoding.Default, StreamBufferSize) { AutoFlush = true }; } if (startInfo.RedirectStandardOutput) { Debug.Assert(stdoutFd >= 0); - _standardOutput = new StreamReader(OpenStream(stdoutFd, FileAccess.Read), + _standardOutput = new StreamReader(OpenStream(stdoutFd, PipeDirection.In), startInfo.StandardOutputEncoding ?? Encoding.Default, true, StreamBufferSize); } if (startInfo.RedirectStandardError) { Debug.Assert(stderrFd >= 0); - _standardError = new StreamReader(OpenStream(stderrFd, FileAccess.Read), + _standardError = new StreamReader(OpenStream(stderrFd, PipeDirection.In), startInfo.StandardErrorEncoding ?? Encoding.Default, true, StreamBufferSize); } @@ -747,14 +778,12 @@ internal static TimeSpan TicksToTimeSpan(double ticks) /// Opens a stream around the specified file descriptor and with the specified access. /// The file descriptor. - /// The access mode. + /// The pipe direction. /// The opened stream. - private static FileStream OpenStream(int fd, FileAccess access) + private static Stream OpenStream(int fd, PipeDirection direction) { Debug.Assert(fd >= 0); - return new FileStream( - new SafeFileHandle((IntPtr)fd, ownsHandle: true), - access, StreamBufferSize, isAsync: false); + return new AnonymousPipeClientStream(direction, new SafePipeHandle((IntPtr)fd, ownsHandle: true)); } /// Parses a command-line argument string into a list of arguments. diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs index f5e4aa38e51f1..18aff1fb17238 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Win32.cs @@ -330,9 +330,17 @@ private bool WaitForInputIdleCore(int milliseconds) /// /// A child process is a process which has this process's id as its parent process id and which started after this process did. /// - private bool IsParentOf(Process possibleChild) => - StartTime < possibleChild.StartTime - && Id == possibleChild.ParentProcessId; + private bool IsParentOf(Process possibleChild) + { + try + { + return StartTime < possibleChild.StartTime && Id == possibleChild.ParentProcessId; + } + catch (Exception e) when (IsProcessInvalidException(e)) + { + return false; + } + } /// /// Get the process's parent process id. @@ -353,9 +361,17 @@ private unsafe int ParentProcessId } } - private bool Equals(Process process) => - Id == process.Id - && StartTime == process.StartTime; + private bool Equals(Process process) + { + try + { + return Id == process.Id && StartTime == process.StartTime; + } + catch (Exception e) when (IsProcessInvalidException(e)) + { + return false; + } + } private List? KillTree() { @@ -389,7 +405,7 @@ private bool Equals(Process process) => (exceptions ??= new List()).Add(e); } - List<(Process Process, SafeProcessHandle Handle)> children = GetProcessHandlePairs(p => SafePredicateTest(() => IsParentOf(p))); + List<(Process Process, SafeProcessHandle Handle)> children = GetProcessHandlePairs((thisProcess, otherProcess) => thisProcess.IsParentOf(otherProcess)); try { foreach ((Process Process, SafeProcessHandle Handle) child in children) @@ -413,7 +429,7 @@ private bool Equals(Process process) => return exceptions; } - private List<(Process Process, SafeProcessHandle Handle)> GetProcessHandlePairs(Func predicate) + private List<(Process Process, SafeProcessHandle Handle)> GetProcessHandlePairs(Func predicate) { var results = new List<(Process Process, SafeProcessHandle Handle)>(); @@ -422,7 +438,7 @@ private bool Equals(Process process) => SafeProcessHandle h = SafeGetHandle(p); if (!h.IsInvalid) { - if (predicate(p)) + if (predicate(this, p)) { results.Add((p, h)); } diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index 0dfc4ae2357cb..475f31853f4d9 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -89,6 +89,8 @@ public static void LeaveDebugMode() } /// Terminates the associated process immediately. + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public void Kill() { using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_TERMINATE | Interop.Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION, throwIfExited: false)) diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index 6c9181f8655d6..2861e81aea384 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -2,16 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO; using System.Runtime.Serialization; +using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Runtime.Versioning; -using System.Collections.Generic; namespace System.Diagnostics { @@ -1197,6 +1197,8 @@ private void SetProcessId(int processId) /// component. /// /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public bool Start() { Close(); @@ -1238,6 +1240,8 @@ public bool Start() /// component. /// /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public static Process Start(string fileName) { // the underlying Start method can only return null on Windows platforms, @@ -1254,6 +1258,8 @@ public static Process Start(string fileName) /// component. /// /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public static Process Start(string fileName, string arguments) { // the underlying Start method can only return null on Windows platforms, @@ -1265,6 +1271,8 @@ public static Process Start(string fileName, string arguments) /// /// Starts a process resource by specifying the name of an application and a set of command line arguments /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public static Process Start(string fileName, IEnumerable arguments) { if (fileName == null) @@ -1289,6 +1297,8 @@ public static Process Start(string fileName, IEnumerable arguments) /// component. /// /// + [UnsupportedOSPlatform("ios")] + [UnsupportedOSPlatform("tvos")] public static Process? Start(ProcessStartInfo startInfo) { Process process = new Process(); @@ -1345,7 +1355,7 @@ public override string ToString() string processName = ProcessName; if (processName.Length != 0) { - return string.Format(CultureInfo.CurrentCulture, "{0} ({1})", base.ToString(), processName); + return $"{base.ToString()} ({processName})"; } } return base.ToString(); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs index 18243a75c5670..53c9c743de856 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs @@ -252,7 +252,7 @@ internal static class NtProcessInfoHelper private const int DefaultCachedBufferSize = 128 * 1024; #endif - internal static ProcessInfo[] GetProcessInfos(Predicate? processIdFilter = null) + internal static ProcessInfo[] GetProcessInfos(int? processIdFilter = null) { ProcessInfo[] processInfos; @@ -342,7 +342,7 @@ private static int GetNewBufferSize(int existingBufferSize, int requiredSize) return newSize; } - private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan data, Predicate? processIdFilter) + private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan data, int? processIdFilter) { // Use a dictionary to avoid duplicate entries if any // 60 is a reasonable number for processes on a normal machine. @@ -356,7 +356,7 @@ private static unsafe ProcessInfo[] GetProcessInfos(ReadOnlySpan data, Pre // Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD. int processInfoProcessId = pi.UniqueProcessId.ToInt32(); - if (processIdFilter == null || processIdFilter(processInfoProcessId)) + if (processIdFilter == null || processIdFilter.GetValueOrDefault() == processInfoProcessId) { // get information for a process ProcessInfo processInfo = new ProcessInfo((int)pi.NumberOfThreads) diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs index 98075165c63ae..103443d7f2325 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs @@ -75,7 +75,7 @@ public static ProcessInfo[] GetProcessInfos(string machineName) else { // local case: do not use performance counter and also attempt to get the matching (by pid) process only - ProcessInfo[] processInfos = NtProcessInfoHelper.GetProcessInfos(pid => pid == processId); + ProcessInfo[] processInfos = NtProcessInfoHelper.GetProcessInfos(processId); if (processInfos.Length == 1) { return processInfos[0]; diff --git a/src/libraries/System.Diagnostics.Process/tests/Interop.Unix.cs b/src/libraries/System.Diagnostics.Process/tests/Interop.Unix.cs index 5cc19a03f9b82..ddab95fab7548 100644 --- a/src/libraries/System.Diagnostics.Process/tests/Interop.Unix.cs +++ b/src/libraries/System.Diagnostics.Process/tests/Interop.Unix.cs @@ -8,7 +8,7 @@ namespace System.Diagnostics.Tests { - internal partial class Interop + internal static partial class Interop { [DllImport("libc")] internal static extern int getsid(int pid); diff --git a/src/libraries/System.Diagnostics.Process/tests/Interop.cs b/src/libraries/System.Diagnostics.Process/tests/Interop.cs index 98946c288c9b9..0bcbb62f9bb2b 100644 --- a/src/libraries/System.Diagnostics.Process/tests/Interop.cs +++ b/src/libraries/System.Diagnostics.Process/tests/Interop.cs @@ -8,7 +8,7 @@ namespace System.Diagnostics.Tests { - internal partial class Interop + internal static partial class Interop { [StructLayout(LayoutKind.Sequential, Size = 40)] public struct PROCESS_MEMORY_COUNTERS diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessCollectionTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessCollectionTests.cs index 3ac20166921f3..90acc19d048d7 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessCollectionTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessCollectionTests.cs @@ -8,6 +8,7 @@ namespace System.Diagnostics.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class ProcessCollectionTests : ProcessTestBase { [Fact] diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs index 2631cb8b1a3dc..e127633db8c36 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs @@ -72,6 +72,7 @@ public ModuleCollectionSubClass() : base() { } } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ModulesAreDisposedWhenProcessIsDisposed() { Process process = CreateDefaultProcess(); diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs index fcaccc3ffcccb..d288f23649626 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs @@ -22,6 +22,7 @@ namespace System.Diagnostics.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class ProcessStartInfoTests : ProcessTestBase { private const string ItemSeparator = "CAFF9451396B4EEF8A5155A15BDC2080"; // random string that shouldn't be in any env vars; used instead of newline to separate env var strings diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs index 577a9f1a06137..1f1027a58573f 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStreamReadTests.cs @@ -14,6 +14,7 @@ namespace System.Diagnostics.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class ProcessStreamReadTests : ProcessTestBase { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] @@ -344,7 +345,6 @@ async private Task WaitPipeSignal(PipeStream pipe, int millisecond) } } - [ActiveIssue("https://github.com/dotnet/runtime/issues/44329")] [PlatformSpecific(~TestPlatforms.Windows)] // currently on Windows these operations async-over-sync on Windows [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public async Task ReadAsync_OutputStreams_Cancel_RespondsQuickly() diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index d6333e67747ad..2e206aa77d254 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -21,6 +21,7 @@ namespace System.Diagnostics.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public partial class ProcessTests : ProcessTestBase { private class FinalizingProcess : Process diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs index 2266582ac5190..46305d91ac865 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessThreadTests.cs @@ -10,6 +10,7 @@ namespace System.Diagnostics.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public partial class ProcessThreadTests : ProcessTestBase { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs index 5cbd89d5a7705..8bd7985a218fd 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs @@ -11,6 +11,7 @@ namespace System.Diagnostics.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class ProcessWaitingTests : ProcessTestBase { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] diff --git a/src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.Diagnostics.StackTrace/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/StackTraceSymbols.cs b/src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/StackTraceSymbols.cs index b15da6d123b96..95a445f8500fa 100644 --- a/src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/StackTraceSymbols.cs +++ b/src/libraries/System.Diagnostics.StackTrace/src/System/Diagnostics/StackTraceSymbols.cs @@ -10,7 +10,7 @@ namespace System.Diagnostics { - internal class StackTraceSymbols : IDisposable + internal sealed class StackTraceSymbols : IDisposable { private readonly ConditionalWeakTable _metadataCache; diff --git a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs index 469b6464149a5..85017059a5683 100644 --- a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs +++ b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs @@ -11,7 +11,7 @@ namespace System.Diagnostics { internal static class TraceInternal { - private class TraceProvider : DebugProvider + private sealed class TraceProvider : DebugProvider { #pragma warning disable CS8770 // Method lacks `[DoesNotReturn]` annotation to match overridden member. public override void Fail(string? message, string? detailMessage) { TraceInternal.Fail(message, detailMessage); } diff --git a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs index 945dc200c3afd..3935b1609fa1a 100644 --- a/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs +++ b/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceListener.cs @@ -390,7 +390,7 @@ public virtual void TraceEvent(TraceEventCache? eventCache, string source, Trace private void WriteHeader(string source, TraceEventType eventType, int id) { - Write(string.Format(CultureInfo.InvariantCulture, "{0} {1}: {2} : ", source, eventType.ToString(), id.ToString(CultureInfo.InvariantCulture))); + Write($"{source} {eventType.ToString()}: {id.ToString(CultureInfo.InvariantCulture)} : "); } private void WriteFooter(TraceEventCache? eventCache) diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs index 8e2f891799f0e..9c6a217abbc1e 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADAMStoreCtx.cs @@ -15,7 +15,7 @@ namespace System.DirectoryServices.AccountManagement { - internal partial class ADAMStoreCtx : ADStoreCtx + internal sealed partial class ADAMStoreCtx : ADStoreCtx { private const int mappingIndex = 1; private List _cachedBindableObjectList; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs index 5e89d45e7e908..a0cbd527c958d 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNConstraintLinkedAttrSet.cs @@ -9,7 +9,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class ADDNConstraintLinkedAttrSet : ADDNLinkedAttrSet + internal sealed class ADDNConstraintLinkedAttrSet : ADDNLinkedAttrSet { /// /// diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs index 7d4739ae1c36d..3174e96bcd672 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADDNLinkedAttrSet.cs @@ -1387,7 +1387,7 @@ internal enum ExpansionMode ASQ = 1, } - internal class ADDNLinkedAttrSetBookmark : ResultSetBookmark + internal sealed class ADDNLinkedAttrSetBookmark : ResultSetBookmark { public Dictionary usersVisited; public List groupsToVisit; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs index b100cff9817ac..94f1be8347062 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADEntriesSet.cs @@ -9,7 +9,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class ADEntriesSet : ResultSet + internal sealed class ADEntriesSet : ResultSet { private readonly SearchResultCollection _searchResults; private readonly ADStoreCtx _storeCtx; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx.cs index b2ac217818c34..501bdd07c2654 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx.cs @@ -1824,12 +1824,7 @@ internal override bool IsMemberOfInStore(GroupPrincipal g, Principal p) try { - string path = string.Format( - CultureInfo.InvariantCulture, - "LDAP://{0}/{1}", - string.IsNullOrEmpty(this.UserSuppliedServerName) ? this.DnsHostName : this.UserSuppliedServerName, - this.ContextBasePartitionDN - ); + string path = $"LDAP://{(string.IsNullOrEmpty(this.UserSuppliedServerName) ? this.DnsHostName : this.UserSuppliedServerName)}/{this.ContextBasePartitionDN}"; defaultNCDirEntry = SDSUtils.BuildDirectoryEntry(path, this.credentials, this.authTypes); diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_LoadStore.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_LoadStore.cs index ce4f990717e29..685aae5b49582 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_LoadStore.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_LoadStore.cs @@ -805,7 +805,7 @@ internal override Type NativeType(Principal p) protected static Dictionary> TypeToLdapPropListMap; - private class PropertyMappingTableEntry + private sealed class PropertyMappingTableEntry { internal string propertyName; // PAPI name internal string suggestedADPropertyName; // LDAP attribute name diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs index 136d28275e7cb..850ca0b9cf9cc 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreCtx_Query.cs @@ -310,7 +310,7 @@ internal override ResultSet Query(PrincipalSearcher ps, int sizeLimit) private static Hashtable s_filterPropertiesTable; - private class FilterPropertyTableEntry + private sealed class FilterPropertyTableEntry { internal string suggestedADPropertyName; internal FilterConverterDelegate converter; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreKey.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreKey.cs index 5576526f50093..845afc0533f96 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreKey.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADStoreKey.cs @@ -7,7 +7,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class ADStoreKey : StoreKey + internal sealed class ADStoreKey : StoreKey { // For regular ADStoreKeys private System.Guid _objectGuid; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs index 0db5839d93b14..ee0aaa86f7c63 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/ADUtils.cs @@ -12,11 +12,8 @@ namespace System.DirectoryServices.AccountManagement { - internal class ADUtils + internal static class ADUtils { - // To stop the compiler from autogenerating a constructor for this class - private ADUtils() { } - // We use this, rather than simply testing DirectoryEntry.SchemaClassName, because we don't // want to miss objects that are of a derived type. // Note that, since computer is a derived class of user in AD, if you don't want to confuse diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs index 65498e61b4819..62483eb22b56a 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/DSPropertyCollection.cs @@ -13,7 +13,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class dSPropertyCollection + internal sealed class dSPropertyCollection { private readonly PropertyCollection _pc; private readonly ResultPropertyCollection _rp; @@ -41,7 +41,7 @@ public dSPropertyValueCollection this[string propertyName] } } - internal class dSPropertyValueCollection + internal sealed class dSPropertyValueCollection { private readonly PropertyValueCollection _pc; private readonly ResultPropertyValueCollection _rc; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/QBEMatchType.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/QBEMatchType.cs index 764c1a953e97d..78d8bdf301fe7 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/QBEMatchType.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/QBEMatchType.cs @@ -5,7 +5,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class QbeMatchType + internal sealed class QbeMatchType { private object _value; private MatchType _matchType; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs index a39d0c626102a..0be16009b54b8 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs @@ -15,7 +15,7 @@ namespace System.DirectoryServices.AccountManagement /// if disposeDirEntry parameter is set to true in its constructor. /// /// - internal class RangeRetriever : CollectionBase, IEnumerable, IEnumerator, IDisposable + internal sealed class RangeRetriever : CollectionBase, IEnumerable, IEnumerator, IDisposable { /// /// diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs index 07a3bc43b90bd..1b0c87719373e 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSCache.cs @@ -14,7 +14,7 @@ namespace System.DirectoryServices.AccountManagement /// /// This is a class designed to cache DirectoryEntires instead of creating them every time. /// - internal class SDSCache + internal sealed class SDSCache { public static SDSCache Domain { @@ -205,13 +205,13 @@ private SDSCache(bool isSAM) private readonly object _tableLock = new object(); private readonly bool _isSAM; - private class CredHolder + private sealed class CredHolder { public Hashtable explicitCreds = new Hashtable(); public Hashtable defaultCreds = new Hashtable(); } - private class Placeholder + private sealed class Placeholder { // initially non-signaled public ManualResetEvent contextReadyEvent = new ManualResetEvent(false); diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs index 7ffa76e66d702..d747da6bfe3d3 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SDSUtils.cs @@ -12,11 +12,8 @@ namespace System.DirectoryServices.AccountManagement { - internal class SDSUtils + internal static class SDSUtils { - // To stop the compiler from autogenerating a constructor for this class - private SDSUtils() { } - internal static Principal SearchResultToPrincipal(SearchResult sr, PrincipalContext owningContext, Type principalType) { Principal p; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs index 109b59086c4bc..a06b3ac12c489 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/SidList.cs @@ -12,7 +12,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class SidList + internal sealed class SidList { internal SidList(List sidListByteFormat) : this(sidListByteFormat, null, null) { @@ -66,7 +66,7 @@ internal SidList(UnsafeNativeMethods.SID_AND_ATTR[] sidAndAttr) TranslateSids(null, pSids); } - protected void TranslateSids(string target, IntPtr[] pSids) + private void TranslateSids(string target, IntPtr[] pSids) { GlobalDebug.WriteLineIf(GlobalDebug.Info, "AuthZSet", "SidList: processing {0} SIDs", pSids.Length); @@ -305,7 +305,7 @@ public int Compare(SidListEntry entry1, SidListEntry entry2) } ********/ - internal class SidListEntry : IDisposable + internal sealed class SidListEntry : IDisposable { public IntPtr pSid = IntPtr.Zero; public string name; @@ -313,7 +313,7 @@ internal class SidListEntry : IDisposable // // IDisposable // - public virtual void Dispose() + public void Dispose() { if (pSid != IntPtr.Zero) { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs index bd9cc4962d7eb..0e7a2f55840a5 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/TokenGroupsSet.cs @@ -11,7 +11,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class TokenGroupSet : ResultSet + internal sealed class TokenGroupSet : ResultSet { internal TokenGroupSet( string userDN, diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs index 822a5d0b1ab6c..326d14d4e5ca3 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AccountInfo.cs @@ -11,7 +11,7 @@ namespace System.DirectoryServices.AccountManagement #if TESTHOOK public class AccountInfo #else - internal class AccountInfo + internal sealed class AccountInfo #endif { // diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs index 19efbfba03ffd..4c6491929ad38 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AuthZSet.cs @@ -13,7 +13,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class AuthZSet : ResultSet + internal sealed class AuthZSet : ResultSet { internal AuthZSet( byte[] userSid, diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ConfigurationHandler.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ConfigurationHandler.cs index fc0e04949d57c..1486d9010d574 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ConfigurationHandler.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ConfigurationHandler.cs @@ -10,7 +10,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class ConfigSettings + internal sealed class ConfigSettings { public ConfigSettings(DebugLevel debugLevel, string debugLogFile) { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/EmptySet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/EmptySet.cs index c5f89702131c6..3a1551910461c 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/EmptySet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/EmptySet.cs @@ -6,7 +6,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class EmptySet : BookmarkableResultSet + internal sealed class EmptySet : BookmarkableResultSet { internal EmptySet() { @@ -43,7 +43,7 @@ internal override void RestoreBookmark(ResultSetBookmark bookmark) } } - internal class EmptySetBookmark : ResultSetBookmark + internal sealed class EmptySetBookmark : ResultSetBookmark { } } diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionCache.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionCache.cs index c7db802646b59..dc814522927cd 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionCache.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionCache.cs @@ -8,7 +8,7 @@ namespace System.DirectoryServices.AccountManagement using System.Collections; using System.DirectoryServices; - internal class ExtensionCacheValue + internal sealed class ExtensionCacheValue { internal ExtensionCacheValue(object[] value) { @@ -47,7 +47,7 @@ internal MatchType MatchType private readonly MatchType _matchType; } - internal class ExtensionCache + internal sealed class ExtensionCache { private readonly Dictionary _cache = new Dictionary(); diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs index 212b1f664952b..1a7c3ac330f67 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ExtensionHelper.cs @@ -7,7 +7,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class ExtensionHelper + internal sealed class ExtensionHelper { internal ExtensionHelper(Principal p) { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs index fc60e2c928a97..2b892033b299c 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/FindResultEnumerator.cs @@ -8,7 +8,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class FindResultEnumerator : IEnumerator, IEnumerator + internal sealed class FindResultEnumerator : IEnumerator, IEnumerator { // // Public properties diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityClaim.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityClaim.cs index 41510a4b48292..fd84e5354ebf8 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityClaim.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityClaim.cs @@ -6,7 +6,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class IdentityClaim + internal sealed class IdentityClaim { public string UrnValue { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityReference.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityReference.cs index 5891b2d061499..d39223855093a 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityReference.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityReference.cs @@ -6,7 +6,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class IdentityReference + internal sealed class IdentityReference { public string UrnValue { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityType.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityType.cs index 4235b1b054431..71ea59f4e314f 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityType.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/IdentityType.cs @@ -16,18 +16,18 @@ public enum IdentityType Guid = 5 } - internal class IdentMap + internal static class IdentMap { - private IdentMap() { } - internal static object[,] StringMap = { - {IdentityType.SamAccountName, IdentityTypeStringMap.SamAccount}, - {IdentityType.Name, IdentityTypeStringMap.Name}, - {IdentityType.UserPrincipalName, IdentityTypeStringMap.Upn}, - {IdentityType.DistinguishedName, IdentityTypeStringMap.DistinguishedName}, - {IdentityType.Sid, IdentityTypeStringMap.Sid}, - {IdentityType.Guid, IdentityTypeStringMap.Guid}}; + {IdentityType.SamAccountName, IdentityTypeStringMap.SamAccount}, + {IdentityType.Name, IdentityTypeStringMap.Name}, + {IdentityType.UserPrincipalName, IdentityTypeStringMap.Upn}, + {IdentityType.DistinguishedName, IdentityTypeStringMap.DistinguishedName}, + {IdentityType.Sid, IdentityTypeStringMap.Sid}, + {IdentityType.Guid, IdentityTypeStringMap.Guid} + }; } + internal static class IdentityTypeStringMap { public const string Guid = "ms-guid"; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs index 13713bcb4e5a4..e208968250bb7 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/NetCred.cs @@ -10,7 +10,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class NetCred + internal sealed class NetCred { public NetCred(string username, string password) { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Pair.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Pair.cs index d83fc70ebe17e..3e4d71c5e1385 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Pair.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Pair.cs @@ -6,7 +6,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class Pair + internal sealed class Pair { // // Constructor diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs index a478199410e07..3e22c9282dbd7 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PasswordInfo.cs @@ -10,7 +10,7 @@ namespace System.DirectoryServices.AccountManagement #if TESTHOOK public class PasswordInfo #else - internal class PasswordInfo + internal sealed class PasswordInfo #endif { // diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs index f98261081b385..edb88e95d3436 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Principal.cs @@ -717,7 +717,7 @@ internal void AdvancedFilterSet(string attribute, object value, Type objectType, else _extensionCache.properties[attribute] = new ExtensionCacheValue(new object[] { value }, objectType, mt); - _extensionCacheChanged = LoadState.Changed; ; + _extensionCacheChanged = LoadState.Changed; } // diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs index 0b7efe1cd818e..d247d9fa03d7c 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/PrincipalCollectionEnumerator.cs @@ -9,7 +9,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class PrincipalCollectionEnumerator : IEnumerator, IEnumerator + internal sealed class PrincipalCollectionEnumerator : IEnumerator, IEnumerator { // // Public properties diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/QbeFilterDescription.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/QbeFilterDescription.cs index 35980616f8a12..32bb7e164ebab 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/QbeFilterDescription.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/QbeFilterDescription.cs @@ -10,7 +10,7 @@ namespace System.DirectoryServices.AccountManagement // // A collection of individual property filters // - internal class QbeFilterDescription + internal sealed class QbeFilterDescription { private readonly ArrayList _filtersToApply = new ArrayList(); @@ -31,11 +31,8 @@ public ArrayList FiltersToApply // // Constructs individual property filters, given the name of the property // - internal class FilterFactory + internal static class FilterFactory { - // Put a private constructor because this class should only be used as static methods - private FilterFactory() { } - private static readonly Hashtable s_subclasses = new Hashtable(); static FilterFactory() @@ -123,236 +120,236 @@ public object Extra // The derived classes - internal class DescriptionFilter : FilterBase + internal sealed class DescriptionFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalDescription; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class SidFilter : FilterBase + internal sealed class SidFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalSid; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class SamAccountNameFilter : FilterBase + internal sealed class SamAccountNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalSamAccountName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class DistinguishedNameFilter : FilterBase + internal sealed class DistinguishedNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalDistinguishedName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class GuidFilter : FilterBase + internal sealed class GuidFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalGuid; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class IdentityClaimFilter : FilterBase + internal sealed class IdentityClaimFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalIdentityClaims; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class UserPrincipalNameFilter : FilterBase + internal sealed class UserPrincipalNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalUserPrincipalName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class StructuralObjectClassFilter : FilterBase + internal sealed class StructuralObjectClassFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalStructuralObjectClass; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class NameFilter : FilterBase + internal sealed class NameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class DisplayNameFilter : FilterBase + internal sealed class DisplayNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalDisplayName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class CertificateFilter : FilterBase + internal sealed class CertificateFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AuthenticablePrincipalCertificates; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class AuthPrincEnabledFilter : FilterBase + internal sealed class AuthPrincEnabledFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AuthenticablePrincipalEnabled; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class PermittedWorkstationFilter : FilterBase + internal sealed class PermittedWorkstationFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoPermittedWorkstations; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class PermittedLogonTimesFilter : FilterBase + internal sealed class PermittedLogonTimesFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoPermittedLogonTimes; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class ExpirationDateFilter : FilterBase + internal sealed class ExpirationDateFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoExpirationDate; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class SmartcardLogonRequiredFilter : FilterBase + internal sealed class SmartcardLogonRequiredFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoSmartcardRequired; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class DelegationPermittedFilter : FilterBase + internal sealed class DelegationPermittedFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoDelegationPermitted; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class HomeDirectoryFilter : FilterBase + internal sealed class HomeDirectoryFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoHomeDirectory; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class HomeDriveFilter : FilterBase + internal sealed class HomeDriveFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoHomeDrive; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class ScriptPathFilter : FilterBase + internal sealed class ScriptPathFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoScriptPath; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class PasswordNotRequiredFilter : FilterBase + internal sealed class PasswordNotRequiredFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PwdInfoPasswordNotRequired; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class PasswordNeverExpiresFilter : FilterBase + internal sealed class PasswordNeverExpiresFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PwdInfoPasswordNeverExpires; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class CannotChangePasswordFilter : FilterBase + internal sealed class CannotChangePasswordFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PwdInfoCannotChangePassword; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class AllowReversiblePasswordEncryptionFilter : FilterBase + internal sealed class AllowReversiblePasswordEncryptionFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PwdInfoAllowReversiblePasswordEncryption; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class GivenNameFilter : FilterBase + internal sealed class GivenNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.UserGivenName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class MiddleNameFilter : FilterBase + internal sealed class MiddleNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.UserMiddleName; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class SurnameFilter : FilterBase + internal sealed class SurnameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.UserSurname; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class EmailAddressFilter : FilterBase + internal sealed class EmailAddressFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.UserEmailAddress; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class VoiceTelephoneNumberFilter : FilterBase + internal sealed class VoiceTelephoneNumberFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.UserVoiceTelephoneNumber; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class EmployeeIDFilter : FilterBase + internal sealed class EmployeeIDFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.UserEmployeeID; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class GroupIsSecurityGroupFilter : FilterBase + internal sealed class GroupIsSecurityGroupFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.GroupIsSecurityGroup; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class GroupScopeFilter : FilterBase + internal sealed class GroupScopeFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.GroupGroupScope; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class ServicePrincipalNameFilter : FilterBase + internal sealed class ServicePrincipalNameFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.ComputerServicePrincipalNames; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class ExtensionCacheFilter : FilterBase + internal sealed class ExtensionCacheFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PrincipalExtensionCache; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class BadPasswordAttemptFilter : FilterBase + internal sealed class BadPasswordAttemptFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PwdInfoLastBadPasswordAttempt; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class LastLogonTimeFilter : FilterBase + internal sealed class LastLogonTimeFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoLastLogon; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class LockoutTimeFilter : FilterBase + internal sealed class LockoutTimeFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoAcctLockoutTime; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class ExpiredAccountFilter : FilterBase + internal sealed class ExpiredAccountFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoExpiredAccount; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class PasswordSetTimeFilter : FilterBase + internal sealed class PasswordSetTimeFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.PwdInfoLastPasswordSet; public override string PropertyName { get { return PropertyNameStatic; } } } - internal class BadLogonCountFilter : FilterBase + internal sealed class BadLogonCountFilter : FilterBase { public const string PropertyNameStatic = PropertyNames.AcctInfoBadLogonCount; public override string PropertyName { get { return PropertyNameStatic; } } diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs index b4f4afe53b6d4..22a545a591ebf 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMGroupsSet.cs @@ -12,7 +12,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class SAMGroupsSet : ResultSet + internal sealed class SAMGroupsSet : ResultSet { internal SAMGroupsSet(UnsafeNativeMethods.IADsMembers iADsMembers, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) { diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs index e8070d4ca3534..f8134abdbe127 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMMembersSet.cs @@ -13,7 +13,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class SAMMembersSet : BookmarkableResultSet + internal sealed class SAMMembersSet : BookmarkableResultSet { internal SAMMembersSet(string groupPath, UnsafeNativeMethods.IADsGroup group, bool recursive, SAMStoreCtx storeCtx, DirectoryEntry ctxBase) { @@ -597,7 +597,7 @@ public override void Dispose() private ResultSet _foreignResultSet; // current foreign group's ResultSet (if enumerating via proxy to foreign group) } - internal class SAMMembersSetBookmark : ResultSetBookmark + internal sealed class SAMMembersSetBookmark : ResultSetBookmark { public List groupsToVisit; public List groupsVisited; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs index 20793aebefcca..7af3ed85f00ce 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMQuerySet.cs @@ -11,7 +11,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class SAMQuerySet : ResultSet + internal sealed class SAMQuerySet : ResultSet { // We will iterate over all principals under ctxBase, returning only those which are in the list of types and which // satisfy ALL the matching properties. @@ -202,7 +202,7 @@ internal abstract class SAMMatcher // The matcher routines for query-by-example support // - internal class QbeMatcher : SAMMatcher + internal sealed class QbeMatcher : SAMMatcher { private readonly QbeFilterDescription _propertiesToMatch; @@ -314,7 +314,7 @@ internal override bool Matches(DirectoryEntry de) private static readonly Hashtable s_filterPropertiesTable = CreateFilterPropertiesTable(); - private class FilterPropertyTableEntry + private sealed class FilterPropertyTableEntry { internal string winNTPropertyName; internal MatcherDelegate matcher; @@ -666,7 +666,7 @@ private static bool GroupTypeMatcher(FilterBase filter, string winNTPropertyName // The matcher routines for FindBy* support // - internal class FindByDateMatcher : SAMMatcher + internal sealed class FindByDateMatcher : SAMMatcher { internal enum DateProperty { @@ -804,7 +804,7 @@ private bool TestForMatch(Nullable nullableStoreValue) } } - internal class GroupMemberMatcher : SAMMatcher + internal sealed class GroupMemberMatcher : SAMMatcher { private readonly byte[] _memberSidToMatch; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx.cs index 65f616ea199fb..9f57bc6e5e796 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx.cs @@ -9,7 +9,7 @@ namespace System.DirectoryServices.AccountManagement { - internal partial class SAMStoreCtx : StoreCtx + internal sealed partial class SAMStoreCtx : StoreCtx { private readonly DirectoryEntry _ctxBase; private readonly object _ctxBaseLock = new object(); // when mutating ctxBase diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_LoadStore.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_LoadStore.cs index 3be71349a6a42..f1f39b9b53ef6 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_LoadStore.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_LoadStore.cs @@ -17,7 +17,7 @@ namespace System.DirectoryServices.AccountManagement { - internal partial class SAMStoreCtx : StoreCtx + internal sealed partial class SAMStoreCtx : StoreCtx { // // Native <--> Principal @@ -743,7 +743,7 @@ private enum ObjectMask Principal = User | Computer | Group } - private class PropertyMappingTableEntry + private sealed class PropertyMappingTableEntry { internal string propertyName; // PAPI name internal string suggestedWinNTPropertyName; // WinNT attribute name diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs index b48fe5051878d..fea8ea271a984 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreCtx_Query.cs @@ -12,7 +12,7 @@ namespace System.DirectoryServices.AccountManagement { - internal partial class SAMStoreCtx : StoreCtx + internal sealed partial class SAMStoreCtx : StoreCtx { // // Query operations diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreKey.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreKey.cs index 11b7612c9ab5e..4d03a2fd5e83a 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreKey.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMStoreKey.cs @@ -7,7 +7,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class SAMStoreKey : StoreKey + internal sealed class SAMStoreKey : StoreKey { private readonly byte[] _sid; private readonly string _machineName; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs index 999d44c6357da..9ea29c680ad6e 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/SAM/SAMUtils.cs @@ -11,11 +11,8 @@ namespace System.DirectoryServices.AccountManagement { - internal class SAMUtils + internal static class SAMUtils { - // To stop the compiler from autogenerating a constructor for this class - private SAMUtils() { } - internal static bool IsOfObjectClass(DirectoryEntry de, string classToCompare) { return string.Equals(de.SchemaClassName, classToCompare, StringComparison.OrdinalIgnoreCase); diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollection.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollection.cs index eb16ae3118c16..c177f70c758f1 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollection.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollection.cs @@ -8,7 +8,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class TrackedCollection : ICollection, ICollection, IEnumerable, IEnumerable + internal sealed class TrackedCollection : ICollection, ICollection, IEnumerable, IEnumerable { // // ICollection @@ -197,7 +197,7 @@ public bool Remove(T value) // Private implementation // - internal class ValueEl + internal sealed class ValueEl { public bool isInserted; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollectionEnumerator.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollectionEnumerator.cs index 81cd5ab77c9a8..279243e436353 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollectionEnumerator.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/TrackedCollectionEnumerator.cs @@ -8,7 +8,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class TrackedCollectionEnumerator : IEnumerator, IEnumerator + internal sealed class TrackedCollectionEnumerator : IEnumerator, IEnumerator { // // Public properties diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs index 238d4b3457349..bec63840291cd 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/UnknownPrincipal.cs @@ -9,7 +9,7 @@ namespace System.DirectoryServices.AccountManagement { [DirectoryRdnPrefix("CN")] - internal class UnknownPrincipal : Principal + internal sealed class UnknownPrincipal : Principal { // // Public constructors diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs index 96a2d684a938d..a60381397f80d 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/User.cs @@ -416,12 +416,12 @@ internal override void ResetAllChangeStatus() { GlobalDebug.WriteLineIf(GlobalDebug.Info, "User", "ResetAllChangeStatus"); - _givenNameChanged = (_givenNameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; ; - _middleNameChanged = (_middleNameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; ; - _surnameChanged = (_surnameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; ; - _emailAddressChanged = (_emailAddressChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; ; - _voiceTelephoneNumberChanged = (_voiceTelephoneNumberChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; ; - _employeeIDChanged = (_employeeIDChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; ; + _givenNameChanged = (_givenNameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; + _middleNameChanged = (_middleNameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; + _surnameChanged = (_surnameChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; + _emailAddressChanged = (_emailAddressChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; + _voiceTelephoneNumberChanged = (_voiceTelephoneNumberChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; + _employeeIDChanged = (_employeeIDChanged == LoadState.Changed) ? LoadState.Loaded : LoadState.NotSet; base.ResetAllChangeStatus(); } diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs index 41d629044809f..65d159fd6c5f8 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/Utils.cs @@ -13,11 +13,8 @@ namespace System.DirectoryServices.AccountManagement { - internal class Utils + internal static class Utils { - // To stop the compiler from autogenerating a constructor for this class - private Utils() { } - // // byte utilities // diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs index 838af2be33f5d..dc9f7326b5c53 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/ValueCollectionEnumerator.cs @@ -8,7 +8,7 @@ namespace System.DirectoryServices.AccountManagement { - internal class ValueCollectionEnumerator : IEnumerator, IEnumerator + internal sealed class ValueCollectionEnumerator : IEnumerator, IEnumerator // T must be a ValueType { // diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/constants.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/constants.cs index 93c5e6f23c771..98be922fbe23c 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/constants.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/constants.cs @@ -24,17 +24,15 @@ internal static class DefaultContextOptions internal static ContextOptions ADDefaultContextOption = ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing; } - internal class LdapConstants + internal static class LdapConstants { public static int LDAP_SSL_PORT = 636; public static int LDAP_PORT = 389; internal static DateTime defaultUtcTime = new DateTime(1601, 1, 1, 0, 0, 0); - private LdapConstants() { } } // The string constants used internally to specify each property - internal class PropertyNames + internal static class PropertyNames { - private PropertyNames() { } // Principal internal const string PrincipalDisplayName = "Principal.DisplayName"; internal const string PrincipalDescription = "Principal.Description"; @@ -95,16 +93,14 @@ private PropertyNames() { } // these two are not publicly exposed properties, but are used internally to track ResetPassword/ExpirePasswordNow // operations against unpersisted principals, so that they can be performed once the principal has been Saved - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Not a password.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Not a password.")] internal const string PwdInfoPassword = "AuthenticablePrincipal.PasswordInfo.Password"; internal const string PwdInfoExpireImmediately = "AuthenticablePrincipal.PasswordInfo.ExpireImmediately"; } // Given an internal property name (from PropertyNames), returns the external form of the name for use in error-reporting - internal class PropertyNamesExternal + internal static class PropertyNamesExternal { - private PropertyNamesExternal() { } - private static readonly int s_acctInfoPrefixLength = PropertyNames.AcctInfoPrefix.Length; private static readonly int s_pwdInfoPrefixLength = PropertyNames.PwdInfoPrefix.Length; diff --git a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs index db4233f1bb677..583bb416a0755 100644 --- a/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs +++ b/src/libraries/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/interopt.cs @@ -14,19 +14,15 @@ namespace System.DirectoryServices.AccountManagement using System.Security; using System.Text; - internal class Constants + internal static class Constants { - private Constants() { } internal static byte[] GUID_USERS_CONTAINER_BYTE = new byte[] { 0xa9, 0xd1, 0xca, 0x15, 0x76, 0x88, 0x11, 0xd1, 0xad, 0xed, 0x00, 0xc0, 0x4f, 0xd8, 0xd5, 0xcd }; internal static byte[] GUID_COMPUTRS_CONTAINER_BYTE = new byte[] { 0xaa, 0x31, 0x28, 0x25, 0x76, 0x88, 0x11, 0xd1, 0xad, 0xed, 0x00, 0xc0, 0x4f, 0xd8, 0xd5, 0xcd }; internal static byte[] GUID_FOREIGNSECURITYPRINCIPALS_CONTAINER_BYTE = new byte[] { 0x22, 0xb7, 0x0c, 0x67, 0xd5, 0x6e, 0x4e, 0xfb, 0x91, 0xe9, 0x30, 0x0f, 0xca, 0x3d, 0xc1, 0xaa }; } - internal class SafeNativeMethods + internal static class SafeNativeMethods { - // To stop the compiler from autogenerating a constructor for this class - private SafeNativeMethods() { } - [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "GetCurrentThreadId", CharSet = CharSet.Unicode)] public static extern int GetCurrentThreadId(); @@ -34,11 +30,8 @@ private SafeNativeMethods() { } public static extern int LsaNtStatusToWinError(int ntStatus); } - internal class UnsafeNativeMethods + internal static class UnsafeNativeMethods { - // To stop the compiler from autogenerating a constructor for this class - private UnsafeNativeMethods() { } - [DllImport(ExternDll.Activeds, ExactSpelling = true, EntryPoint = "ADsOpenObject", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] private static extern int IntADsOpenObject(string path, string userName, string password, int flags, [In, Out] ref Guid iid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppObject); public static int ADsOpenObject(string path, string userName, string password, int flags, [In, Out] ref Guid iid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppObject) diff --git a/src/libraries/System.DirectoryServices.Protocols/Directory.Build.props b/src/libraries/System.DirectoryServices.Protocols/Directory.Build.props index 970b93ed8c2c0..4c915e0dd141b 100644 --- a/src/libraries/System.DirectoryServices.Protocols/Directory.Build.props +++ b/src/libraries/System.DirectoryServices.Protocols/Directory.Build.props @@ -7,7 +7,7 @@ 4.0.0.0 Microsoft true - browser + browser;android;ios;tvos Provides the methods defined in the Lightweight Directory Access Protocol (LDAP) version 3 (V3) and Directory Services Markup Language (DSML) version 2.0 (V2) standards. - \ No newline at end of file + diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs index ba4bce11ae35e..ba46374c53c94 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs @@ -43,7 +43,7 @@ public enum SearchOption PhantomRoot = 2 } - internal class UtilityHandle + internal static class UtilityHandle { private static readonly ConnectionHandle s_handle = new ConnectionHandle(); diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/ResultCode.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/ResultCode.cs index d9ac25ad4cd71..07a41b903a931 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/ResultCode.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/ResultCode.cs @@ -52,7 +52,7 @@ public enum ResultCode Other = 80 } - internal class OperationErrorMappings + internal static class OperationErrorMappings { private static readonly Dictionary s_resultCodeMapping = new Dictionary(capacity: 43) { diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/utils.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/utils.cs index bc73cada6afc4..53df2213b2b18 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/utils.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/utils.cs @@ -5,7 +5,7 @@ namespace System.DirectoryServices.Protocols { - internal class Utility + internal static class Utility { internal static bool IsResultCode(ResultCode code) { diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapAsyncResult.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapAsyncResult.cs index 25a90ad33f3fc..3b83e360ee28a 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapAsyncResult.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapAsyncResult.cs @@ -58,7 +58,7 @@ public LdapAsyncWaitHandle(SafeWaitHandle handle) : base() } } - internal class LdapRequestState + internal sealed class LdapRequestState { internal DirectoryResponse _response; internal LdapAsyncResult _ldapAsync; @@ -75,7 +75,7 @@ internal enum ResultsStatus Done = 2 } - internal class LdapPartialAsyncResult : LdapAsyncResult + internal sealed class LdapPartialAsyncResult : LdapAsyncResult { internal LdapConnection _con; internal int _messageID = -1; diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs index 09d4f219c554c..06d892dd9ab9e 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapException.cs @@ -34,7 +34,7 @@ internal enum LdapError SendTimeOut = 0x70 } - internal class LdapErrorMappings + internal static class LdapErrorMappings { private static readonly Dictionary s_resultCodeMapping = new Dictionary(capacity: 20) { @@ -161,7 +161,7 @@ public TlsOperationException(DirectoryResponse response, string message, Excepti } } - internal class ErrorChecking + internal static class ErrorChecking { public static void CheckAndSetLdapError(int error) { diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapPartialResultsProcessor.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapPartialResultsProcessor.cs index ca6744dde1ea1..4f5b6b37f529a 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapPartialResultsProcessor.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/ldap/LdapPartialResultsProcessor.cs @@ -9,7 +9,7 @@ namespace System.DirectoryServices.Protocols { - internal class LdapPartialResultsProcessor + internal sealed class LdapPartialResultsProcessor { private readonly ArrayList _resultList = new ArrayList(); private readonly ManualResetEvent _workThreadWaitHandle; @@ -333,7 +333,7 @@ private void AddResult(SearchResponse partialResults, SearchResponse newResult) } } - internal class PartialResultsRetriever + internal sealed class PartialResultsRetriever { private readonly ManualResetEvent _workThreadWaitHandle; private readonly LdapPartialResultsProcessor _processor; diff --git a/src/libraries/System.DirectoryServices/ref/System.DirectoryServices.manual.cs b/src/libraries/System.DirectoryServices/ref/System.DirectoryServices.manual.cs index 9989215fd6176..2462cbec76e9f 100644 --- a/src/libraries/System.DirectoryServices/ref/System.DirectoryServices.manual.cs +++ b/src/libraries/System.DirectoryServices/ref/System.DirectoryServices.manual.cs @@ -8,5 +8,5 @@ namespace System.DirectoryServices { [System.ComponentModel.TypeConverter(typeof(DirectoryEntryConverter))] public partial class DirectoryEntry { } - internal class DirectoryEntryConverter { } + internal sealed class DirectoryEntryConverter { } } diff --git a/src/libraries/System.DirectoryServices/src/Interop/AdsValueHelper2.cs b/src/libraries/System.DirectoryServices/src/Interop/AdsValueHelper2.cs index d834eb54409ef..080dba7450b86 100644 --- a/src/libraries/System.DirectoryServices/src/Interop/AdsValueHelper2.cs +++ b/src/libraries/System.DirectoryServices/src/Interop/AdsValueHelper2.cs @@ -21,7 +21,7 @@ internal struct SystemTime } [StructLayout(LayoutKind.Sequential)] - internal class DnWithBinary + internal sealed class DnWithBinary { public int dwLength; public IntPtr lpBinaryValue; // GUID of directory object @@ -29,7 +29,7 @@ internal class DnWithBinary } [StructLayout(LayoutKind.Sequential)] - internal class DnWithString + internal sealed class DnWithString { public IntPtr pszStringValue; // associated value public IntPtr pszDNString; // Distinguished Name @@ -38,7 +38,7 @@ internal class DnWithString /// /// Helper class for dealing with struct AdsValue. /// - internal class AdsValueHelper + internal sealed class AdsValueHelper { public AdsValue adsvalue; private GCHandle _pinnedHandle; diff --git a/src/libraries/System.DirectoryServices/src/Interop/NativeMethods.cs b/src/libraries/System.DirectoryServices/src/Interop/NativeMethods.cs index 19124aeee55bd..7ce5748edbc6c 100644 --- a/src/libraries/System.DirectoryServices/src/Interop/NativeMethods.cs +++ b/src/libraries/System.DirectoryServices/src/Interop/NativeMethods.cs @@ -3,7 +3,7 @@ namespace System.DirectoryServices.Interop { - internal class NativeMethods + internal static class NativeMethods { public enum AuthenticationModes { diff --git a/src/libraries/System.DirectoryServices/src/Interop/SafeNativeMethods.cs b/src/libraries/System.DirectoryServices/src/Interop/SafeNativeMethods.cs index 461be3d67afaf..f1ad83abb156c 100644 --- a/src/libraries/System.DirectoryServices/src/Interop/SafeNativeMethods.cs +++ b/src/libraries/System.DirectoryServices/src/Interop/SafeNativeMethods.cs @@ -7,7 +7,7 @@ namespace System.DirectoryServices.Interop { - internal class SafeNativeMethods + internal static class SafeNativeMethods { [DllImport(ExternDll.Oleaut32, PreserveSig = false)] public static extern void VariantClear(IntPtr pObject); diff --git a/src/libraries/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs b/src/libraries/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs index d862355eb6760..168287d83bca1 100644 --- a/src/libraries/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs +++ b/src/libraries/System.DirectoryServices/src/Interop/UnsafeNativeMethods.cs @@ -26,7 +26,7 @@ internal struct Variant public IntPtr ptr2; } - internal class UnsafeNativeMethods + internal static class UnsafeNativeMethods { [DllImport(ExternDll.Activeds, ExactSpelling = true, EntryPoint = "ADsOpenObject", CharSet = System.Runtime.InteropServices.CharSet.Unicode)] private static extern int IntADsOpenObject(string path, string? userName, string? password, int flags, [In, Out] ref Guid iid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppObject); diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ADSearcher.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ADSearcher.cs index 898d79cc0ee38..098a2bbf2a745 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ADSearcher.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ADSearcher.cs @@ -5,7 +5,7 @@ namespace System.DirectoryServices.ActiveDirectory { - internal class ADSearcher + internal sealed class ADSearcher { private readonly DirectorySearcher _searcher; private static readonly TimeSpan s_defaultTimeSpan = new TimeSpan(0, 120, 0); diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ActiveDirectorySyntax.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ActiveDirectorySyntax.cs index 6d31c04bb8aba..7b6c094b56eeb 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ActiveDirectorySyntax.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/ActiveDirectorySyntax.cs @@ -30,7 +30,7 @@ public enum ActiveDirectorySyntax : int ReplicaLink = 22 } - internal class OMObjectClass + internal sealed class OMObjectClass { public OMObjectClass(byte[] data) => Data = data; @@ -58,7 +58,7 @@ public bool Equals(OMObjectClass OMObjectClass) public byte[] Data { get; } } - internal class Syntax + internal sealed class Syntax { public readonly string attributeSyntax; public readonly int oMSyntax; diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryEntryManager.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryEntryManager.cs index 296d91115d325..d30f70c40eb2e 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryEntryManager.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DirectoryEntryManager.cs @@ -10,7 +10,7 @@ namespace System.DirectoryServices.ActiveDirectory /// Internal class that is used as a key in the hashtable /// of directory entries /// - internal class DistinguishedName + internal sealed class DistinguishedName { public DistinguishedName(string dn) { @@ -81,7 +81,7 @@ public override string ToString() /// and creates a new directory entry (for a given dn) only if /// it doesn't already exist /// - internal class DirectoryEntryManager + internal sealed class DirectoryEntryManager { private readonly Hashtable _directoryEntries = new Hashtable(); private string? _bindingPrefix; diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DomainController.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DomainController.cs index 0575534ed6b83..e0028300fc0de 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DomainController.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/DomainController.cs @@ -355,7 +355,7 @@ public void TransferRoleOwnership(ActiveDirectoryRole role) } catch (COMException e) { - throw ExceptionHelper.GetExceptionFromCOMException(context, e); ; + throw ExceptionHelper.GetExceptionFromCOMException(context, e); } // invalidate the role collection so that it gets loaded again next time diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs index fd37da0e8bf3d..950075ba9c8be 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/Exception.cs @@ -240,7 +240,7 @@ public override void GetObjectData(SerializationInfo serializationInfo, Streamin } } - internal class ExceptionHelper + internal static class ExceptionHelper { private const int ERROR_NOT_ENOUGH_MEMORY = 8; // map to outofmemory exception private const int ERROR_OUTOFMEMORY = 14; // map to outofmemory exception diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/PropertyManager.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/PropertyManager.cs index 9a4b9ef1129fb..278aa9cdad752 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/PropertyManager.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/PropertyManager.cs @@ -6,7 +6,7 @@ namespace System.DirectoryServices.ActiveDirectory { - internal class PropertyManager + internal static class PropertyManager { public static string DefaultNamingContext = "defaultNamingContext"; public static string SchemaNamingContext = "schemaNamingContext"; diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/TrustHelper.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/TrustHelper.cs index e4d5813316847..f03d5a4c448d9 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/TrustHelper.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/TrustHelper.cs @@ -35,7 +35,7 @@ internal enum TRUST_ATTRIBUTE TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL = 0x00000040 } - internal class TrustHelper + internal static class TrustHelper { private const int STATUS_OBJECT_NAME_NOT_FOUND = 2; internal const int ERROR_NOT_FOUND = 1168; @@ -55,8 +55,6 @@ internal class TrustHelper private const int ERROR_INVALID_LEVEL = 124; private static readonly char[] s_punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray(); - private TrustHelper() { } - internal static bool GetTrustedDomainInfoStatus(DirectoryContext context, string? sourceName, string targetName, TRUST_ATTRIBUTE attribute, bool isForest) { PolicySafeHandle? handle = null; diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs index a5b4e24c1cfdd..9d08308f6008b 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/ActiveDirectory/UnsafeNativeMethods.cs @@ -625,7 +625,7 @@ internal sealed class POLICY_ACCOUNT_DOMAIN_INFO public IntPtr domainSid = IntPtr.Zero; } - internal class UnsafeNativeMethods + internal static class UnsafeNativeMethods { public delegate int DsReplicaConsistencyCheck([In]IntPtr handle, int taskID, int flags); diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/AdsVLV.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/AdsVLV.cs index 4f9bb0a6eed2e..4f9b73107991c 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/AdsVLV.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/AdsVLV.cs @@ -6,7 +6,7 @@ namespace System.DirectoryServices { [StructLayout(LayoutKind.Sequential)] - internal class AdsVLV + internal sealed class AdsVLV { public int beforeCount; public int afterCount; diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs index 2bb99779757c8..f8af94cfacd86 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/Design/DirectoryEntryConverter.cs @@ -7,7 +7,7 @@ namespace System.DirectoryServices.Design { - internal class DirectoryEntryConverter : TypeConverter + internal sealed class DirectoryEntryConverter : TypeConverter { private static StandardValuesCollection? s_values; private static readonly Hashtable s_componentsCreated = new Hashtable(StringComparer.OrdinalIgnoreCase); diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntries.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntries.cs index 8672fd066c3c4..01ce52280b419 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntries.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntries.cs @@ -101,7 +101,7 @@ public void Remove(DirectoryEntry entry) /// Supports a simple ForEach-style iteration over a collection and defines /// enumerators, size, and synchronization methods. /// - private class ChildEnumerator : IEnumerator + private sealed class ChildEnumerator : IEnumerator { private readonly DirectoryEntry _container; private SafeNativeMethods.EnumVariant? _enumVariant; diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs index 8883d62821d45..ac1ee821b1c46 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/DirectoryServicesCOMException.cs @@ -38,7 +38,7 @@ public override void GetObjectData(SerializationInfo serializationInfo, Streamin } } - internal class COMExceptionHelper + internal static class COMExceptionHelper { internal static Exception CreateFormattedComException(int hr) { diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/PropertyCollection.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/PropertyCollection.cs index 4ba6161ce1cd1..51878e2cc52d1 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/PropertyCollection.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/PropertyCollection.cs @@ -171,7 +171,7 @@ void ICollection.CopyTo(Array array, int index) } } - private class PropertyEnumerator : IDictionaryEnumerator, IDisposable + private sealed class PropertyEnumerator : IDictionaryEnumerator, IDisposable { private readonly DirectoryEntry _entry; // clone (to be disposed) private readonly DirectoryEntry _parentEntry; // original entry to pass to PropertyValueCollection @@ -191,7 +191,7 @@ public void Dispose() GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposing) { @@ -278,7 +278,7 @@ public void CopyTo(Array array, int index) public virtual IEnumerator GetEnumerator() => new ValuesEnumerator(props); } - private class KeysCollection : ValuesCollection + private sealed class KeysCollection : ValuesCollection { public KeysCollection(PropertyCollection props) : base(props) { @@ -335,7 +335,7 @@ public bool MoveNext() public void Reset() => _currentIndex = -1; } - private class KeysEnumerator : ValuesEnumerator + private sealed class KeysEnumerator : ValuesEnumerator { public KeysEnumerator(PropertyCollection collection) : base(collection) { diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SchemaNameCollection.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SchemaNameCollection.cs index 2a2c6ab2a6ba8..853c659ecfd88 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SchemaNameCollection.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SchemaNameCollection.cs @@ -218,7 +218,7 @@ void ICollection.CopyTo(Array array, int index) // this class and HintsDelegateWrapper exist only because you can't create // a delegate to a property's accessors. You have to supply methods. So these // classes wrap an object and supply properties as methods. - internal class FilterDelegateWrapper + internal sealed class FilterDelegateWrapper { private readonly UnsafeNativeMethods.IAdsContainer _obj; internal FilterDelegateWrapper(UnsafeNativeMethods.IAdsContainer wrapped) diff --git a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SearchResultCollection.cs b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SearchResultCollection.cs index 129df11fbebd2..63f1230917d4f 100644 --- a/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SearchResultCollection.cs +++ b/src/libraries/System.DirectoryServices/src/System/DirectoryServices/SearchResultCollection.cs @@ -228,7 +228,7 @@ void ICollection.CopyTo(Array array, int index) /// /// Supports a simple ForEach-style iteration over a collection. /// - private class ResultsEnumerator : IEnumerator + private sealed class ResultsEnumerator : IEnumerator { private readonly NetworkCredential? _parentCredentials; private readonly AuthenticationTypes _parentAuthenticationType; diff --git a/src/libraries/System.Drawing.Common/Directory.Build.props b/src/libraries/System.Drawing.Common/Directory.Build.props index 1b6fc7d5280cc..addd07be3bbd8 100644 --- a/src/libraries/System.Drawing.Common/Directory.Build.props +++ b/src/libraries/System.Drawing.Common/Directory.Build.props @@ -2,7 +2,6 @@ Open - true true browser Provides access to GDI+ graphics functionality. diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs index 57a538561aa4e..fc4c1d957d18b 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Comdlg32.cs @@ -15,7 +15,7 @@ internal static partial class Comdlg32 internal static extern bool PrintDlg([In, Out] PRINTDLGX86 lppd); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - internal class PRINTDLG + internal sealed class PRINTDLG { internal int lStructSize; internal IntPtr hwndOwner; @@ -39,7 +39,7 @@ internal class PRINTDLG } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)] - internal class PRINTDLGX86 + internal sealed class PRINTDLGX86 { internal int lStructSize; internal IntPtr hwndOwner; diff --git a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs index 875dabd9ef392..6c3e188d9a7fc 100644 --- a/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs +++ b/src/libraries/System.Drawing.Common/src/Interop/Windows/Interop.Gdi32.cs @@ -101,7 +101,7 @@ internal unsafe struct BITMAPINFO_FLAT } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - internal class DOCINFO + internal sealed class DOCINFO { internal int cbSize = 20; internal string? lpszDocName; diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj index 70519aab76611..153bbc77f97fc 100644 --- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj +++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -339,7 +339,6 @@ - diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ClientUtils.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ClientUtils.cs index b74cb3d1374fc..0bb94bd19b8ce 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ClientUtils.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ClientUtils.cs @@ -44,7 +44,7 @@ public static bool IsSecurityOrCriticalException(Exception ex) /// Also avoid calling Remove(item). Instead call RemoveByHashCode(item) /// to make sure dead refs are removed. /// - internal class WeakRefCollection : IList + internal sealed class WeakRefCollection : IList { internal WeakRefCollection() : this(4) { } @@ -227,7 +227,7 @@ public int Add(object? value) /// added to a collection since Contains(WeakRef(item)) and Remove(WeakRef(item)) would not be able to /// identify the item. /// - internal class WeakRefObject + internal sealed class WeakRefObject { private readonly int _hash; private readonly WeakReference _weakHolder; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ColorConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ColorConverter.cs index ac819e36fa0ac..62bca37dc684c 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ColorConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ColorConverter.cs @@ -402,7 +402,7 @@ public override bool GetStandardValuesSupported(ITypeDescriptorContext context) /// IComparer for color values. This takes color values but compares their /// names. /// - private class ColorComparer : IComparer { + private sealed class ColorComparer : IComparer { public int Compare(object left, object right) { Color cLeft = (Color)left; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs index 9ccb2c51c12d5..91a932b60dc7b 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.Unix.cs @@ -65,7 +65,7 @@ private void CreateFont(string familyName, float emSize, FontStyle style, Graphi int status = Gdip.GdipCreateFont(new HandleRef(this, family.NativeFamily), emSize, style, unit, out _nativeFont); if (status == Gdip.FontStyleNotFound) - throw new ArgumentException(string.Format("Style {0} isn't supported by font {1}.", style.ToString(), familyName)); + throw new ArgumentException($"Style {style.ToString()} isn't supported by font {familyName}."); Gdip.CheckStatus(status); } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs index f2c6e339e2b79..dfa935c8ec0e6 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Font.cs @@ -262,16 +262,8 @@ public override int GetHashCode() /// /// Returns a human-readable string representation of this . /// - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "[{0}: Name={1}, Size={2}, Units={3}, GdiCharSet={4}, GdiVerticalFont={5}]", - GetType().Name, - FontFamily.Name, - _fontSize, - (int)_fontUnit, - _gdiCharSet, - _gdiVerticalFont); - } + public override string ToString() => + $"[{GetType().Name}: Name={FontFamily.Name}, Size={_fontSize}, Units={(int)_fontUnit}, GdiCharSet={_gdiCharSet}, GdiVerticalFont={_gdiVerticalFont}]"; // This is used by SystemFonts when constructing a system Font objects. internal void SetSystemFontName(string systemFontName) => _systemFontName = systemFontName; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs index 1d30015640544..f2f0594472a67 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs @@ -12,7 +12,7 @@ namespace System.Drawing { - internal partial class SafeNativeMethods + internal static partial class SafeNativeMethods { internal unsafe partial class Gdip { @@ -121,30 +121,9 @@ private static void PlatformInitialize() [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipSetImagePalette(IntPtr image, IntPtr palette); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyCount(IntPtr image, out uint propNumbers); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyIdList(IntPtr image, uint propNumbers, [Out] int[] list); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertySize(IntPtr image, out int bufferSize, out int propNumbers); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetAllPropertyItems(IntPtr image, int bufferSize, int propNumbers, IntPtr items); - [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetImageBounds(IntPtr image, out RectangleF source, ref GraphicsUnit unit); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyItemSize(IntPtr image, int propertyID, out int propertySize); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyItem(IntPtr image, int propertyID, int propertySize, IntPtr buffer); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSetPropertyItem(IntPtr image, GdipPropertyItem* propertyItem); - [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetImageThumbnail(IntPtr image, uint width, uint height, out IntPtr thumbImage, IntPtr callback, IntPtr callBackData); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs index 234eed272a771..47aeb4f9397c3 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Windows.cs @@ -248,27 +248,6 @@ private static void PlatformInitialize() [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetImagePaletteSize(HandleRef image, out int size); - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyCount(HandleRef image, out int count); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyIdList(HandleRef image, int count, int[] list); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out int size); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertyItem(HandleRef image, int propid, int size, IntPtr buffer); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetPropertySize(HandleRef image, out int totalSize, ref int count); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipGetAllPropertyItems(HandleRef image, int totalSize, int count, IntPtr buffer); - - [DllImport(LibraryName, ExactSpelling = true)] - internal static extern int GdipSetPropertyItem(HandleRef image, PropertyItemInternal propitem); - [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipImageForceValidation(IntPtr image); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs index 4071574574e10..18619993c0d1d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs @@ -1060,9 +1060,30 @@ internal static unsafe partial class Gdip [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipImageRotateFlip(HandleRef image, int rotateFlipType); + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetAllPropertyItems(HandleRef image, uint totalBufferSize, uint numProperties, PropertyItemInternal* allItems); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyCount(HandleRef image, out uint numOfProperty); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyIdList(HandleRef image, uint numOfProperty, int* list); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyItem(HandleRef image, int propid, uint propSize, PropertyItemInternal* buffer); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out uint size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertySize(HandleRef image, out uint totalBufferSize, out uint numProperties); + [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipRemovePropertyItem(HandleRef image, int propid); + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPropertyItem(HandleRef image, PropertyItemInternal* item); + [DllImport(LibraryName, ExactSpelling = true)] internal static extern int GdipGetImageType(HandleRef image, out int type); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs index fcffae054bd25..82340aa17020f 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs @@ -8,7 +8,7 @@ namespace System.Drawing /// /// Contains information about the context of a Graphics object. /// - internal class GraphicsContext : IDisposable + internal sealed class GraphicsContext : IDisposable { /// /// The state that identifies the context. diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs index 9c4b2ddd921fe..bd14e2d73e7f7 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Unix.cs @@ -101,7 +101,7 @@ internal abstract class ImageData }; [StructLayout(LayoutKind.Sequential)] - internal class IconImage : ImageData + internal sealed class IconImage : ImageData { internal BitmapInfoHeader iconHeader; //image header internal uint[]? iconColors; //colors table @@ -110,7 +110,7 @@ internal class IconImage : ImageData }; [StructLayout(LayoutKind.Sequential)] - internal class IconDump : ImageData + internal sealed class IconDump : ImageData { internal byte[]? data; }; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs index 35dd66a8f282b..6bd26a336c1bf 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Icon.Windows.cs @@ -951,13 +951,13 @@ int SaveAsFile([In, MarshalAs(UnmanagedType.Interface)] Interop.Ole32.IStream ps void SetHdc([In] IntPtr hdc); } - internal class Ole + internal static class Ole { public const int PICTYPE_ICON = 3; } [StructLayout(LayoutKind.Sequential)] - internal class PICTDESC + internal sealed class PICTDESC { internal int cbSizeOfStruct; public int picType; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Unix.cs index 0385b18eb5ece..45504c00830ef 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Unix.cs @@ -98,35 +98,6 @@ public RectangleF GetBounds(ref GraphicsUnit pageUnit) return source; } - public PropertyItem GetPropertyItem(int propid) - { - int propSize; - IntPtr property; - PropertyItem item = new PropertyItem(); - GdipPropertyItem gdipProperty = default; - int status; - - status = Gdip.GdipGetPropertyItemSize(nativeImage, propid, - out propSize); - Gdip.CheckStatus(status); - - /* Get PropertyItem */ - property = Marshal.AllocHGlobal(propSize); - try - { - status = Gdip.GdipGetPropertyItem(nativeImage, propid, propSize, property); - Gdip.CheckStatus(status); - gdipProperty = (GdipPropertyItem)Marshal.PtrToStructure(property, - typeof(GdipPropertyItem))!; - GdipPropertyItem.MarshalTo(gdipProperty, item); - } - finally - { - Marshal.FreeHGlobal(property); - } - return item; - } - public Image GetThumbnailImage(int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort? callback, IntPtr callbackData) { if ((thumbWidth <= 0) || (thumbHeight <= 0)) @@ -285,37 +256,6 @@ public void SaveAdd(Image image, EncoderParameters encoderParams) Gdip.CheckStatus(st); } - public void SetPropertyItem(PropertyItem propitem) - { - if (propitem == null) - throw new ArgumentNullException(nameof(propitem)); - - int nItemSize = Marshal.SizeOf(propitem.Value![0]); - int size = nItemSize * propitem.Value.Length; - IntPtr dest = Marshal.AllocHGlobal(size); - try - { - GdipPropertyItem pi = default; - pi.id = propitem.Id; - pi.len = propitem.Len; - pi.type = propitem.Type; - - Marshal.Copy(propitem.Value, 0, dest, size); - pi.value = dest; - - unsafe - { - int status = Gdip.GdipSetPropertyItem(nativeImage, &pi); - - Gdip.CheckStatus(status); - } - } - finally - { - Marshal.FreeHGlobal(dest); - } - } - [Browsable(false)] public ColorPalette Palette { @@ -375,72 +315,6 @@ internal void storeGDIPalette(ColorPalette palette) } } - [Browsable(false)] - public int[] PropertyIdList - { - get - { - uint propNumbers; - - int status = Gdip.GdipGetPropertyCount(nativeImage, - out propNumbers); - Gdip.CheckStatus(status); - - int[] idList = new int[propNumbers]; - status = Gdip.GdipGetPropertyIdList(nativeImage, - propNumbers, idList); - Gdip.CheckStatus(status); - - return idList; - } - } - - [Browsable(false)] - public PropertyItem[] PropertyItems - { - get - { - int propNums, propsSize, propSize; - IntPtr properties, propPtr; - PropertyItem[] items; - GdipPropertyItem gdipProperty = default; - int status; - - status = Gdip.GdipGetPropertySize(nativeImage, out propsSize, out propNums); - Gdip.CheckStatus(status); - - items = new PropertyItem[propNums]; - - if (propNums == 0) - return items; - - /* Get PropertyItem list*/ - properties = Marshal.AllocHGlobal(propsSize * propNums); - try - { - status = Gdip.GdipGetAllPropertyItems(nativeImage, propsSize, - propNums, properties); - Gdip.CheckStatus(status); - - propSize = Marshal.SizeOf(gdipProperty); - propPtr = properties; - - for (int i = 0; i < propNums; i++, propPtr = new IntPtr(propPtr.ToInt64() + propSize)) - { - gdipProperty = (GdipPropertyItem)Marshal.PtrToStructure - (propPtr, typeof(GdipPropertyItem))!; - items[i] = new PropertyItem(); - GdipPropertyItem.MarshalTo(gdipProperty, items[i]); - } - } - finally - { - Marshal.FreeHGlobal(properties); - } - return items; - } - } - protected virtual void Dispose(bool disposing) { if (nativeImage != IntPtr.Zero) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs index 545e85f9f649b..a6dd53744a974 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.Windows.cs @@ -390,91 +390,6 @@ public Image GetThumbnailImage(int thumbWidth, int thumbHeight, GetThumbnailImag return CreateImageObject(thumbImage); } - /// - /// Gets an array of the property IDs stored in this . - /// - [Browsable(false)] - public int[] PropertyIdList - { - get - { - Gdip.CheckStatus(Gdip.GdipGetPropertyCount(new HandleRef(this, nativeImage), out int count)); - - int[] propid = new int[count]; - - //if we have a 0 count, just return our empty array - if (count == 0) - return propid; - - Gdip.CheckStatus(Gdip.GdipGetPropertyIdList(new HandleRef(this, nativeImage), count, propid)); - - return propid; - } - } - - /// - /// Gets the specified property item from this . - /// - public PropertyItem? GetPropertyItem(int propid) - { - Gdip.CheckStatus(Gdip.GdipGetPropertyItemSize(new HandleRef(this, nativeImage), propid, out int size)); - - if (size == 0) - return null; - - IntPtr propdata = Marshal.AllocHGlobal(size); - - try - { - Gdip.CheckStatus(Gdip.GdipGetPropertyItem(new HandleRef(this, nativeImage), propid, size, propdata)); - return PropertyItemInternal.ConvertFromMemory(propdata, 1)[0]; - } - finally - { - Marshal.FreeHGlobal(propdata); - } - } - - /// - /// Sets the specified property item to the specified value. - /// - public void SetPropertyItem(PropertyItem propitem) - { - PropertyItemInternal propItemInternal = PropertyItemInternal.ConvertFromPropertyItem(propitem); - - using (propItemInternal) - { - Gdip.CheckStatus(Gdip.GdipSetPropertyItem(new HandleRef(this, nativeImage), propItemInternal)); - } - } - - /// - /// Gets an array of objects that describe this . - /// - [Browsable(false)] - public PropertyItem[] PropertyItems - { - get - { - Gdip.CheckStatus(Gdip.GdipGetPropertyCount(new HandleRef(this, nativeImage), out int count)); - Gdip.CheckStatus(Gdip.GdipGetPropertySize(new HandleRef(this, nativeImage), out int size, ref count)); - - if (size == 0 || count == 0) - return Array.Empty(); - - IntPtr propdata = Marshal.AllocHGlobal(size); - try - { - Gdip.CheckStatus(Gdip.GdipGetAllPropertyItems(new HandleRef(this, nativeImage), size, count, propdata)); - return PropertyItemInternal.ConvertFromMemory(propdata, count); - } - finally - { - Marshal.FreeHGlobal(propdata); - } - } - } - internal static void ValidateImage(IntPtr image) { try diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.cs index 4321b50a8140e..24977ec94a51f 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Image.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Image.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.ComponentModel; using System.Diagnostics; using System.Drawing.Imaging; @@ -298,6 +299,65 @@ public PixelFormat PixelFormat } } + /// + /// Gets an array of the property IDs stored in this . + /// + [Browsable(false)] + public unsafe int[] PropertyIdList + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPropertyCount(new HandleRef(this, nativeImage), out uint count)); + if (count == 0) + return Array.Empty(); + + var propid = new int[count]; + fixed (int* pPropid = propid) + { + Gdip.CheckStatus(Gdip.GdipGetPropertyIdList(new HandleRef(this, nativeImage), count, pPropid)); + } + + return propid; + } + } + + /// + /// Gets an array of objects that describe this . + /// + [Browsable(false)] + public unsafe PropertyItem[] PropertyItems + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPropertySize(new HandleRef(this, nativeImage), out uint size, out uint count)); + + if (size == 0 || count == 0) + return Array.Empty(); + + var result = new PropertyItem[(int)count]; + byte[] buffer = ArrayPool.Shared.Rent((int)size); + fixed (byte *pBuffer = buffer) + { + PropertyItemInternal* pPropData = (PropertyItemInternal*)pBuffer; + Gdip.CheckStatus(Gdip.GdipGetAllPropertyItems(new HandleRef(this, nativeImage), size, count, pPropData)); + + for (int i = 0; i < count; i++) + { + result[i] = new PropertyItem + { + Id = pPropData[i].id, + Len = pPropData[i].len, + Type = pPropData[i].type, + Value = pPropData[i].Value.ToArray() + }; + } + } + + ArrayPool.Shared.Return(buffer); + return result; + } + } + /// /// Returns the number of frames of the given dimension. /// @@ -308,6 +368,36 @@ public int GetFrameCount(FrameDimension dimension) return count; } + /// + /// Gets the specified property item from this . + /// + public unsafe PropertyItem? GetPropertyItem(int propid) + { + Gdip.CheckStatus(Gdip.GdipGetPropertyItemSize(new HandleRef(this, nativeImage), propid, out uint size)); + + if (size == 0) + return null; + + PropertyItem result; + byte[] buffer = ArrayPool.Shared.Rent((int)size); + fixed (byte *pBuffer = buffer) + { + PropertyItemInternal* pPropData = (PropertyItemInternal*)pBuffer; + Gdip.CheckStatus(Gdip.GdipGetPropertyItem(new HandleRef(this, nativeImage), propid, size, pPropData)); + + result = new PropertyItem + { + Id = pPropData->id, + Len = pPropData->len, + Type = pPropData->type, + Value = pPropData->Value.ToArray() + }; + } + + ArrayPool.Shared.Return(buffer); + return result; + } + /// /// Selects the frame specified by the given dimension and index. /// @@ -318,6 +408,24 @@ public int SelectActiveFrame(FrameDimension dimension, int frameIndex) return 0; } + /// + /// Sets the specified property item to the specified value. + /// + public unsafe void SetPropertyItem(PropertyItem propitem) + { + fixed (byte *propItemValue = propitem.Value) + { + var propItemInternal = new PropertyItemInternal + { + id = propitem.Id, + len = propitem.Len, + type = propitem.Type, + value = propItemValue + }; + Gdip.CheckStatus(Gdip.GdipSetPropertyItem(new HandleRef(this, nativeImage), &propItemInternal)); + } + } + public void RotateFlip(RotateFlipType rotateFlipType) { int status = Gdip.GdipImageRotateFlip(new HandleRef(this, nativeImage), unchecked((int)rotateFlipType)); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.Unix.cs index 807dcc81fe08f..a8a03c47fa444 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.Unix.cs @@ -40,7 +40,7 @@ namespace System.Drawing { - internal class AnimateEventArgs : EventArgs + internal sealed class AnimateEventArgs : EventArgs { private int frameCount; @@ -88,7 +88,7 @@ public static void Animate(Image image, EventHandler onFrameChangedHandler) if (ht.ContainsKey(image)) return; - PropertyItem item = image.GetPropertyItem(0x5100); // FrameDelay in libgdiplus + PropertyItem item = image.GetPropertyItem(0x5100)!; // FrameDelay in libgdiplus byte[] value = item.Value!; int[] delay = new int[(value.Length >> 2)]; for (int i = 0, n = 0; i < value.Length; i += 4, n++) @@ -165,7 +165,7 @@ private static void UpdateImageFrame(Image image) } } - internal class WorkerThread + internal sealed class WorkerThread { private EventHandler frameChangeHandler; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs index 710ce1d4381b5..9836fbfc9a38d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageConverter.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.ComponentModel; using System.Diagnostics; using System.Drawing.Imaging; @@ -112,20 +113,38 @@ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContex { try { - short signature = MemoryMarshal.Read(rawData); + short signature = BinaryPrimitives.ReadInt16LittleEndian(rawData); if (signature != 0x1c15) { return null; } - // The data is in the form of OBJECTHEADER. It's an encoded format that Access uses to push imagesinto the DB. - OBJECTHEADER pHeader = MemoryMarshal.Read(rawData); + // The data is in the form of OBJECTHEADER. It's an encoded format that Access uses to push images into the DB. + // + // The layout of OBJECTHEADER is as follows - we only need the signature + // and headersize fields, which need to be read as little-endian data: + // + // [StructLayout(LayoutKind.Sequential)] + // private struct OBJECTHEADER + // { + // public short signature; // it's always 0x1c15 + // public short headersize; + // public short objectType; + // public short nameLen; + // public short classLen; + // public short nameOffset; + // public short classOffset; + // public short width; + // public short height; + // public IntPtr pInfo; + // } + short headersize = BinaryPrimitives.ReadInt16LittleEndian(rawData.Slice(2, 2)); // pHeader.signature will always be 0x1c15. // "PBrush" should be the 6 chars after position 12 as well. - if (rawData.Length <= pHeader.headersize + 18 || - !rawData.Slice(pHeader.headersize + 12, 6).SequenceEqual(PBrush)) + if (rawData.Length <= headersize + 18 || + !rawData.Slice(headersize + 12, 6).SequenceEqual(PBrush)) { return null; } @@ -143,20 +162,5 @@ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContex return null; } - - [StructLayout(LayoutKind.Sequential)] - private struct OBJECTHEADER - { - public short signature; // it's always 0x1c15 - public short headersize; - public short objectType; - public short nameLen; - public short classLen; - public short nameOffset; - public short classOffset; - public short width; - public short height; - public IntPtr pInfo; - } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageInfo.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageInfo.cs index 7bfadb150ffbe..d9e299ee16dfe 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageInfo.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageInfo.cs @@ -15,7 +15,7 @@ public sealed partial class ImageAnimator /// /// ImageAnimator nested helper class used to store extra image state info. /// - private class ImageInfo + private sealed class ImageInfo { private const int PropertyTagFrameDelay = 0x5100; @@ -185,7 +185,7 @@ internal void UpdateFrame() /// /// Raises the FrameChanged event. /// - protected void OnFrameChanged(EventArgs e) + private void OnFrameChanged(EventArgs e) { if (_onFrameChangedHandler != null) { diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs index fd67c140ebe23..d47f98c9f1523 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs @@ -7,7 +7,7 @@ namespace System.Drawing.Imaging // sdkinc\imaging.h [StructLayout(LayoutKind.Sequential, Pack = 8)] - internal class ImageCodecInfoPrivate + internal sealed class ImageCodecInfoPrivate { #pragma warning disable CS0618 // Legacy code: We don't care about using obsolete API's. [MarshalAs(UnmanagedType.Struct)] diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs index c03dfc4c55784..eb2c7f73a9533 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs @@ -6,7 +6,7 @@ namespace System.Drawing.Imaging using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] - internal class MetafileHeaderEmf + internal sealed class MetafileHeaderEmf { /// The ENHMETAHEADER structure is defined natively as a union with WmfHeader. /// Extreme care should be taken if changing the layout of the corresponding managed diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs index b5d88328ec9d9..d42e1ceaea7b2 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs @@ -6,7 +6,7 @@ namespace System.Drawing.Imaging using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 8)] - internal class MetafileHeaderWmf + internal sealed class MetafileHeaderWmf { /// The ENHMETAHEADER structure is defined natively as a union with WmfHeader. /// Extreme care should be taken if changing the layout of the corresponding managed diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs index 4f28e7a933247..cc7974280e30a 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs @@ -9,11 +9,6 @@ namespace System.Drawing.Imaging /// public sealed class PropertyItem { - private int _id; - private int _len; - private short _type; - private byte[]? _value; - internal PropertyItem() { } @@ -21,34 +16,21 @@ internal PropertyItem() /// /// Represents the ID of the property. /// - public int Id - { - get { return _id; } - set { _id = value; } - } + public int Id { get; set; } + /// /// Represents the length of the property. /// - public int Len - { - get { return _len; } - set { _len = value; } - } + public int Len { get; set; } + /// /// Represents the type of the property. /// - public short Type - { - get { return _type; } - set { _type = value; } - } + public short Type { get; set; } + /// /// Contains the property value. /// - public byte[]? Value - { - get { return _value; } - set { _value = value; } - } + public byte[]? Value { get; set; } } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs index 0d3ae0aa061a8..daafc9a7f6806 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/LibX11Functions.cs @@ -46,4 +46,19 @@ internal static class LibX11Functions [DllImport("libX11", EntryPoint = "XFree")] internal static extern void XFree(IntPtr data); } + + [StructLayout(LayoutKind.Sequential)] + internal struct XVisualInfo + { + internal IntPtr visual; + internal IntPtr visualid; + internal int screen; + internal uint depth; + internal int klass; + internal IntPtr red_mask; + internal IntPtr green_mask; + internal IntPtr blue_mask; + internal int colormap_size; + internal int bits_per_rgb; + } } diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/NativeMethods.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/NativeMethods.cs index 9d08ae770b0d4..4762777ab19fa 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/NativeMethods.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/NativeMethods.cs @@ -6,7 +6,7 @@ namespace System.Drawing { - internal class NativeMethods + internal static class NativeMethods { internal static HandleRef NullHandleRef => new HandleRef(null, IntPtr.Zero); diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/NativeStructs.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/NativeStructs.Unix.cs deleted file mode 100644 index 0b1822199e0ee..0000000000000 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/NativeStructs.Unix.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// -// System.Drawing.NativeStructs.cs -// -// Author: -// Alexandre Pigolkine (pigolkine@gmx.de) -// Jordi Mas (jordi@ximian.com) -// -// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System.Drawing.Imaging; -using System.Runtime.InteropServices; - -namespace System.Drawing -{ - [StructLayout(LayoutKind.Sequential)] - internal struct GdipImageCodecInfo - { - internal Guid Clsid; - internal Guid FormatID; - internal IntPtr CodecName; - internal IntPtr DllName; - internal IntPtr FormatDescription; - internal IntPtr FilenameExtension; - internal IntPtr MimeType; - internal ImageCodecFlags Flags; - internal int Version; - internal int SigCount; - internal int SigSize; - private IntPtr SigPattern; - private IntPtr SigMask; - - internal static void MarshalTo(GdipImageCodecInfo gdipcodec, ImageCodecInfo codec) - { - codec.CodecName = Marshal.PtrToStringUni(gdipcodec.CodecName); - codec.DllName = Marshal.PtrToStringUni(gdipcodec.DllName); - codec.FormatDescription = Marshal.PtrToStringUni(gdipcodec.FormatDescription); - codec.FilenameExtension = Marshal.PtrToStringUni(gdipcodec.FilenameExtension); - codec.MimeType = Marshal.PtrToStringUni(gdipcodec.MimeType); - codec.Clsid = gdipcodec.Clsid; - codec.FormatID = gdipcodec.FormatID; - codec.Flags = gdipcodec.Flags; - codec.Version = gdipcodec.Version; - codec.SignatureMasks = new byte[gdipcodec.SigCount][]; - codec.SignaturePatterns = new byte[gdipcodec.SigCount][]; - IntPtr p = gdipcodec.SigPattern; - IntPtr m = gdipcodec.SigMask; - for (int i = 0; i < gdipcodec.SigCount; i++) - { - codec.SignatureMasks[i] = new byte[gdipcodec.SigSize]; - Marshal.Copy(m, codec.SignatureMasks[i], 0, gdipcodec.SigSize); - m = new IntPtr(m.ToInt64() + gdipcodec.SigSize); - codec.SignaturePatterns[i] = new byte[gdipcodec.SigSize]; - Marshal.Copy(p, codec.SignaturePatterns[i], 0, gdipcodec.SigSize); - p = new IntPtr(p.ToInt64() + gdipcodec.SigSize); - } - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct GdipPropertyItem - { - internal int id; - internal int len; - internal short type; - internal IntPtr value; - - internal static void MarshalTo(GdipPropertyItem gdipProp, PropertyItem prop) - { - prop.Id = gdipProp.id; - prop.Len = gdipProp.len; - prop.Type = gdipProp.type; - prop.Value = new byte[gdipProp.len]; - Marshal.Copy(gdipProp.value, prop.Value, 0, gdipProp.len); - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct IconInfo - { - private int fIcon; - public int xHotspot; - public int yHotspot; - public IntPtr hbmMask; - public IntPtr hbmColor; - - public bool IsIcon - { - get { return (fIcon == 1); } - set { fIcon = (value) ? 1 : 0; } - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct XVisualInfo - { - internal IntPtr visual; - internal IntPtr visualid; - internal int screen; - internal uint depth; - internal int klass; - internal IntPtr red_mask; - internal IntPtr green_mask; - internal IntPtr blue_mask; - internal int colormap_size; - internal int bits_per_rgb; - } -} diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs index d74432e34bffc..f4907591b8d34 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs @@ -10,7 +10,7 @@ namespace System.Drawing /// /// Retrieves the printer graphics during preview. /// - internal class PrintPreviewGraphics + internal sealed class PrintPreviewGraphics { private readonly PrintPageEventArgs _printPageEventArgs; private readonly PrintDocument _printDocument; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs index 853639a6e5430..1889516f87ad4 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.Windows.cs @@ -572,7 +572,7 @@ internal DeviceContext CreateDeviceContext(PageSettings pageSettings) internal DeviceContext CreateDeviceContext(IntPtr hdevmode) { IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode); - DeviceContext dc = DeviceContext.CreateDC(DriverName, PrinterNameInternal, fileName:null, modePointer); ; + DeviceContext dc = DeviceContext.CreateDC(DriverName, PrinterNameInternal, fileName:null, modePointer); Interop.Kernel32.GlobalUnlock(hdevmode); return dc; } @@ -1650,7 +1650,7 @@ public int Add(string value) } } - private class ArrayEnumerator : IEnumerator + private sealed class ArrayEnumerator : IEnumerator { private readonly object[] _array; private readonly int _endIndex; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintingServices.Unix.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintingServices.Unix.cs index 613f1ce2e1aa0..3324fa97cd1ef 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintingServices.Unix.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Printing/PrintingServices.Unix.cs @@ -1021,14 +1021,14 @@ internal static void LoadDefaultResolutions(PrinterSettings.PrinterResolutionCol } } - internal class SysPrn + internal static class SysPrn { internal static void GetPrintDialogInfo(string printer, ref string port, ref string type, ref string status, ref string comment) { PrintingServices.GetPrintDialogInfo(printer, ref port, ref type, ref status, ref comment); } - internal class Printer + internal sealed class Printer { public readonly string Comment; public readonly string Port; @@ -1046,7 +1046,7 @@ public Printer(string port, string type, string status, string comment) } } - internal class GraphicsPrinter + internal sealed class GraphicsPrinter { private Graphics? graphics; private IntPtr hDC; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs index c811ea17672ee..e64c042c6d90e 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs @@ -1,119 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.InteropServices; + namespace System.Drawing.Imaging { - using System.Runtime.InteropServices; - - // sdkinc\imaging.h - [StructLayout(LayoutKind.Sequential)] - internal sealed class PropertyItemInternal : IDisposable + internal unsafe struct PropertyItemInternal { public int id; public int len; public short type; - public IntPtr value = IntPtr.Zero; - - internal PropertyItemInternal() - { - } - - ~PropertyItemInternal() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - } - - private void Dispose(bool disposing) - { - if (value != IntPtr.Zero) - { - Marshal.FreeHGlobal(value); - value = IntPtr.Zero; - } - - if (disposing) - { - GC.SuppressFinalize(this); - } - } - - internal static PropertyItemInternal ConvertFromPropertyItem(PropertyItem propItem) - { - PropertyItemInternal propItemInternal = new PropertyItemInternal(); - propItemInternal.id = propItem.Id; - propItemInternal.len = 0; - propItemInternal.type = propItem.Type; - - byte[]? propItemValue = propItem.Value; - if (propItemValue != null) - { - int length = propItemValue.Length; - propItemInternal.len = length; - propItemInternal.value = Marshal.AllocHGlobal(length); - Marshal.Copy(propItemValue, 0, propItemInternal.value, length); - } - - return propItemInternal; - } - - internal static PropertyItem[] ConvertFromMemory(IntPtr propdata, int count) - { - PropertyItem[] props = new PropertyItem[count]; - - for (int i = 0; i < count; i++) - { - PropertyItemInternal? propcopy = null; - try - { - propcopy = (PropertyItemInternal)Marshal.PtrToStructure(propdata, - typeof(PropertyItemInternal))!; - - props[i] = new PropertyItem(); - props[i].Id = propcopy.id; - props[i].Len = propcopy.len; - props[i].Type = propcopy.type; - - // this calls Marshal.Copy and creates a copy of the original memory into a byte array. - props[i].Value = propcopy.Value; - - propcopy.value = IntPtr.Zero; // we dont actually own this memory so dont free it. - } - finally - { - if (propcopy != null) - { - propcopy.Dispose(); - } - } - - propdata = (IntPtr)((long)propdata + (int)Marshal.SizeOf(typeof(PropertyItemInternal))); - } - - return props; - } - - public byte[]? Value - { - get - { - if (len == 0) - { - return null; - } - - byte[] bytes = new byte[len]; + public byte* value; - Marshal.Copy(value, - bytes, - 0, - (int)len); - return bytes; - } - } + public Span Value => new Span(value, len); } } diff --git a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs index 5ca8b91b9c5c8..b64f7c40b0f37 100644 --- a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs +++ b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs @@ -342,7 +342,7 @@ public override bool Equals(object? obj) /// public override int GetHashCode() => _hDC.GetHashCode(); - internal class GraphicsState + internal sealed class GraphicsState { internal IntPtr hBrush; internal IntPtr hFont; diff --git a/src/libraries/System.Drawing.Common/tests/ImageTests.cs b/src/libraries/System.Drawing.Common/tests/ImageTests.cs index 04a361394609b..08cce1156a2e8 100644 --- a/src/libraries/System.Drawing.Common/tests/ImageTests.cs +++ b/src/libraries/System.Drawing.Common/tests/ImageTests.cs @@ -5,13 +5,498 @@ using System.Drawing.Imaging; using System.IO; using System.Linq; +using System.Runtime.InteropServices; +using System.Text; using Microsoft.DotNet.XUnitExtensions; using Xunit; +using Encoder = System.Drawing.Imaging.Encoder; namespace System.Drawing.Tests { public class ImageTests { + private const int PropertyTagLuminanceTable = 0x5090; + private const int PropertyTagChrominanceTable = 0x5091; + private const int PropertyTagExifUserComment = 0x9286; + private const int PropertyTagTypeASCII = 2; + private const int PropertyTagTypeShort = 3; + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PropertyIdList_GetBitmapJpg_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + Assert.NotSame(bitmap.PropertyIdList, bitmap.PropertyIdList); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Returns new int[0] in .NET Framework.")] + public void PropertyIdList_GetEmptyMemoryBitmap_ReturnsExpected() + { + using var bitmap = new Bitmap(1, 1); + Assert.Empty(bitmap.PropertyIdList); + Assert.Same(bitmap.PropertyIdList, bitmap.PropertyIdList); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PropertyItems_GetBitmapJpg_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Equal(3, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(29, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("LEAD Technologies Inc. V1.01\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + + Assert.NotSame(items, bitmap.PropertyItems); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Returns new PropertyItem[0] in .NET Framework.")] + public void PropertyItems_GetEmptyBitmapBmp_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + Assert.Empty(bitmap.PropertyItems); + Assert.Same(bitmap.PropertyItems, bitmap.PropertyItems); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Returns new PropertyItem[0] in .NET Framework.")] + public void PropertyItems_GetEmptyMemoryBitmap_ReturnsExpected() + { + using var bitmap = new Bitmap(1, 1); + Assert.Empty(bitmap.PropertyItems); + Assert.Same(bitmap.PropertyItems, bitmap.PropertyItems); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetPropertyItem_InvokeExistsBitmapBmp_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagExifUserComment); + Assert.Equal(PropertyTagExifUserComment, item.Id); + Assert.Equal(29, item.Len); + Assert.Equal(PropertyTagTypeASCII, item.Type); + Assert.Equal("LEAD Technologies Inc. V1.01\0", Encoding.ASCII.GetString(item.Value)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void GetPropertyItem_NoSuchPropertyItemEmptyMemoryBitmap_ThrowsArgumentException(int propid) + { + using var bitmap = new Bitmap(1, 1); + Assert.Throws(null, () => bitmap.GetPropertyItem(propid)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void GetPropertyItem_NoSuchPropertyItemEmptyImageBitmapBmp_ThrowsArgumentException(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + Assert.Throws(null, () => bitmap.GetPropertyItem(propid)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RemovePropertyItem_InvokeMemoryBitmap_Success() + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item1 = source.GetPropertyItem(PropertyTagExifUserComment); + PropertyItem item2 = source.GetPropertyItem(PropertyTagChrominanceTable); + PropertyItem item3 = source.GetPropertyItem(PropertyTagLuminanceTable); + using var bitmap = new Bitmap(1, 1); + bitmap.SetPropertyItem(item1); + bitmap.SetPropertyItem(item2); + bitmap.SetPropertyItem(item3); + + bitmap.RemovePropertyItem(PropertyTagExifUserComment); + Assert.Equal(new int[] { PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + Assert.Throws(null, () => bitmap.GetPropertyItem(PropertyTagExifUserComment)); + Assert.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagExifUserComment)); + + bitmap.RemovePropertyItem(PropertyTagLuminanceTable); + Assert.Equal(new int[] { PropertyTagChrominanceTable }, bitmap.PropertyIdList); + Assert.Throws(null, () => bitmap.GetPropertyItem(PropertyTagLuminanceTable)); + Assert.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagLuminanceTable)); + + bitmap.RemovePropertyItem(PropertyTagChrominanceTable); + Assert.Empty(bitmap.PropertyIdList); + Assert.Throws(null, () => bitmap.GetPropertyItem(PropertyTagChrominanceTable)); + Assert.Throws(() => bitmap.RemovePropertyItem(PropertyTagChrominanceTable)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RemovePropertyItem_InvokeBitmapJpg_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + bitmap.RemovePropertyItem(PropertyTagExifUserComment); + Assert.Equal(new int[] { PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + Assert.Throws(null, () => bitmap.GetPropertyItem(PropertyTagExifUserComment)); + Assert.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagExifUserComment)); + + bitmap.RemovePropertyItem(PropertyTagLuminanceTable); + Assert.Equal(new int[] { PropertyTagChrominanceTable }, bitmap.PropertyIdList); + Assert.Throws(null, () => bitmap.GetPropertyItem(PropertyTagLuminanceTable)); + Assert.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagLuminanceTable)); + + bitmap.RemovePropertyItem(PropertyTagChrominanceTable); + Assert.Empty(bitmap.PropertyIdList); + Assert.Throws(null, () => bitmap.GetPropertyItem(PropertyTagChrominanceTable)); + Assert.Throws(() => bitmap.RemovePropertyItem(PropertyTagChrominanceTable)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyItemEmptyMemoryBitmap_ThrowsExternalException(int propid) + { + using var bitmap = new Bitmap(1, 1); + Assert.Throws(() => bitmap.RemovePropertyItem(propid)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyItemEmptyImageBitmapBmp_ThrowsExternalException(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + Assert.Throws(() => bitmap.RemovePropertyItem(propid)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyNotEmptyMemoryBitmap_ThrowsArgumentException(int propid) + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item1 = source.GetPropertyItem(PropertyTagExifUserComment); + PropertyItem item2 = source.GetPropertyItem(PropertyTagChrominanceTable); + PropertyItem item3 = source.GetPropertyItem(PropertyTagLuminanceTable); + using var bitmap = new Bitmap(1, 1); + bitmap.SetPropertyItem(item1); + bitmap.SetPropertyItem(item2); + bitmap.SetPropertyItem(item3); + + Assert.Throws(null, () => bitmap.RemovePropertyItem(propid)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyNotEmptyBitmapJpg_ThrowsArgumentException(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + Assert.Throws(null, () => bitmap.RemovePropertyItem(propid)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void SetPropertyItem_InvokeMemoryBitmap_Success(int propid) + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + using var bitmap = new Bitmap(1, 1); + + // Change data. + PropertyItem item = source.GetPropertyItem(PropertyTagExifUserComment); + item.Value = Encoding.ASCII.GetBytes("Hello World\0"); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment }, bitmap.PropertyIdList); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Single(items); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + + // New data. + item.Id = propid; + item.Value = Encoding.ASCII.GetBytes("New Value\0"); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + + // Set same. + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void SetPropertyItem_InvokeBitmapJpg_Success(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + + // Change data. + PropertyItem item = bitmap.GetPropertyItem(PropertyTagExifUserComment); + item.Value = Encoding.ASCII.GetBytes("Hello World\0"); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Equal(3, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + + // New data. + item.Id = propid; + item.Value = Encoding.ASCII.GetBytes("New Value\0"); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(4, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + Assert.Equal(propid, items[3].Id); + Assert.Equal(10, items[3].Len); + Assert.Equal(PropertyTagTypeASCII, items[3].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[3].Value)); + + // Set same. + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(4, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + Assert.Equal(propid, items[3].Id); + Assert.Equal(10, items[3].Len); + Assert.Equal(PropertyTagTypeASCII, items[3].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[3].Value)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void SetPropertyItem_InvokeBitmapBmp_Success(int propid) + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + + // Change data. + PropertyItem item = source.GetPropertyItem(PropertyTagExifUserComment); + item.Value = Encoding.ASCII.GetBytes("Hello World\0"); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment }, bitmap.PropertyIdList); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Single(items); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + + // New data. + item.Id = propid; + item.Value = Encoding.ASCII.GetBytes("New Value\0"); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + + // Set same. + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + } + public static IEnumerable InvalidBytes_TestData() { // IconTests.Ctor_InvalidBytesInStream_TestData an array of 2 objects, but this test only uses the diff --git a/src/libraries/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs b/src/libraries/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs index 185a26be954f0..5dccc82bdfb70 100644 --- a/src/libraries/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs +++ b/src/libraries/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs @@ -1,27 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// -// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// using System.Collections.Generic; using Xunit; @@ -30,7 +8,64 @@ namespace System.Drawing.Imaging.Tests { public class PropertyItemTests { - private const string PngFile = "16x16_nonindexed_24bit.png"; + private const int PropertyTagLuminanceTable = 0x5090; + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + public void Id_Set_GetReturnsExpected(int value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Id = value; + Assert.Equal(value, item.Id); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + public void Len_Set_GetReturnsExpected(int value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Len = value; + Assert.Equal(value, item.Len); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + public void Type_Set_GetReturnsExpected(short value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Type = value; + Assert.Equal(value, item.Type); + } + + public static IEnumerable Value_Set_TestData() + { + yield return new object[] { null }; + yield return new object[] { Array.Empty() }; + yield return new object[] { new byte[] { 1, 2, 3 } }; + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Value_Set_TestData))] + public void Value_Set_GetReturnsExpected(byte[] value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Value = value; + Assert.Same(value, item.Value); + } public static IEnumerable Properties_TestData() { @@ -40,31 +75,29 @@ public static IEnumerable Properties_TestData() } [ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34592", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [ConditionalTheory(Helpers.IsDrawingSupported)] [MemberData(nameof(Properties_TestData))] public void Properties_SetValues_ReturnsExpected(int id, int len, short type, byte[] value) { - using (Image image = new Bitmap(Helpers.GetTestBitmapPath(PngFile))) - using (Image clone = (Image)image.Clone()) - { - PropertyItem[] propItems = clone.PropertyItems; - PropertyItem propItem = propItems[0]; - Assert.Equal(771, propItem.Id); - Assert.Equal(1, propItem.Len); - Assert.Equal(1, propItem.Type); - Assert.Equal(new byte[1] { 0 }, propItem.Value); + using var image = new Bitmap(Helpers.GetTestBitmapPath("16x16_nonindexed_24bit.png")); + using Image clone = (Image)image.Clone(); + + PropertyItem[] propItems = clone.PropertyItems; + PropertyItem propItem = propItems[0]; + Assert.Equal(771, propItem.Id); + Assert.Equal(1, propItem.Len); + Assert.Equal(1, propItem.Type); + Assert.Equal(new byte[1] { 0 }, propItem.Value); - propItem.Id = id; - propItem.Len = len; - propItem.Type = type; - propItem.Value = value; + propItem.Id = id; + propItem.Len = len; + propItem.Type = type; + propItem.Value = value; - Assert.Equal(id, propItem.Id); - Assert.Equal(len, propItem.Len); - Assert.Equal(type, propItem.Type); - Assert.Equal(value, propItem.Value); - } + Assert.Equal(id, propItem.Id); + Assert.Equal(len, propItem.Len); + Assert.Equal(type, propItem.Type); + Assert.Equal(value, propItem.Value); } } } diff --git a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs index 60a30a32aa130..43eae0f7320db 100644 --- a/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs +++ b/src/libraries/System.Drawing.Primitives/src/System/Drawing/Color.cs @@ -487,12 +487,34 @@ private void GetRgbValues(out int r, out int g, out int b) b = (int)(value & ARGBBlueMask) >> ARGBBlueShift; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void MinMaxRgb(out int min, out int max, int r, int g, int b) + { + if (r > g) + { + max = r; + min = g; + } + else + { + max = g; + min = r; + } + if (b > max) + { + max = b; + } + else if (b < min) + { + min = b; + } + } + public float GetBrightness() { GetRgbValues(out int r, out int g, out int b); - int min = Math.Min(Math.Min(r, g), b); - int max = Math.Max(Math.Max(r, g), b); + MinMaxRgb(out int min, out int max, r, g, b); return (max + min) / (byte.MaxValue * 2f); } @@ -504,8 +526,7 @@ public float GetHue() if (r == g && g == b) return 0f; - int min = Math.Min(Math.Min(r, g), b); - int max = Math.Max(Math.Max(r, g), b); + MinMaxRgb(out int min, out int max, r, g, b); float delta = max - min; float hue; @@ -531,8 +552,7 @@ public float GetSaturation() if (r == g && g == b) return 0f; - int min = Math.Min(Math.Min(r, g), b); - int max = Math.Max(Math.Max(r, g), b); + MinMaxRgb(out int min, out int max, r, g, b); int div = max + min; if (div > byte.MaxValue) diff --git a/src/libraries/System.Drawing.Primitives/tests/ColorTests.cs b/src/libraries/System.Drawing.Primitives/tests/ColorTests.cs index e4e1df8366ce9..034780c521b65 100644 --- a/src/libraries/System.Drawing.Primitives/tests/ColorTests.cs +++ b/src/libraries/System.Drawing.Primitives/tests/ColorTests.cs @@ -390,6 +390,12 @@ private void CheckRed(Color color) [InlineData(51, 255, 51, 0.6f)] [InlineData(51, 51, 255, 0.6f)] [InlineData(51, 51, 51, 0.2f)] + [InlineData(0, 51, 255, 0.5f)] + [InlineData(51, 255, 0, 0.5f)] + [InlineData(0, 255, 51, 0.5f)] + [InlineData(255, 0, 51, 0.5f)] + [InlineData(51, 0, 255, 0.5f)] + [InlineData(255, 51, 0, 0.5f)] public void GetBrightness(int r, int g, int b, float expected) { Assert.Equal(expected, Color.FromArgb(r, g, b).GetBrightness()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs index d3eea752b95b6..9daae9ce25e82 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs @@ -302,13 +302,13 @@ public MyClass Method_ReturnMyClass(out int? p1, short? p2, out byte p3) { p1 = 2; p3 = 3; - return new MyClass { Field = (int)p2 }; ; + return new MyClass { Field = (int)p2 }; } public MyClass Method_ReturnMyClass(out ulong l) { l = 3; - return new MyClass { Field = (int)l }; ; + return new MyClass { Field = (int)l }; } public MyClass Method_ReturnMyClass(params MyStruct[] arr) => new MyClass { Field = 1 }; diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs index 73983f274b73e..a345456c53b8f 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.DynamicType/Conformance.dynamic.dynamicType.conversions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions.common.common; +using System; using Xunit; namespace ManagedTests.DynamicCSharp.Conformance.dynamic.dynamicType.conversions.common.common @@ -396,6 +397,7 @@ private static bool NullableUlongTypeWithNullableNumbericConversionFromUintInAss #endregion [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.executeOrder.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.executeOrder.cs index fe261b82c3805..a9b0ebba2ebeb 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.executeOrder.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.NamedAndOptional/Conformance.dynamic.namedandoptional.usage.executeOrder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using Xunit; namespace ManagedTests.DynamicCSharp.Conformance.dynamic.namedandoptional.usage.executeOrder.nested01.nested01 @@ -1034,6 +1035,7 @@ public static int Bar3() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs index 0d9e8122cf68e..af1b08aa772d2 100644 --- a/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs +++ b/src/libraries/System.Dynamic.Runtime/tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Methods.1class2methods.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using Xunit; namespace ManagedTests.DynamicCSharp.Conformance.dynamic.overloadResolution.Methods.Oneclass2methods.diffnumberprms001.diffnumberprms001 @@ -2965,6 +2966,7 @@ public void Method(short? s) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); @@ -2996,6 +2998,7 @@ public void Method(short? s) } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void DynamicCSharpRunTest() { Assert.Equal(0, MainMethod()); diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs index 4e48b4bb26a73..9938ddad4ac2c 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnWriter.cs @@ -648,7 +648,7 @@ private static void CheckUniversalTag(Asn1Tag? tag, UniversalTagNumber universal } } - private class ArrayIndexSetOfValueComparer : IComparer<(int, int)> + private sealed class ArrayIndexSetOfValueComparer : IComparer<(int, int)> { private readonly byte[] _data; diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs index 93750c409a205..e85db7683c930 100644 --- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs +++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/SetOfValueComparer.cs @@ -5,7 +5,7 @@ namespace System.Formats.Asn1 { - internal class SetOfValueComparer : IComparer> + internal sealed class SetOfValueComparer : IComparer> { internal static SetOfValueComparer Instance { get; } = new SetOfValueComparer(); diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Map.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Map.cs index a649b9391151a..bcea2bd5c2dcb 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Map.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Reader/CborReader.Map.cs @@ -207,7 +207,7 @@ private void ReturnKeyEncodingRangeAllocation(HashSet<(int Offset, int Length)>? } // Comparing buffer slices up to their binary content - private class KeyEncodingComparer : IEqualityComparer<(int Offset, int Length)> + private sealed class KeyEncodingComparer : IEqualityComparer<(int Offset, int Length)> { private readonly CborReader _reader; diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Map.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Map.cs index 26fb509fd8d82..859461e2f3655 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Map.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Map.cs @@ -272,7 +272,7 @@ public KeyValuePairEncodingRange(int offset, int keyLength, int totalLength) } // Defines order and equality semantics for a key/value encoding range pair up to key encoding - private class KeyEncodingComparer : IComparer, + private sealed class KeyEncodingComparer : IComparer, IEqualityComparer<(int Offset, int Length)> { private readonly CborWriter _writer; diff --git a/src/libraries/System.Globalization.Calendars/tests/GregorianCalendar/GregorianCalendarGetWeekOfYear.cs b/src/libraries/System.Globalization.Calendars/tests/GregorianCalendar/GregorianCalendarGetWeekOfYear.cs index 8cc1605229856..6c6ee4f3128b4 100644 --- a/src/libraries/System.Globalization.Calendars/tests/GregorianCalendar/GregorianCalendarGetWeekOfYear.cs +++ b/src/libraries/System.Globalization.Calendars/tests/GregorianCalendar/GregorianCalendarGetWeekOfYear.cs @@ -104,7 +104,7 @@ private static int GetDayOfYearHelper(Calendar calendar, DateTime time, Calendar break; } - return (weekOfYear - 1) * DaysPerWeek + offset + 1;; + return (weekOfYear - 1) * DaysPerWeek + offset + 1; } } } diff --git a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs index 86b2c42591441..3960233e5eafa 100644 --- a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs +++ b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs @@ -177,7 +177,7 @@ public void RoundTrip_Chunks() for (int i = 0; i < totalSize; i += chunkSize) { byte[] uncompressed = new byte[chunkSize]; - new Random().NextBytes(uncompressed); + Random.Shared.NextBytes(uncompressed); byte[] compressed = new byte[BrotliEncoder.GetMaxCompressedLength(chunkSize)]; byte[] decompressed = new byte[chunkSize]; var uncompressedSpan = new ReadOnlySpan(uncompressed); diff --git a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs index 2cdf0224710dd..4feaee51111be 100644 --- a/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs +++ b/src/libraries/System.IO.Compression/tests/CompressionStreamUnitTests.Deflate.cs @@ -30,6 +30,7 @@ public static IEnumerable DecompressFailsWithWrapperStream_MemberData( /// Test to pass GZipStream data and ZLibStream data to a DeflateStream [Theory] [MemberData(nameof(DecompressFailsWithWrapperStream_MemberData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/36845", TestPlatforms.Android)] public async Task DecompressFailsWithWrapperStream(string uncompressedPath, string newDirectory, string newSuffix) { string fileName = Path.Combine(newDirectory, Path.GetFileName(uncompressedPath) + newSuffix); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj b/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj index 15e15be33edb3..6198325a2dfb7 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/System.IO.FileSystem.Watcher.Tests.csproj @@ -2,9 +2,6 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)-FreeBSD - - true diff --git a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln index 4c3dc58180ee8..93f9297296e4e 100644 --- a/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln +++ b/src/libraries/System.IO.FileSystem/System.IO.FileSystem.sln @@ -25,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{32A31E04-255 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D9FB1730-B750-4C0D-8D24-8C992DEB6034}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.FileSystem.Net5Compat.Tests", "tests\Net5CompatTests\System.IO.FileSystem.Net5Compat.Tests.csproj", "{48E07F12-8597-40DE-8A37-CCBEB9D54012}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamConformanceTests", "..\Common\tests\StreamConformanceTests\StreamConformanceTests.csproj", "{FEF03BCC-509F-4646-9132-9DE27FA3DA6F}" +EndProject Global GlobalSection(NestedProjects) = preSolution {D350D6E7-52F1-40A4-B646-C178F6BBB689} = {1A727AF9-4F39-4109-BB8F-813286031DC9} @@ -37,6 +41,8 @@ Global {06DD5AA8-FDA6-495B-A8D1-8CE83C78DE6C} = {32A31E04-2554-4223-BED8-45757408B4F6} {877E39A8-51CB-463A-AF4C-6FAE4F438075} = {D9FB1730-B750-4C0D-8D24-8C992DEB6034} {D7DF8034-3AE5-4DEF-BCC4-6353239391BF} = {D9FB1730-B750-4C0D-8D24-8C992DEB6034} + {48E07F12-8597-40DE-8A37-CCBEB9D54012} = {1A727AF9-4F39-4109-BB8F-813286031DC9} + {FEF03BCC-509F-4646-9132-9DE27FA3DA6F} = {1A727AF9-4F39-4109-BB8F-813286031DC9} EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,6 +89,14 @@ Global {06DD5AA8-FDA6-495B-A8D1-8CE83C78DE6C}.Debug|Any CPU.Build.0 = Debug|Any CPU {06DD5AA8-FDA6-495B-A8D1-8CE83C78DE6C}.Release|Any CPU.ActiveCfg = Release|Any CPU {06DD5AA8-FDA6-495B-A8D1-8CE83C78DE6C}.Release|Any CPU.Build.0 = Release|Any CPU + {48E07F12-8597-40DE-8A37-CCBEB9D54012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48E07F12-8597-40DE-8A37-CCBEB9D54012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48E07F12-8597-40DE-8A37-CCBEB9D54012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48E07F12-8597-40DE-8A37-CCBEB9D54012}.Release|Any CPU.Build.0 = Release|Any CPU + {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEF03BCC-509F-4646-9132-9DE27FA3DA6F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs b/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs index 64c2bbc309368..08121dc0b5b25 100644 --- a/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs +++ b/src/libraries/System.IO.FileSystem/ref/System.IO.FileSystem.cs @@ -101,6 +101,7 @@ public EnumerationOptions() { } public bool IgnoreInaccessible { get { throw null; } set { } } public System.IO.MatchCasing MatchCasing { get { throw null; } set { } } public System.IO.MatchType MatchType { get { throw null; } set { } } + public int MaxRecursionDepth { get { throw null; } set { } } public bool RecurseSubdirectories { get { throw null; } set { } } public bool ReturnSpecialDirectories { get { throw null; } set { } } } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs index 95bb53f43ba8f..ae179b015e075 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs @@ -22,7 +22,7 @@ public unsafe abstract partial class FileSystemEnumerator : CriticalFin private string? _currentPath; private IntPtr _directoryHandle; private bool _lastEntryFound; - private Queue? _pending; + private Queue<(string Path, int RemainingDepth)>? _pending; private Interop.Sys.DirectoryEntry _entry; private TResult? _current; @@ -144,12 +144,12 @@ public bool MoveNext() if (isDirectory && !isSpecialDirectory) { - if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry)) + if (_options.RecurseSubdirectories && _remainingRecursionDepth > 0 && ShouldRecurseIntoEntry(ref entry)) { // Recursion is on and the directory was accepted, Queue it if (_pending == null) - _pending = new Queue(); - _pending.Enqueue(Path.Join(_currentPath, entry.FileName)); + _pending = new Queue<(string Path, int RemainingDepth)>(); + _pending.Enqueue((Path.Join(_currentPath, entry.FileName), _remainingRecursionDepth - 1)); } } @@ -215,7 +215,7 @@ private bool DequeueNextDirectory() if (_pending == null || _pending.Count == 0) return false; - _currentPath = _pending.Dequeue(); + (_currentPath, _remainingRecursionDepth) = _pending.Dequeue(); _directoryHandle = CreateDirectoryHandle(_currentPath, ignoreNotFound: true); } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs index 32fd206ea897d..a6ac2027bedac 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.Windows.cs @@ -38,7 +38,7 @@ public unsafe abstract partial class FileSystemEnumerator : CriticalFin private IntPtr _directoryHandle; private string? _currentPath; private bool _lastEntryFound; - private Queue<(IntPtr Handle, string Path)>? _pending; + private Queue<(IntPtr Handle, string Path, int RemainingDepth)>? _pending; private void Init() { @@ -161,7 +161,7 @@ public bool MoveNext() if (!_options.ReturnSpecialDirectories) continue; } - else if (_options.RecurseSubdirectories && ShouldRecurseIntoEntry(ref entry)) + else if (_options.RecurseSubdirectories && _remainingRecursionDepth > 0 && ShouldRecurseIntoEntry(ref entry)) { // Recursion is on and the directory was accepted, Queue it string subDirectory = Path.Join(_currentPath.AsSpan(), _entry->FileName); @@ -171,8 +171,8 @@ public bool MoveNext() try { if (_pending == null) - _pending = new Queue<(IntPtr, string)>(); - _pending.Enqueue((subDirectoryHandle, subDirectory)); + _pending = new Queue<(IntPtr, string, int)>(); + _pending.Enqueue((subDirectoryHandle, subDirectory, _remainingRecursionDepth - 1)); } catch { @@ -209,7 +209,7 @@ private bool DequeueNextDirectory() if (_pending == null || _pending.Count == 0) return false; - (_directoryHandle, _currentPath) = _pending.Dequeue(); + (_directoryHandle, _currentPath, _remainingRecursionDepth) = _pending.Dequeue(); return true; } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs index b5168eb1ab059..5a626a85bf7fb 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEnumerator.cs @@ -14,6 +14,8 @@ namespace System.IO.Enumeration { public unsafe abstract partial class FileSystemEnumerator : CriticalFinalizerObject, IEnumerator { + private int _remainingRecursionDepth; + /// /// Encapsulates a find operation. /// @@ -37,6 +39,7 @@ internal FileSystemEnumerator(string directory, bool isNormalized, EnumerationOp string path = isNormalized ? directory : Path.GetFullPath(directory); _rootDirectory = Path.TrimEndingDirectorySeparator(path); _options = options ?? EnumerationOptions.Default; + _remainingRecursionDepth = _options.MaxRecursionDepth; Init(); } diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs b/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs index ca6b0350a8e3b..e3d6ad15b20d1 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/EnumerationOptions.cs @@ -12,6 +12,10 @@ namespace System.IO { public class EnumerationOptions { + private int _maxRecursionDepth; + + internal const int DefaultMaxRecursionDepth = int.MaxValue; + /// /// For internal use. These are the options we want to use if calling the existing Directory/File APIs where you don't /// explicitly specify EnumerationOptions. @@ -34,6 +38,7 @@ public EnumerationOptions() { IgnoreInaccessible = true; AttributesToSkip = FileAttributes.Hidden | FileAttributes.System; + MaxRecursionDepth = DefaultMaxRecursionDepth; } /// @@ -96,6 +101,24 @@ internal static EnumerationOptions FromSearchOption(SearchOption searchOption) /// public MatchCasing MatchCasing { get; set; } + /// Gets or sets a value that indicates the maximum directory depth to recurse while enumerating, when is set to . + /// A number that represents the maximum directory depth to recurse while enumerating. The default value is . + /// If is set to a negative number, the default value is used. + /// If is set to zero, enumeration returns the contents of the initial directory. + public int MaxRecursionDepth + { + get => _maxRecursionDepth; + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + } + + _maxRecursionDepth = value; + } + } + /// /// Set to true to return "." and ".." directory entries. /// diff --git a/src/libraries/System.IO.FileSystem/src/System/IO/ReadLinesIterator.cs b/src/libraries/System.IO.FileSystem/src/System/IO/ReadLinesIterator.cs index 349526cc8bb8e..cc77c9064a5b6 100644 --- a/src/libraries/System.IO.FileSystem/src/System/IO/ReadLinesIterator.cs +++ b/src/libraries/System.IO.FileSystem/src/System/IO/ReadLinesIterator.cs @@ -25,7 +25,7 @@ namespace System.IO // - IEnumerator instances from the same IEnumerable party on the same underlying // reader. // - internal class ReadLinesIterator : Iterator + internal sealed class ReadLinesIterator : Iterator { private readonly string _path; private readonly Encoding _encoding; diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs new file mode 100644 index 0000000000000..5ac0e61653404 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Enumeration/RecursionDepthTests.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO.Enumeration; +using System.Linq; +using Xunit; + +namespace System.IO.Tests.Enumeration +{ + public class RecursionDepthTests : FileSystemTest + { + public static IEnumerable GetEntryNames(string directory, int depth) + { + return new FileSystemEnumerable( + directory, + (ref FileSystemEntry entry) => entry.FileName.ToString(), + new EnumerationOptions() { RecurseSubdirectories = true, MaxRecursionDepth = depth }); + } + + [Theory, + InlineData(0, 2), + InlineData(1, 4), + InlineData(2, 5), + InlineData(3, 5), + InlineData(int.MaxValue, 5) + ] + public void EnumerateDirectory_WithSpecifedRecursionDepth(int depth, int expectedCount) + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); + DirectoryInfo testSubdirectory1 = Directory.CreateDirectory(Path.Combine(testDirectory.FullName, "Subdirectory1")); + DirectoryInfo testSubdirectory2 = Directory.CreateDirectory(Path.Combine(testSubdirectory1.FullName, "Subdirectory2")); + FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "fileone.htm")); + FileInfo fileTwo = new FileInfo(Path.Combine(testSubdirectory1.FullName, "filetwo.html")); + FileInfo fileThree = new FileInfo(Path.Combine(testSubdirectory2.FullName, "filethree.doc")); + + fileOne.Create().Dispose(); + fileTwo.Create().Dispose(); + fileThree.Create().Dispose(); + + string[] results = GetEntryNames(testDirectory.FullName, depth).ToArray(); + Assert.Equal(expectedCount, results.Length); + } + + [Fact] + public void NegativeRecursionDepth_ThrowsArgumentOutOfRangeException() + { + Assert.Throws(() => new EnumerationOptions() { MaxRecursionDepth = -1 }); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs index ebf7d91aee7bb..82b10a882c3d3 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/FileStreamConformanceTests.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; using System.IO.Pipes; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -29,6 +31,136 @@ private Task CreateStream(byte[] initialData, FileAccess access) protected override Task CreateWriteOnlyStreamCore(byte[] initialData) => CreateStream(initialData, FileAccess.Write); protected override bool NopFlushCompletesSynchronously => OperatingSystem.IsWindows(); + + [Theory] + [MemberData(nameof(AllReadWriteModes))] + public async Task FileOffsetIsPreservedWhenFileStreamIsCreatedFromSafeFileHandle_Reads(ReadWriteMode mode) + { + byte[] initialData = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + using FileStream stream = (FileStream)await CreateReadOnlyStreamCore(initialData); + byte[] buffer = new byte[5]; + int bytesRead = await ReadAsync(mode, stream, buffer, 0, buffer.Length); + + Assert.Equal(bytesRead, stream.Position); + + using FileStream createdFromHandle = new FileStream(stream.SafeFileHandle, FileAccess.Read); + + Assert.Equal(bytesRead, stream.Position); // accessing SafeFileHandle must not change the position + Assert.Equal(stream.Position, createdFromHandle.Position); // but it should sync the offset with OS + } + + [Theory] + [MemberData(nameof(AllReadWriteModes))] + public async Task FileOffsetIsPreservedWhenFileStreamIsCreatedFromSafeFileHandle_Writes(ReadWriteMode mode) + { + using FileStream stream = (FileStream)await CreateWriteOnlyStreamCore(Array.Empty()); + byte[] buffer = new byte[] { 0, 1, 2, 3, 4 }; + await WriteAsync(mode, stream, buffer, 0, buffer.Length); + + Assert.Equal(buffer.Length, stream.Position); + + using FileStream createdFromHandle = new FileStream(stream.SafeFileHandle, FileAccess.Write); + + Assert.Equal(buffer.Length, stream.Position); + Assert.Equal(stream.Position, createdFromHandle.Position); + } + + [Theory] + [MemberData(nameof(AllReadWriteModes))] + public async Task WriteAsyncStartsWherePreviousReadAsyncHasFinished(ReadWriteMode mode) + { + if (mode == ReadWriteMode.SyncByte) + { + // it reads a single byte even if buffer.Length > 1 + return; + } + + byte[] initialData = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + byte[] readBuffer = new byte[initialData.Length * 2]; // the edge case: reading more than available + byte[] writeBuffer = new byte[] { 10, 11, 12, 13, 14, 15 }; + string filePath; + + using (FileStream stream = (FileStream)await CreateReadWriteStreamCore(initialData)) + { + filePath = stream.Name; + + int bytesRead = await ReadAsync(mode, stream, readBuffer, 0, readBuffer.Length); + + Assert.Equal(bytesRead, initialData.Length); + Assert.Equal(initialData.Length, stream.Position); + Assert.Equal(stream.Position, stream.Length); + + await WriteAsync(mode, stream, writeBuffer, 0, writeBuffer.Length); + + Assert.Equal(initialData.Length + writeBuffer.Length, stream.Position); + Assert.Equal(stream.Position, stream.Length); + } + + byte[] allBytes = File.ReadAllBytes(filePath); + Assert.Equal(initialData.Concat(writeBuffer), allBytes); + } + + [Theory] + [MemberData(nameof(AllReadWriteModes))] + public async Task NoDataIsLostWhenWritingToFile(ReadWriteMode mode) + { + string filePath; + List writtenBytes = new List(); + + using (FileStream stream = (FileStream)await CreateWriteOnlyStreamCore(Array.Empty())) + { + filePath = stream.Name; + + // the following buffer fits into internal FileStream buffer + byte[] small = Enumerable.Repeat(byte.MinValue, BufferSize - 1).ToArray(); + // the following buffer does not fit into internal FileStream buffer + byte[] big = Enumerable.Repeat(byte.MaxValue, BufferSize + 1).ToArray(); + // in this test we are selecting a random buffer and write it to file + // the goal is to cover all possible scenarios for the internal buffering logic + Random random = new Random(12345); + for (int i = 0; i < 1000; i++) + { + byte[] bytes = random.Next() % 2 == 0 ? small : big; + + await WriteAsync(mode, stream, bytes, 0, bytes.Length); + + writtenBytes.AddRange(bytes); + Assert.Equal(writtenBytes.Count, stream.Length); + Assert.Equal(stream.Length, stream.Position); + } + } + + byte[] allBytes = File.ReadAllBytes(filePath); + Assert.Equal(writtenBytes.ToArray(), allBytes); + } + + [Theory] + [InlineData(FileAccess.Write)] + [InlineData(FileAccess.ReadWrite)] // FileAccess.Read does not allow for length manipulations + public async Task LengthIsNotCachedAfterHandleHasBeenExposed(FileAccess fileAccess) + { + using FileStream stream = (FileStream)await CreateStream(Array.Empty(), fileAccess); + using FileStream createdFromHandle = new FileStream(stream.SafeFileHandle, fileAccess); + + Assert.Equal(0, stream.Length); + Assert.Equal(0, createdFromHandle.Length); + + createdFromHandle.SetLength(1); + Assert.Equal(1, createdFromHandle.Length); + Assert.Equal(1, stream.Length); + + createdFromHandle.SetLength(2); + Assert.Equal(2, createdFromHandle.Length); + Assert.Equal(2, stream.Length); + + stream.SetLength(1); + Assert.Equal(1, stream.Length); + Assert.Equal(1, createdFromHandle.Length); + + stream.SetLength(2); + Assert.Equal(2, stream.Length); + Assert.Equal(2, createdFromHandle.Length); + } } public class UnbufferedSyncFileStreamStandaloneConformanceTests : FileStreamStandaloneConformanceTests diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs index 04773b5a8df72..40064cd42c359 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/SafeFileHandle.cs @@ -37,6 +37,14 @@ public void DisposeClosesHandle() } } + [Fact] + public void DisposingBufferedFileStreamThatWasClosedViaSafeFileHandleCloseDoesNotThrow() + { + FileStream fs = new FileStream(GetTestFilePath(), FileMode.Create, FileAccess.ReadWrite, FileShare.Read, bufferSize: 100); + fs.SafeFileHandle.Dispose(); + fs.Dispose(); // must not throw + } + [Fact] public void AccessFlushesFileClosesHandle() { @@ -58,13 +66,13 @@ public void AccessFlushesFileClosesHandle() } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNet5CompatFileStreamEnabled))] public async Task ThrowWhenHandlePositionIsChanged_sync() { await ThrowWhenHandlePositionIsChanged(useAsync: false); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported), nameof(PlatformDetection.IsNet5CompatFileStreamEnabled))] public async Task ThrowWhenHandlePositionIsChanged_async() { await ThrowWhenHandlePositionIsChanged(useAsync: true); @@ -96,15 +104,13 @@ private async Task ThrowWhenHandlePositionIsChanged(bool useAsync) // Put data in FS write buffer and update position from FSR fs.WriteByte(0); fsr.Position = 0; - Assert.Throws(() => fs.Position); - - fs.WriteByte(0); - fsr.Position++; - Assert.Throws(() => fs.Read(new byte[1], 0, 1)); - fs.WriteByte(0); - fsr.Position++; - if (useAsync && OperatingSystem.IsWindows()) // Async I/O behaviors differ due to kernel-based implementation on Windows + if (useAsync + // Async I/O behaviors differ due to kernel-based implementation on Windows + && OperatingSystem.IsWindows() + // ReadAsync which in this case (single byte written to buffer) calls FlushAsync is now 100% async + // so it does not complete synchronously anymore + && PlatformDetection.IsNet5CompatFileStreamEnabled) { Assert.Throws(() => FSAssert.CompletesSynchronously(fs.ReadAsync(new byte[1], 0, 1))); } @@ -113,6 +119,14 @@ private async Task ThrowWhenHandlePositionIsChanged(bool useAsync) await Assert.ThrowsAsync(() => fs.ReadAsync(new byte[1], 0, 1)); } + fs.WriteByte(0); + fsr.Position++; + Assert.Throws(() => fs.Read(new byte[1], 0, 1)); + + fs.WriteByte(0); + fsr.Position++; + await Assert.ThrowsAsync(() => fs.ReadAsync(new byte[1], 0, 1)); + fs.WriteByte(0); fsr.Position++; Assert.Throws(() => fs.ReadByte()); diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs index 6ed4d1db2c4cc..9e4db63e6fbc3 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/WriteAsync.cs @@ -222,7 +222,15 @@ public async Task ManyConcurrentWriteAsyncs_OuterLoop( Assert.Null(writes[i].Exception); if (useAsync) { - Assert.Equal((i + 1) * writeSize, fs.Position); + // To ensure that the buffer of a FileStream opened for async IO is flushed + // by FlushAsync in asynchronous way, we aquire a lock for every buffered WriteAsync. + // The side effect of this is that the Position of FileStream is not updated until + // the lock is released by a previous operation. + // So now all WriteAsync calls should be awaited before starting another async file operation. + if (PlatformDetection.IsNet5CompatFileStreamEnabled) + { + Assert.Equal((i + 1) * writeSize, fs.Position); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs new file mode 100644 index 0000000000000..bb2d05086cf0a --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/Net5CompatSwitchTests.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using Xunit; + +namespace System.IO.Tests +{ + public class Net5CompatSwitchTests + { + [Fact] + public static void LegacySwitchIsHonored() + { + string filePath = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + + using (FileStream fileStream = File.Create(filePath)) + { + object strategy = fileStream + .GetType() + .GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance) + .GetValue(fileStream); + + Assert.DoesNotContain("Net5Compat", strategy.GetType().FullName); + } + + File.Delete(filePath); + } + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj new file mode 100644 index 0000000000000..1389ee3462649 --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -0,0 +1,36 @@ + + + true + true + + $(NetCoreAppCurrent)-windows + + --working-dir=/test-dir + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json new file mode 100644 index 0000000000000..0c1a3482aba4f --- /dev/null +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.IO.UseNet5CompatFileStream": false + } +} diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index 7a81b2e7b1f3d..f22b4500be176 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -4,7 +4,7 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser - --working-dir=/test-dir + --working-dir=/test-dir @@ -32,6 +32,7 @@ + diff --git a/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs b/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs index e18e564bd48f3..dadc27e2fbf2b 100644 --- a/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs +++ b/src/libraries/System.IO.IsolatedStorage/tests/AssemblyInfo.cs @@ -8,4 +8,5 @@ // create unique identities for every test to allow every test to have // it's own store. [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] +[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/48720", TestPlatforms.AnyUnix, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [assembly: SkipOnMono("System.IO.IsolatedStorage is not supported on Browser", TestPlatforms.Browser)] diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs index 2e8b989b7dd8d..202c3ead180f2 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/Interop.Windows.cs @@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -internal partial class Interop +internal static partial class Interop { public static unsafe void CheckForAvailableVirtualMemory(ulong nativeSize) { diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs index 83a60d4ccf914..b5e5a54c56c26 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Unix.cs @@ -6,7 +6,7 @@ namespace System.IO.MemoryMappedFiles { - internal partial class MemoryMappedView + internal sealed partial class MemoryMappedView { public static MemoryMappedView CreateView( SafeMemoryMappedFileHandle memMappedFileHandle, MemoryMappedFileAccess access, diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs index 01117ab6960bc..cc3300c12c9fe 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.Windows.cs @@ -8,7 +8,7 @@ namespace System.IO.MemoryMappedFiles { - internal partial class MemoryMappedView + internal sealed partial class MemoryMappedView { // These control the retry behaviour when lock violation errors occur during Flush: private const int MaxFlushWaits = 15; // must be <=30 diff --git a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs index 32730cb04b9de..84905632e61ab 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedView.cs @@ -6,7 +6,7 @@ namespace System.IO.MemoryMappedFiles { - internal partial class MemoryMappedView : IDisposable + internal sealed partial class MemoryMappedView : IDisposable { private readonly SafeMemoryMappedViewHandle _viewHandle; private readonly long _pointerOffset; @@ -43,7 +43,7 @@ public MemoryMappedFileAccess Access get { return _access; } } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (!_viewHandle.IsClosed) { diff --git a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CrossProcess.cs b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CrossProcess.cs index fa8f454e3052e..61e5b9eb92b89 100644 --- a/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CrossProcess.cs +++ b/src/libraries/System.IO.MemoryMappedFiles/tests/MemoryMappedFile.CrossProcess.cs @@ -9,6 +9,7 @@ namespace System.IO.MemoryMappedFiles.Tests public class CrossProcessTests : FileCleanupTestBase { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void DataShared() { // Create a new file and load it into an MMF diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs index df36d6818e685..4713d8f3497aa 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/InternalRelationshipCollection.cs @@ -25,7 +25,7 @@ namespace System.IO.Packaging /// /// Collection of all the relationships corresponding to a given source PackagePart /// - internal class InternalRelationshipCollection : IEnumerable + internal sealed class InternalRelationshipCollection : IEnumerable { // Mono will parse a URI starting with '/' as an absolute URI, while .NET Core and // .NET Framework will parse this as relative. This will break internal relationships diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/OrderedDictionary.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/OrderedDictionary.cs index 0f17bb5596d06..37cf8b27714b0 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/OrderedDictionary.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/OrderedDictionary.cs @@ -12,7 +12,7 @@ namespace System.IO.Packaging /// This is similar to , but the items will not be sorted by a comparer but rather retain the /// order in which they were added while still retaining good lookup, insertion, and removal. /// - internal class OrderedDictionary : IEnumerable where TKey : notnull + internal sealed class OrderedDictionary : IEnumerable where TKey : notnull { private readonly Dictionary> _dictionary; private readonly LinkedList _order; diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs index aaf4ecddef6ba..f4ed524180ef1 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PartBasedPackageProperties.cs @@ -22,7 +22,7 @@ namespace System.IO.Packaging /// Setting a property to null deletes this property. 'null' is never strictly speaking /// a property value, but an absence indicator. /// - internal class PartBasedPackageProperties : PackageProperties + internal sealed class PartBasedPackageProperties : PackageProperties { #region Constructors diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs index 8eff678658bd3..d24b354558bb0 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/XmlCompatibilityReader.cs @@ -1558,7 +1558,7 @@ public NamespaceElementPair(string namespaceName, string itemName) /// Each scope stores the "previous" or parent scope, its depth, and an associated XmlCompatibilityReader. /// At a particular Reader depth, only one scope should be pushed. /// - private class CompatibilityScope + private sealed class CompatibilityScope { private readonly CompatibilityScope? _previous; private readonly int _depth; @@ -1834,7 +1834,7 @@ public void Verify() } } - private class ProcessContentSet + private sealed class ProcessContentSet { private bool _all; private readonly string _namespaceName; @@ -1889,7 +1889,7 @@ public void Add(string elementName) } } - private class PreserveItemSet + private sealed class PreserveItemSet { private bool _all; private readonly string _namespaceName; diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs index 34d40137de6d2..fb4f41723ded8 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipPackage.cs @@ -541,7 +541,7 @@ int IEqualityComparer.GetHashCode(string extension) /// This is a helper class that maintains the Content Types File related to /// this ZipPackage. /// - private class ContentTypeHelper + private sealed class ContentTypeHelper { /// /// Initialize the object without uploading any information from the package. diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipStreamManager.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipStreamManager.cs index e04d3cf1173a7..387819ece7a53 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipStreamManager.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipStreamManager.cs @@ -9,7 +9,7 @@ namespace System.IO.Packaging { - internal class ZipStreamManager : IDisposable + internal sealed class ZipStreamManager : IDisposable { private readonly ZipArchive _zipArchive; private readonly FileAccess _packageFileAccess; @@ -100,7 +100,7 @@ public void Dispose() } // Protected implementation of Dispose pattern. - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (_disposed) return; diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipWrappingStream.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipWrappingStream.cs index a5dd341eb7d5b..4b2df41a9838d 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipWrappingStream.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/ZipWrappingStream.cs @@ -10,7 +10,7 @@ namespace System.IO.Packaging { - internal class ZipWrappingStream : Stream + internal sealed class ZipWrappingStream : Stream { private readonly Stream _baseStream; private readonly ZipArchiveEntry _zipArchiveEntry; diff --git a/src/libraries/System.IO.Pipelines/ref/System.IO.Pipelines.cs b/src/libraries/System.IO.Pipelines/ref/System.IO.Pipelines.cs index b0388773d0f0f..4f35df94ce11a 100644 --- a/src/libraries/System.IO.Pipelines/ref/System.IO.Pipelines.cs +++ b/src/libraries/System.IO.Pipelines/ref/System.IO.Pipelines.cs @@ -50,6 +50,7 @@ protected PipeReader() { } public virtual System.Threading.Tasks.Task CopyToAsync(System.IO.Pipelines.PipeWriter destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task CopyToAsync(System.IO.Stream destination, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.IO.Pipelines.PipeReader Create(System.IO.Stream stream, System.IO.Pipelines.StreamPipeReaderOptions? readerOptions = null) { throw null; } + public static System.IO.Pipelines.PipeReader Create(System.Buffers.ReadOnlySequence sequence) { throw null; } [System.ObsoleteAttribute("OnWriterCompleted may not be invoked on all implementations of PipeReader. This will be removed in a future release.")] public virtual void OnWriterCompleted(System.Action callback, object? state) { } public abstract System.Threading.Tasks.ValueTask ReadAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); diff --git a/src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csproj b/src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csproj index db4b9fb79535b..539d5f0dcef31 100644 --- a/src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csproj +++ b/src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csproj @@ -31,6 +31,7 @@ + diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs index caf0767c2e14b..1abb6be8ca155 100644 --- a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/Pipe.cs @@ -795,6 +795,7 @@ private void CompletePipe() } _writingHead = null; + _writingHeadMemory = default; _readHead = null; _readTail = null; _lastExaminedIndex = -1; diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs index 17427bb0a1203..fb25780a3fab4 100644 --- a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeAwaitable.cs @@ -186,7 +186,7 @@ private enum AwaitableState UseSynchronizationContext = 8 } - private class SchedulingContext + private sealed class SchedulingContext { public SynchronizationContext? SynchronizationContext { get; set; } public ExecutionContext? ExecutionContext { get; set; } diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReader.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReader.cs index e7f383275e5d9..238fb5ba1c4f2 100644 --- a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReader.cs +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/PipeReader.cs @@ -12,7 +12,7 @@ public abstract partial class PipeReader { private PipeReaderStream? _stream; - /// Attempts to synchronously read data the . + /// Attempts to synchronously read data from the . /// When this method returns , this value is set to a instance that represents the result of the read call; otherwise, this value is set to . /// if data was available, or if the call was canceled or the writer was completed; otherwise, . /// If the pipe returns , there is no need to call . @@ -104,6 +104,16 @@ public static PipeReader Create(Stream stream, StreamPipeReaderOptions? readerOp return new StreamPipeReader(stream, readerOptions ?? StreamPipeReaderOptions.s_default); } + /// + /// Creates a wrapping the specified . + /// + /// The sequence. + /// A that wraps the . + public static PipeReader Create(ReadOnlySequence sequence) + { + return new SequencePipeReader(sequence); + } + /// Asynchronously reads the bytes from the and writes them to the specified , using a specified buffer size and cancellation token. /// The pipe writer to which the contents of the current stream will be copied. /// The token to monitor for cancellation requests. The default value is . diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/SequencePipeReader.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/SequencePipeReader.cs new file mode 100644 index 0000000000000..963a595cb1fbc --- /dev/null +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/SequencePipeReader.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Threading; +using System.Threading.Tasks; + +namespace System.IO.Pipelines +{ + internal sealed class SequencePipeReader : PipeReader + { + private ReadOnlySequence _sequence; + private bool _isReaderCompleted; + + private int _cancelNext; + + public SequencePipeReader(ReadOnlySequence sequence) + { + _sequence = sequence; + } + + /// + public override void AdvanceTo(SequencePosition consumed) + { + AdvanceTo(consumed, consumed); + } + + /// + public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) + { + ThrowIfCompleted(); + + // Fast path: did we consume everything? + if (consumed.Equals(_sequence.End)) + { + _sequence = ReadOnlySequence.Empty; + return; + } + + _sequence = _sequence.Slice(consumed); + } + + /// + public override void CancelPendingRead() + { + Interlocked.Exchange(ref _cancelNext, 1); + } + + /// + public override void Complete(Exception? exception = null) + { + if (_isReaderCompleted) + { + return; + } + + _isReaderCompleted = true; + _sequence = ReadOnlySequence.Empty; + } + + /// + public override ValueTask ReadAsync(CancellationToken cancellationToken = default) + { + if (TryRead(out ReadResult result)) + { + return new ValueTask(result); + } + + result = new ReadResult(ReadOnlySequence.Empty, isCanceled: false, isCompleted: true); + return new ValueTask(result); + } + + /// + public override bool TryRead(out ReadResult result) + { + ThrowIfCompleted(); + + bool isCancellationRequested = Interlocked.Exchange(ref _cancelNext, 0) == 1; + if (isCancellationRequested || _sequence.Length > 0) + { + result = new ReadResult(_sequence, isCancellationRequested, isCompleted: true); + return true; + } + + result = default; + return false; + } + + private void ThrowIfCompleted() + { + if (_isReaderCompleted) + { + ThrowHelper.ThrowInvalidOperationException_NoReadingAllowed(); + } + } + } +} diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeReader.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeReader.cs index 0297ca8888392..2ab3696546621 100644 --- a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeReader.cs +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeReader.cs @@ -8,7 +8,7 @@ namespace System.IO.Pipelines { - internal class StreamPipeReader : PipeReader + internal sealed class StreamPipeReader : PipeReader { internal const int InitialSegmentPoolSize = 4; // 16K internal const int MaxSegmentPoolSize = 256; // 1MB diff --git a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs index 079fe296c0e01..80c4d77388d4e 100644 --- a/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs +++ b/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs @@ -8,7 +8,7 @@ namespace System.IO.Pipelines { - internal class StreamPipeWriter : PipeWriter + internal sealed class StreamPipeWriter : PipeWriter { internal const int InitialSegmentPoolSize = 4; // 16K internal const int MaxSegmentPoolSize = 256; // 1MB @@ -328,6 +328,7 @@ private async ValueTask FlushAsyncInternal(bool writeToStream, Read // Mark bytes as written *after* flushing _head = null; _tail = null; + _tailMemory = default; _bytesBuffered = 0; return new FlushResult(isCanceled: false, isCompleted: false); @@ -395,6 +396,7 @@ private void FlushInternal(bool writeToStream) // Mark bytes as written *after* flushing _head = null; _tail = null; + _tailMemory = default; _bytesBuffered = 0; } } diff --git a/src/libraries/System.IO.Pipelines/tests/SequencePipeReaderTests.cs b/src/libraries/System.IO.Pipelines/tests/SequencePipeReaderTests.cs new file mode 100644 index 0000000000000..d12e4f1ec8470 --- /dev/null +++ b/src/libraries/System.IO.Pipelines/tests/SequencePipeReaderTests.cs @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.IO.Pipelines.Tests +{ + public class SequencePipeReaderTests + { + [Fact] + public async Task CanRead() + { + var sequence = new ReadOnlySequence(Encoding.ASCII.GetBytes("Hello World")); + var reader = PipeReader.Create(sequence); + + ReadResult readResult = await reader.ReadAsync(); + ReadOnlySequence buffer = readResult.Buffer; + + Assert.Equal(11, buffer.Length); + Assert.True(buffer.IsSingleSegment); + Assert.Equal("Hello World", Encoding.ASCII.GetString(buffer.ToArray())); + + reader.AdvanceTo(buffer.End); + reader.Complete(); + } + + [Fact] + public async Task TryReadReturnsTrueIfBufferedBytesAndNotExaminedEverything() + { + var sequence = new ReadOnlySequence(Encoding.ASCII.GetBytes("Hello World")); + var reader = PipeReader.Create(sequence); + + ReadResult readResult = await reader.ReadAsync(); + ReadOnlySequence buffer = readResult.Buffer; + Assert.Equal(11, buffer.Length); + Assert.True(buffer.IsSingleSegment); + reader.AdvanceTo(buffer.Start, buffer.GetPosition(5)); + + Assert.True(reader.TryRead(out readResult)); + Assert.Equal(11, buffer.Length); + Assert.True(buffer.IsSingleSegment); + Assert.Equal("Hello World", Encoding.ASCII.GetString(buffer.ToArray())); + + reader.Complete(); + } + + [Fact] + public async Task TryReadReturnsFalseIfBufferedBytesAndEverythingExamined() + { + var sequence = new ReadOnlySequence(Encoding.ASCII.GetBytes("Hello World")); + var reader = PipeReader.Create(sequence); + + ReadResult readResult = await reader.ReadAsync(); + ReadOnlySequence buffer = readResult.Buffer; + Assert.Equal(11, buffer.Length); + Assert.True(buffer.IsSingleSegment); + reader.AdvanceTo(buffer.End); + + Assert.False(reader.TryRead(out readResult)); + reader.Complete(); + } + + [Fact] + public async Task ReadAsyncAfterReceivingCompletedReadResultDoesNotThrow() + { + var sequence = ReadOnlySequence.Empty; + PipeReader reader = PipeReader.Create(sequence); + ReadResult readResult = await reader.ReadAsync(); + Assert.True(readResult.Buffer.IsEmpty); + Assert.True(readResult.IsCompleted); + reader.AdvanceTo(readResult.Buffer.End); + + readResult = await reader.ReadAsync(); + Assert.True(readResult.Buffer.IsEmpty); + Assert.True(readResult.IsCompleted); + reader.AdvanceTo(readResult.Buffer.End); + reader.Complete(); + } + + [Fact] + public async Task DataCanBeReadMultipleTimes() + { + var helloBytes = Encoding.ASCII.GetBytes("Hello World"); + var sequence = new ReadOnlySequence(helloBytes); + PipeReader reader = PipeReader.Create(sequence); + + + ReadResult readResult = await reader.ReadAsync(); + ReadOnlySequence buffer = readResult.Buffer; + reader.AdvanceTo(buffer.Start, buffer.End); + + // Make sure IsCompleted is true + readResult = await reader.ReadAsync(); + buffer = readResult.Buffer; + reader.AdvanceTo(buffer.Start, buffer.End); + Assert.True(readResult.IsCompleted); + + var value = await ReadFromPipeAsString(reader); + Assert.Equal("Hello World", value); + reader.Complete(); + } + + [Fact] + public async Task NextReadAfterPartiallyExaminedReturnsImmediately() + { + var sequence = new ReadOnlySequence(Encoding.ASCII.GetBytes(new string('a', 10000))); + PipeReader reader = PipeReader.Create(sequence); + + ReadResult readResult = await reader.ReadAsync(); + reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.GetPosition(2048)); + + ValueTask task = reader.ReadAsync(); + + // This should complete synchronously since + Assert.True(task.IsCompleted); + + readResult = await task; + reader.AdvanceTo(readResult.Buffer.End); + reader.Complete(); + } + + [Fact] + public async Task CompleteReaderWithoutAdvanceDoesNotThrow() + { + PipeReader reader = PipeReader.Create(ReadOnlySequence.Empty); + await reader.ReadAsync(); + reader.Complete(); + } + + [Fact] + public async Task AdvanceAfterCompleteThrows() + { + PipeReader reader = PipeReader.Create(new ReadOnlySequence(new byte[100])); + ReadOnlySequence buffer = (await reader.ReadAsync()).Buffer; + + reader.Complete(); + + Assert.Throws(() => reader.AdvanceTo(buffer.End)); + } + + [Fact] + public async Task ThrowsOnReadAfterCompleteReader() + { + PipeReader reader = PipeReader.Create(ReadOnlySequence.Empty); + + reader.Complete(); + await Assert.ThrowsAsync(async () => await reader.ReadAsync()); + } + + [Fact] + public void TryReadAfterCancelPendingReadReturnsTrue() + { + PipeReader reader = PipeReader.Create(ReadOnlySequence.Empty); + + reader.CancelPendingRead(); + + Assert.True(reader.TryRead(out ReadResult result)); + Assert.True(result.IsCanceled); + reader.AdvanceTo(result.Buffer.End); + reader.Complete(); + } + + [Fact] + public async Task ReadAsyncReturnsCanceledIfCanceledBeforeRead() + { + var sequence = new ReadOnlySequence(new byte[10000]); + PipeReader reader = PipeReader.Create(sequence); + + // Make sure state isn't used from before + for (var i = 0; i < 3; i++) + { + reader.CancelPendingRead(); + ValueTask readResultTask = reader.ReadAsync(); + Assert.True(readResultTask.IsCompleted); + ReadResult readResult = readResultTask.GetAwaiter().GetResult(); + Assert.True(readResult.IsCanceled); + readResult = await reader.ReadAsync(); + reader.AdvanceTo(readResult.Buffer.End); + } + + reader.Complete(); + } + + [Fact] + public async Task ReadAsyncReturnsCanceledInterleaved() + { + var sequence = new ReadOnlySequence(new byte[10000]); + PipeReader reader = PipeReader.Create(sequence); + + // Cancel and Read interleaved to confirm cancellations are independent + for (var i = 0; i < 3; i++) + { + reader.CancelPendingRead(); + ValueTask readResultTask = reader.ReadAsync(); + Assert.True(readResultTask.IsCompleted); + ReadResult readResult = readResultTask.GetAwaiter().GetResult(); + Assert.True(readResult.IsCanceled); + + readResult = await reader.ReadAsync(); + Assert.False(readResult.IsCanceled); + } + + reader.Complete(); + } + + [Fact] + public void OnWriterCompletedNoops() + { + bool fired = false; + PipeReader reader = PipeReader.Create(ReadOnlySequence.Empty); +#pragma warning disable CS0618 // Type or member is obsolete + reader.OnWriterCompleted((_, __) => { fired = true; }, null); +#pragma warning restore CS0618 // Type or member is obsolete + reader.Complete(); + Assert.False(fired); + } + + private static async Task ReadFromPipeAsString(PipeReader reader) + { + ReadResult readResult = await reader.ReadAsync(); + var result = Encoding.ASCII.GetString(readResult.Buffer.ToArray()); + reader.AdvanceTo(readResult.Buffer.End); + return result; + } + } +} diff --git a/src/libraries/System.IO.Pipelines/tests/System.IO.Pipelines.Tests.csproj b/src/libraries/System.IO.Pipelines/tests/System.IO.Pipelines.Tests.csproj index d4b9d67b32f66..fe6ecb26ae0a9 100644 --- a/src/libraries/System.IO.Pipelines/tests/System.IO.Pipelines.Tests.csproj +++ b/src/libraries/System.IO.Pipelines/tests/System.IO.Pipelines.Tests.csproj @@ -30,6 +30,7 @@ + diff --git a/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs b/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs index 98e9e10d816a8..c9e67d69dd5d4 100644 --- a/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Unix.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Security; +using System.Threading; namespace Microsoft.Win32.SafeHandles { @@ -14,38 +15,35 @@ public sealed partial class SafePipeHandle : SafeHandleZeroOrMinusOneIsInvalid { private const int DefaultInvalidHandle = -1; - // For anonymous pipes, SafePipeHandle.handle is the file descriptor of the pipe, and the - // _named* fields remain null. For named pipes, SafePipeHandle.handle is a copy of the file descriptor - // extracted from the Socket's SafeHandle, and the _named* fields are the socket and its safe handle. + // For anonymous pipes, SafePipeHandle.handle is the file descriptor of the pipe. + // For named pipes, SafePipeHandle.handle is a copy of the file descriptor + // extracted from the Socket's SafeHandle. // This allows operations related to file descriptors to be performed directly on the SafePipeHandle, - // and operations that should go through the Socket to be done via _namedPipeSocket. We keep the + // and operations that should go through the Socket to be done via PipeSocket. We keep the // Socket's SafeHandle alive as long as this SafeHandle is alive. - private Socket? _namedPipeSocket; - private SafeHandle? _namedPipeSocketHandle; + private Socket? _pipeSocket; + private SafeHandle? _pipeSocketHandle; + private volatile int _disposed; internal SafePipeHandle(Socket namedPipeSocket) : base(ownsHandle: true) { - Debug.Assert(namedPipeSocket != null); - _namedPipeSocket = namedPipeSocket; - - _namedPipeSocketHandle = namedPipeSocket.SafeHandle; - - bool ignored = false; - _namedPipeSocketHandle.DangerousAddRef(ref ignored); - SetHandle(_namedPipeSocketHandle.DangerousGetHandle()); + SetPipeSocketInterlocked(namedPipeSocket, ownsHandle: true); + base.SetHandle(_pipeSocketHandle!.DangerousGetHandle()); } - internal Socket? NamedPipeSocket => _namedPipeSocket; - internal SafeHandle? NamedPipeSocketHandle => _namedPipeSocketHandle; + internal Socket PipeSocket => _pipeSocket ?? CreatePipeSocket(); + + internal SafeHandle? PipeSocketHandle => _pipeSocketHandle; protected override void Dispose(bool disposing) { base.Dispose(disposing); // must be called before trying to Dispose the socket - if (disposing && _namedPipeSocket != null) + _disposed = 1; + if (disposing && Volatile.Read(ref _pipeSocket) is Socket socket) { - _namedPipeSocket.Dispose(); - _namedPipeSocket = null; + socket.Dispose(); + _pipeSocket = null; } } @@ -53,24 +51,92 @@ protected override bool ReleaseHandle() { Debug.Assert(!IsInvalid); - // Clean up resources for named handles - if (_namedPipeSocketHandle != null) + if (_pipeSocketHandle != null) { - SetHandle(DefaultInvalidHandle); - _namedPipeSocketHandle.DangerousRelease(); - _namedPipeSocketHandle = null; + base.SetHandle((IntPtr)DefaultInvalidHandle); + _pipeSocketHandle.DangerousRelease(); + _pipeSocketHandle = null; return true; } - - // Clean up resources for anonymous handles - return (long)handle >= 0 ? - Interop.Sys.Close(handle) == 0 : - true; + else + { + return (long)handle >= 0 ? + Interop.Sys.Close(handle) == 0 : + true; + } } public override bool IsInvalid { - get { return (long)handle < 0 && _namedPipeSocket == null; } + get { return (long)handle < 0 && _pipeSocket == null; } + } + + private Socket CreatePipeSocket(bool ownsHandle = true) + { + Socket? socket = null; + if (_disposed == 0) + { + bool refAdded = false; + try + { + DangerousAddRef(ref refAdded); + + socket = SetPipeSocketInterlocked(new Socket(new SafeSocketHandle(handle, ownsHandle)), ownsHandle); + + // Double check if we haven't Disposed in the meanwhile, and ensure + // the Socket is disposed, in case Dispose() missed the _pipeSocket assignment. + if (_disposed == 1) + { + Volatile.Write(ref _pipeSocket, null); + socket.Dispose(); + socket = null; + } + } + finally + { + if (refAdded) + { + DangerousRelease(); + } + } + } + return socket ?? throw new ObjectDisposedException(GetType().ToString()); + } + + private Socket SetPipeSocketInterlocked(Socket socket, bool ownsHandle) + { + Debug.Assert(socket != null); + + // Multiple threads may try to create the PipeSocket. + Socket? current = Interlocked.CompareExchange(ref _pipeSocket, socket, null); + if (current != null) + { + socket.Dispose(); + return current; + } + + // If we own the handle, defer ownership to the SocketHandle. + SafeSocketHandle socketHandle = _pipeSocket.SafeHandle; + if (ownsHandle) + { + _pipeSocketHandle = socketHandle; + + bool ignored = false; + socketHandle.DangerousAddRef(ref ignored); + } + + return socket; + } + + internal void SetHandle(IntPtr descriptor, bool ownsHandle = true) + { + base.SetHandle(descriptor); + + // Avoid throwing when we own the handle by defering pipe creation. + if (!ownsHandle) + { + _pipeSocket = CreatePipeSocket(ownsHandle); + } } } } diff --git a/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Windows.cs b/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Windows.cs index 946f305c9c7f9..5a1d27149f944 100644 --- a/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.Windows.cs @@ -15,5 +15,10 @@ protected override bool ReleaseHandle() { return Interop.Kernel32.CloseHandle(handle); } + + internal void SetHandle(IntPtr descriptor, bool ownsHandle = true) + { + base.SetHandle(descriptor); + } } } diff --git a/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs b/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs index f19fa957d7910..13d9d4ae5acef 100644 --- a/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs +++ b/src/libraries/System.IO.Pipes/src/Microsoft/Win32/SafeHandles/SafePipeHandle.cs @@ -17,12 +17,7 @@ public SafePipeHandle() public SafePipeHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) { - SetHandle(preexistingHandle); - } - - internal void SetHandle(int descriptor) - { - base.SetHandle((IntPtr)descriptor); + SetHandle(preexistingHandle, ownsHandle); } } } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs index 42fe518e0012e..3e07b15738cb6 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs @@ -83,7 +83,7 @@ public override int InBufferSize { CheckPipePropertyOperations(); if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - return InternalHandle?.NamedPipeSocket?.ReceiveBufferSize ?? 0; + return InternalHandle?.PipeSocket.ReceiveBufferSize ?? 0; } } @@ -93,7 +93,7 @@ public override int OutBufferSize { CheckPipePropertyOperations(); if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - return InternalHandle?.NamedPipeSocket?.SendBufferSize ?? 0; + return InternalHandle?.PipeSocket.SendBufferSize ?? 0; } } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 7f0d62611bc44..4628fe4fce0b2 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -134,7 +134,7 @@ public string GetImpersonationUserName() { CheckWriteOperations(); - SafeHandle? handle = InternalHandle?.NamedPipeSocketHandle; + SafeHandle? handle = InternalHandle?.PipeSocketHandle; if (handle == null) { throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet); @@ -155,7 +155,7 @@ public override int InBufferSize { CheckPipePropertyOperations(); if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); - return InternalHandle?.NamedPipeSocket?.ReceiveBufferSize ?? _inBufferSize; + return InternalHandle?.PipeSocket.ReceiveBufferSize ?? _inBufferSize; } } @@ -165,7 +165,7 @@ public override int OutBufferSize { CheckPipePropertyOperations(); if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); - return InternalHandle?.NamedPipeSocket?.SendBufferSize ?? _outBufferSize; + return InternalHandle?.PipeSocket.SendBufferSize ?? _outBufferSize; } } @@ -173,7 +173,7 @@ public override int OutBufferSize public void RunAsClient(PipeStreamImpersonationWorker impersonationWorker) { CheckWriteOperations(); - SafeHandle? handle = InternalHandle?.NamedPipeSocketHandle; + SafeHandle? handle = InternalHandle?.PipeSocketHandle; if (handle == null) { throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet); diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs index 8168691c9a93f..a6ed15e1cd379 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs @@ -277,7 +277,7 @@ private static void RevertImpersonationOnBackout(object? helper, bool exceptionT } } - internal class ExecuteHelper + internal sealed class ExecuteHelper { internal PipeStreamImpersonationWorker _userCode; internal SafePipeHandle? _handle; diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs index 6e6fa8a5c1385..2fa979b222a81 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs @@ -32,6 +32,156 @@ public abstract partial class PipeStream : Stream /// Prefix to prepend to all pipe names. private static readonly string s_pipePrefix = Path.Combine(Path.GetTempPath(), "CoreFxPipe_"); + public override int Read(byte[] buffer, int offset, int count) + { + ValidateBufferArguments(buffer, offset, count); + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + CheckReadOperations(); + + return ReadCore(new Span(buffer, offset, count)); + } + + public override int Read(Span buffer) + { + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + CheckReadOperations(); + + return ReadCore(buffer); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + CheckReadOperations(); + + if (count == 0) + { + UpdateMessageCompletion(false); + return Task.FromResult(0); + } + + return ReadAsyncCore(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + CheckReadOperations(); + + if (buffer.Length == 0) + { + UpdateMessageCompletion(false); + return new ValueTask(0); + } + + return ReadAsyncCore(buffer, cancellationToken); + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state); + + public override int EndRead(IAsyncResult asyncResult) + => TaskToApm.End(asyncResult); + + public override void Write(byte[] buffer, int offset, int count) + { + ValidateBufferArguments(buffer, offset, count); + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + CheckWriteOperations(); + + WriteCore(new ReadOnlySpan(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan buffer) + { + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + CheckWriteOperations(); + + WriteCore(buffer); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + CheckWriteOperations(); + + if (count == 0) + { + return Task.CompletedTask; + } + + return WriteAsyncCore(new ReadOnlyMemory(buffer, offset, count), cancellationToken); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + CheckWriteOperations(); + + if (buffer.Length == 0) + { + return default; + } + + return new ValueTask(WriteAsyncCore(buffer, cancellationToken)); + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) + => TaskToApm.End(asyncResult); + internal static string GetPipePath(string serverName, string pipeName) { if (serverName != "." && serverName != Interop.Sys.GetHostName()) @@ -80,17 +230,14 @@ internal static string GetPipePath(string serverName, string pipeName) /// The handle to validate. internal void ValidateHandleIsPipe(SafePipeHandle safePipeHandle) { - if (safePipeHandle.NamedPipeSocket == null) + Interop.Sys.FileStatus status; + int result = CheckPipeCall(Interop.Sys.FStat(safePipeHandle, out status)); + if (result == 0) { - Interop.Sys.FileStatus status; - int result = CheckPipeCall(Interop.Sys.FStat(safePipeHandle, out status)); - if (result == 0) + if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFIFO && + (status.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFSOCK) { - if ((status.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFIFO && - (status.Mode & Interop.Sys.FileTypes.S_IFMT) != Interop.Sys.FileTypes.S_IFSOCK) - { - throw new IOException(SR.IO_InvalidPipeHandle); - } + throw new IOException(SR.IO_InvalidPipeHandle); } } } @@ -98,9 +245,7 @@ internal void ValidateHandleIsPipe(SafePipeHandle safePipeHandle) /// Initializes the handle to be used asynchronously. /// The handle. private void InitializeAsyncHandle(SafePipeHandle handle) - { - // nop - } + { } internal virtual void DisposeCore(bool disposing) { @@ -112,30 +257,22 @@ private unsafe int ReadCore(Span buffer) Debug.Assert(_handle != null); DebugAssertHandleValid(_handle); - // For named pipes, receive on the socket. - Socket? socket = _handle.NamedPipeSocket; - if (socket != null) + if (buffer.Length == 0) { - // For a blocking socket, we could simply use the same Read syscall as is done - // for reading an anonymous pipe. However, for a non-blocking socket, Read could - // end up returning EWOULDBLOCK rather than blocking waiting for data. Such a case - // is already handled by Socket.Receive, so we use it here. - try - { - return socket.Receive(buffer, SocketFlags.None); - } - catch (SocketException e) - { - throw GetIOExceptionForSocketException(e); - } + return 0; } - // For anonymous pipes, read from the file descriptor. - fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) + // For a blocking socket, we could simply use the same Read syscall as is done + // for reading an anonymous pipe. However, for a non-blocking socket, Read could + // end up returning EWOULDBLOCK rather than blocking waiting for data. Such a case + // is already handled by Socket.Receive, so we use it here. + try { - int result = CheckPipeCall(Interop.Sys.Read(_handle, bufPtr, buffer.Length)); - Debug.Assert(result <= buffer.Length); - return result; + return _handle!.PipeSocket.Receive(buffer, SocketFlags.None); + } + catch (SocketException e) + { + throw GetIOExceptionForSocketException(e); } } @@ -144,46 +281,29 @@ private unsafe void WriteCore(ReadOnlySpan buffer) Debug.Assert(_handle != null); DebugAssertHandleValid(_handle); - // For named pipes, send to the socket. - Socket? socket = _handle.NamedPipeSocket; - if (socket != null) - { - // For a blocking socket, we could simply use the same Write syscall as is done - // for writing to anonymous pipe. However, for a non-blocking socket, Write could - // end up returning EWOULDBLOCK rather than blocking waiting for space available. - // Such a case is already handled by Socket.Send, so we use it here. - try - { - while (buffer.Length > 0) - { - int bytesWritten = socket.Send(buffer, SocketFlags.None); - buffer = buffer.Slice(bytesWritten); - } - } - catch (SocketException e) - { - throw GetIOExceptionForSocketException(e); - } - } - - // For anonymous pipes, write the file descriptor. - fixed (byte* bufPtr = &MemoryMarshal.GetReference(buffer)) + // For a blocking socket, we could simply use the same Write syscall as is done + // for writing to anonymous pipe. However, for a non-blocking socket, Write could + // end up returning EWOULDBLOCK rather than blocking waiting for space available. + // Such a case is already handled by Socket.Send, so we use it here. + try { while (buffer.Length > 0) { - int bytesWritten = CheckPipeCall(Interop.Sys.Write(_handle, bufPtr, buffer.Length)); + int bytesWritten = _handle!.PipeSocket.Send(buffer, SocketFlags.None); buffer = buffer.Slice(bytesWritten); } } + catch (SocketException e) + { + throw GetIOExceptionForSocketException(e); + } } private async ValueTask ReadAsyncCore(Memory destination, CancellationToken cancellationToken) { - Debug.Assert(this is NamedPipeClientStream || this is NamedPipeServerStream, $"Expected a named pipe, got a {GetType()}"); - try { - return await InternalHandle!.NamedPipeSocket!.ReceiveAsync(destination, SocketFlags.None, cancellationToken).ConfigureAwait(false); + return await InternalHandle!.PipeSocket.ReceiveAsync(destination, SocketFlags.None, cancellationToken).ConfigureAwait(false); } catch (SocketException e) { @@ -193,13 +313,11 @@ private async ValueTask ReadAsyncCore(Memory destination, Cancellatio private async Task WriteAsyncCore(ReadOnlyMemory source, CancellationToken cancellationToken) { - Debug.Assert(this is NamedPipeClientStream || this is NamedPipeServerStream, $"Expected a named pipe, got a {GetType()}"); - try { while (source.Length > 0) { - int bytesWritten = await _handle!.NamedPipeSocket!.SendAsync(source, SocketFlags.None, cancellationToken).ConfigureAwait(false); + int bytesWritten = await _handle!.PipeSocket.SendAsync(source, SocketFlags.None, cancellationToken).ConfigureAwait(false); Debug.Assert(bytesWritten > 0 && bytesWritten <= source.Length); source = source.Slice(bytesWritten); } @@ -350,8 +468,8 @@ internal static unsafe void CreateAnonymousPipe(out SafePipeHandle reader, out S Interop.CheckIo(Interop.Sys.Pipe(fds, Interop.Sys.PipeFlags.O_CLOEXEC)); // Store the file descriptors into our safe handles - reader.SetHandle(fds[Interop.Sys.ReadEndOfPipe]); - writer.SetHandle(fds[Interop.Sys.WriteEndOfPipe]); + reader.SetHandle(new IntPtr(fds[Interop.Sys.ReadEndOfPipe])); + writer.SetHandle(new IntPtr(fds[Interop.Sys.WriteEndOfPipe])); } internal int CheckPipeCall(int result) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs index 252796cd1f199..928cc5d1a081f 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs @@ -16,6 +16,218 @@ public abstract partial class PipeStream : Stream internal const bool CheckOperationsRequiresSetHandle = true; internal ThreadPoolBoundHandle? _threadPoolBinding; + public override int Read(byte[] buffer, int offset, int count) + { + if (_isAsync) + { + return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } + + ValidateBufferArguments(buffer, offset, count); + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + CheckReadOperations(); + + return ReadCore(new Span(buffer, offset, count)); + } + + public override int Read(Span buffer) + { + if (_isAsync) + { + return base.Read(buffer); + } + + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + CheckReadOperations(); + + return ReadCore(buffer); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + CheckReadOperations(); + + if (!_isAsync) + { + return base.ReadAsync(buffer, offset, count, cancellationToken); + } + + if (count == 0) + { + UpdateMessageCompletion(false); + return Task.FromResult(0); + } + + return ReadAsyncCore(new Memory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_isAsync) + { + return base.ReadAsync(buffer, cancellationToken); + } + + if (!CanRead) + { + throw Error.GetReadNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + CheckReadOperations(); + + if (buffer.Length == 0) + { + UpdateMessageCompletion(false); + return new ValueTask(0); + } + + return ReadAsyncCore(buffer, cancellationToken); + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + { + if (_isAsync) + return TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state); + else + return base.BeginRead(buffer, offset, count, callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + if (_isAsync) + return TaskToApm.End(asyncResult); + else + return base.EndRead(asyncResult); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (_isAsync) + { + WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + return; + } + + ValidateBufferArguments(buffer, offset, count); + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + CheckWriteOperations(); + + WriteCore(new ReadOnlySpan(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan buffer) + { + if (_isAsync) + { + base.Write(buffer); + return; + } + + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + CheckWriteOperations(); + + WriteCore(buffer); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + ValidateBufferArguments(buffer, offset, count); + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + CheckWriteOperations(); + + if (!_isAsync) + { + return base.WriteAsync(buffer, offset, count, cancellationToken); + } + + if (count == 0) + { + return Task.CompletedTask; + } + + return WriteAsyncCore(new ReadOnlyMemory(buffer, offset, count), cancellationToken); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(CancellationToken)) + { + if (!_isAsync) + { + return base.WriteAsync(buffer, cancellationToken); + } + + if (!CanWrite) + { + throw Error.GetWriteNotSupported(); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + CheckWriteOperations(); + + if (buffer.Length == 0) + { + return default; + } + + return new ValueTask(WriteAsyncCore(buffer, cancellationToken)); + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + { + if (_isAsync) + return TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state); + else + return base.BeginWrite(buffer, offset, count, callback, state); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + if (_isAsync) + TaskToApm.End(asyncResult); + else + base.EndWrite(asyncResult); + } + internal static string GetPipePath(string serverName, string pipeName) { string normalizedPipePath = Path.GetFullPath(@"\\" + serverName + @"\pipe\" + pipeName); diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs index 4dad917bd453f..6df150682be21 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs @@ -107,218 +107,6 @@ protected void InitializeHandle(SafePipeHandle? handle, bool isExposed, bool isA _isFromExistingHandle = isExposed; } - public override int Read(byte[] buffer, int offset, int count) - { - if (_isAsync) - { - return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - } - - ValidateBufferArguments(buffer, offset, count); - if (!CanRead) - { - throw Error.GetReadNotSupported(); - } - CheckReadOperations(); - - return ReadCore(new Span(buffer, offset, count)); - } - - public override int Read(Span buffer) - { - if (_isAsync) - { - return base.Read(buffer); - } - - if (!CanRead) - { - throw Error.GetReadNotSupported(); - } - CheckReadOperations(); - - return ReadCore(buffer); - } - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - ValidateBufferArguments(buffer, offset, count); - if (!CanRead) - { - throw Error.GetReadNotSupported(); - } - - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - CheckReadOperations(); - - if (!_isAsync) - { - return base.ReadAsync(buffer, offset, count, cancellationToken); - } - - if (count == 0) - { - UpdateMessageCompletion(false); - return Task.FromResult(0); - } - - return ReadAsyncCore(new Memory(buffer, offset, count), cancellationToken).AsTask(); - } - - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default(CancellationToken)) - { - if (!_isAsync) - { - return base.ReadAsync(buffer, cancellationToken); - } - - if (!CanRead) - { - throw Error.GetReadNotSupported(); - } - - if (cancellationToken.IsCancellationRequested) - { - return ValueTask.FromCanceled(cancellationToken); - } - - CheckReadOperations(); - - if (buffer.Length == 0) - { - UpdateMessageCompletion(false); - return new ValueTask(0); - } - - return ReadAsyncCore(buffer, cancellationToken); - } - - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - if (_isAsync) - return TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state); - else - return base.BeginRead(buffer, offset, count, callback, state); - } - - public override int EndRead(IAsyncResult asyncResult) - { - if (_isAsync) - return TaskToApm.End(asyncResult); - else - return base.EndRead(asyncResult); - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (_isAsync) - { - WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - return; - } - - ValidateBufferArguments(buffer, offset, count); - if (!CanWrite) - { - throw Error.GetWriteNotSupported(); - } - CheckWriteOperations(); - - WriteCore(new ReadOnlySpan(buffer, offset, count)); - } - - public override void Write(ReadOnlySpan buffer) - { - if (_isAsync) - { - base.Write(buffer); - return; - } - - if (!CanWrite) - { - throw Error.GetWriteNotSupported(); - } - CheckWriteOperations(); - - WriteCore(buffer); - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - ValidateBufferArguments(buffer, offset, count); - if (!CanWrite) - { - throw Error.GetWriteNotSupported(); - } - - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - CheckWriteOperations(); - - if (!_isAsync) - { - return base.WriteAsync(buffer, offset, count, cancellationToken); - } - - if (count == 0) - { - return Task.CompletedTask; - } - - return WriteAsyncCore(new ReadOnlyMemory(buffer, offset, count), cancellationToken); - } - - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default(CancellationToken)) - { - if (!_isAsync) - { - return base.WriteAsync(buffer, cancellationToken); - } - - if (!CanWrite) - { - throw Error.GetWriteNotSupported(); - } - - if (cancellationToken.IsCancellationRequested) - { - return ValueTask.FromCanceled(cancellationToken); - } - - CheckWriteOperations(); - - if (buffer.Length == 0) - { - return default; - } - - return new ValueTask(WriteAsyncCore(buffer, cancellationToken)); - } - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - if (_isAsync) - return TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state); - else - return base.BeginWrite(buffer, offset, count, callback, state); - } - - public override void EndWrite(IAsyncResult asyncResult) - { - if (_isAsync) - TaskToApm.End(asyncResult); - else - base.EndWrite(asyncResult); - } - [Conditional("DEBUG")] private static void DebugAssertHandleValid(SafePipeHandle handle) { diff --git a/src/libraries/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.CrossProcess.cs b/src/libraries/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.CrossProcess.cs index 9a68529796319..ff475367616d6 100644 --- a/src/libraries/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.CrossProcess.cs +++ b/src/libraries/System.IO.Pipes/tests/AnonymousPipeTests/AnonymousPipeTest.CrossProcess.cs @@ -8,6 +8,7 @@ namespace System.IO.Pipes.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class AnonymousPipeTest_CrossProcess { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs index 83e52342abd5b..6e4fbc02c3494 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CrossProcess.cs @@ -11,6 +11,7 @@ namespace System.IO.Pipes.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public sealed class NamedPipeTest_CrossProcess { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs index 220be9d65ca8a..4eb889567c6fb 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs @@ -346,6 +346,7 @@ public async Task Windows_GetImpersonationUserName_Succeed(TokenImpersonationLev [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] // Uses P/Invoke to verify the user name + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public async Task Unix_GetImpersonationUserName_Succeed() { string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs index 3f5a9e352d348..7bab6b302d4de 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs @@ -11,6 +11,7 @@ public class NamedPipeTest_UnixDomainSockets { [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49873", TestPlatforms.Android)] public void NamedPipeServer_Connects_With_UnixDomainSocketEndPointClient() { string pipeName = Path.Combine("/tmp", "pipe-tests-corefx-" + Path.GetRandomFileName()); @@ -28,6 +29,7 @@ public void NamedPipeServer_Connects_With_UnixDomainSocketEndPointClient() [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49873", TestPlatforms.Android)] public async Task NamedPipeClient_Connects_With_UnixDomainSocketEndPointServer() { string pipeName = Path.Combine("/tmp", "pipe-tests-corefx-" + Path.GetRandomFileName()); diff --git a/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs b/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs index 444ea87431c06..b4962b60d11be 100644 --- a/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs +++ b/src/libraries/System.IO.Pipes/tests/PipeStreamConformanceTests.cs @@ -20,7 +20,7 @@ public static string GetUniquePipeName() => protected override Type UnsupportedConcurrentExceptionType => null; protected override bool UsableAfterCanceledReads => false; protected override bool CansReturnFalseAfterDispose => false; - protected override bool FullyCancelableOperations => false; + protected override bool FullyCancelableOperations => !OperatingSystem.IsWindows(); [PlatformSpecific(TestPlatforms.Windows)] // WaitForPipeDrain isn't supported on Unix [Fact] diff --git a/src/libraries/System.IO.Ports/Directory.Build.props b/src/libraries/System.IO.Ports/Directory.Build.props index b2e30a348448c..de0f5365f7735 100644 --- a/src/libraries/System.IO.Ports/Directory.Build.props +++ b/src/libraries/System.IO.Ports/Directory.Build.props @@ -3,10 +3,10 @@ Open true - browser + browser;android;ios;tvos Provides classes for controlling serial ports. Commonly Used Types: System.IO.Ports.SerialPort - \ No newline at end of file + diff --git a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Unix.cs b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Unix.cs index 3b1c766340651..b6ed4401c0ffe 100644 --- a/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Unix.cs +++ b/src/libraries/System.IO.Ports/src/System/IO/Ports/SerialStream.Unix.cs @@ -941,7 +941,7 @@ private static Exception GetLastIOError() return Interop.GetIOException(Interop.Sys.GetLastErrorInfo()); } - private class SerialStreamIORequest : TaskCompletionSource + private sealed class SerialStreamIORequest : TaskCompletionSource { public Memory Buffer { get; private set; } public bool IsCompleted => Task.IsCompleted; diff --git a/src/libraries/System.IO/System.IO.sln b/src/libraries/System.IO/System.IO.sln index fa248eb5af822..26a6942c71070 100644 --- a/src/libraries/System.IO/System.IO.sln +++ b/src/libraries/System.IO/System.IO.sln @@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{9FDAA57A-696 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D9FD8082-D04C-4DA8-9F4C-261D1C65A6D3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.IO.Net5Compat.Tests", "tests\Net5CompatTests\System.IO.Net5Compat.Tests.csproj", "{0217540D-FA86-41B3-9754-7BB5096ABA3E}" +EndProject Global GlobalSection(NestedProjects) = preSolution {D11D3624-1322-45D1-A604-7E68CDB85BE8} = {5AD2C433-C661-4AD1-BD9F-D164ADC43512} @@ -34,6 +36,7 @@ Global {D0D1CDAC-16F8-4382-A219-74A513CC1790} = {9FDAA57A-696B-4CB1-99AE-BCDF91848B75} {0769544B-1A5D-4D74-94FD-899DF6C39D62} = {D9FD8082-D04C-4DA8-9F4C-261D1C65A6D3} {AA5E80B2-A0AA-46F1-B319-5B528BAC382B} = {D9FD8082-D04C-4DA8-9F4C-261D1C65A6D3} + {0217540D-FA86-41B3-9754-7BB5096ABA3E} = {5AD2C433-C661-4AD1-BD9F-D164ADC43512} EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -76,6 +79,10 @@ Global {D0D1CDAC-16F8-4382-A219-74A513CC1790}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0D1CDAC-16F8-4382-A219-74A513CC1790}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0D1CDAC-16F8-4382-A219-74A513CC1790}.Release|Any CPU.Build.0 = Release|Any CPU + {0217540D-FA86-41B3-9754-7BB5096ABA3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0217540D-FA86-41B3-9754-7BB5096ABA3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0217540D-FA86-41B3-9754-7BB5096ABA3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0217540D-FA86-41B3-9754-7BB5096ABA3E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs b/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs index d1a85304ae59a..84a1fc2b1c447 100644 --- a/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs +++ b/src/libraries/System.IO/tests/BinaryWriter/BinaryWriter.WriteByteCharTests.cs @@ -579,13 +579,13 @@ public void BinaryWriter_WriteSpan() char testChar; - testChar = BitConverter.ToChar(new byte[] { (byte)baseStream.ReadByte(), (byte)baseStream.ReadByte() }, 0); + testChar = (char)((ushort)baseStream.ReadByte() + ((ushort)baseStream.ReadByte() << 8)); Assert.Equal('a', testChar); - testChar = BitConverter.ToChar(new byte[] { (byte)baseStream.ReadByte(), (byte)baseStream.ReadByte() }, 0); + testChar = (char)((ushort)baseStream.ReadByte() + ((ushort)baseStream.ReadByte() << 8)); Assert.Equal('7', testChar); - testChar = BitConverter.ToChar(new byte[] { (byte)baseStream.ReadByte(), (byte)baseStream.ReadByte() }, 0); + testChar = (char)((ushort)baseStream.ReadByte() + ((ushort)baseStream.ReadByte() << 8)); Assert.Equal(char.MaxValue, testChar); } } diff --git a/src/libraries/System.IO/tests/MemoryStream/MemoryStream.ToArrayTests.cs b/src/libraries/System.IO/tests/MemoryStream/MemoryStream.ToArrayTests.cs new file mode 100644 index 0000000000000..1f787de74f35e --- /dev/null +++ b/src/libraries/System.IO/tests/MemoryStream/MemoryStream.ToArrayTests.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; +using System; +using System.IO; +using System.Collections.Generic; +using System.Security.Cryptography; + +namespace System.IO.Tests +{ + public class MemoryStream_ToArrayTests + { + [Theory] + [MemberData(nameof(GetArraysVariedBySize))] + public static void ToArray_ZeroOffset(byte[] array) + { + var stream = new MemoryStream(); + stream.Write(array); + var newArray = stream.ToArray(); + + Assert.Equal(array.Length, newArray.Length); + Assert.Equal(array, newArray); + } + + [Theory] + [MemberData(nameof(GetArraysVariedBySize))] + public static void ToArray_Offset(byte[] array) + { + int index = 0; + int count = array.Length; + + if (count > 3) + { + // Trim some off each end + index = 1; + count -= 3; + } + + var stream = new MemoryStream(array, index, count); + var newArray = stream.ToArray(); + + Assert.Equal(count, newArray.Length); + Assert.True(array.AsSpan(index, count).SequenceEqual(newArray)); + } + + public static IEnumerable GetArraysVariedBySize() + { + yield return new object[] { RandomNumberGenerator.GetBytes(0) }; + yield return new object[] { RandomNumberGenerator.GetBytes(1) }; + yield return new object[] { RandomNumberGenerator.GetBytes(2) }; + yield return new object[] { RandomNumberGenerator.GetBytes(256) }; + yield return new object[] { RandomNumberGenerator.GetBytes(512) }; + yield return new object[] { RandomNumberGenerator.GetBytes(1024) }; + yield return new object[] { RandomNumberGenerator.GetBytes(2047) }; + yield return new object[] { RandomNumberGenerator.GetBytes(2048) }; + yield return new object[] { RandomNumberGenerator.GetBytes(2049) }; + yield return new object[] { RandomNumberGenerator.GetBytes(2100) }; + } + } +} diff --git a/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj b/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj new file mode 100644 index 0000000000000..321e2f485088b --- /dev/null +++ b/src/libraries/System.IO/tests/Net5CompatTests/System.IO.Net5Compat.Tests.csproj @@ -0,0 +1,25 @@ + + + System.IO + true + true + true + + $(NetCoreAppCurrent)-windows + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json b/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json new file mode 100644 index 0000000000000..0c1a3482aba4f --- /dev/null +++ b/src/libraries/System.IO/tests/Net5CompatTests/runtimeconfig.template.json @@ -0,0 +1,5 @@ +{ + "configProperties": { + "System.IO.UseNet5CompatFileStream": false + } +} diff --git a/src/libraries/System.IO/tests/System.IO.Tests.csproj b/src/libraries/System.IO/tests/System.IO.Tests.csproj index 3bf8d260ba2ad..b0a0339292e9b 100644 --- a/src/libraries/System.IO/tests/System.IO.Tests.csproj +++ b/src/libraries/System.IO/tests/System.IO.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoClass.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoClass.cs index 1f3439e380562..8451677a1b0d7 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoClass.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoClass.cs @@ -10,7 +10,7 @@ namespace System.Dynamic /// members will share the same class. Classes are dynamically assigned as the /// expando object gains members. /// - internal class ExpandoClass + internal sealed class ExpandoClass { private readonly string[] _keys; // list of names associated with each element in the data array, sorted private readonly int _hashCode; // pre-calculated hash code of all the keys the class contains diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs index 5a6b72d308774..ae79848d9541e 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs @@ -346,7 +346,7 @@ public string[] Items [DebuggerTypeProxy(typeof(KeyCollectionDebugView))] [DebuggerDisplay("Count = {Count}")] - private class KeyCollection : ICollection + private sealed class KeyCollection : ICollection { private readonly ExpandoObject _expando; private readonly int _expandoVersion; @@ -485,7 +485,7 @@ public object[] Items [DebuggerTypeProxy(typeof(ValueCollectionDebugView))] [DebuggerDisplay("Count = {Count}")] - private class ValueCollection : ICollection + private sealed class ValueCollection : ICollection { private readonly ExpandoObject _expando; private readonly int _expandoVersion; @@ -773,7 +773,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() #region MetaExpando - private class MetaExpando : DynamicMetaObject + private sealed class MetaExpando : DynamicMetaObject { public MetaExpando(Expression expression, ExpandoObject value) : base(expression, BindingRestrictions.Empty, value) @@ -1024,7 +1024,7 @@ private BindingRestrictions GetRestrictions() /// pair. This enables us to do a class check in a thread safe manner w/o /// requiring locks. /// - private class ExpandoData + private sealed class ExpandoData { internal static ExpandoData Empty = new ExpandoData(); diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs index 4a204c0a74d79..cd3446767c633 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/DelegateHelpers.cs @@ -63,8 +63,6 @@ public static TReturn FuncThunk2(Func handle return (TReturn)handler(new object?[]{t1, t2}); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod", - Justification = "Calling Array.Empty() is safe since the T doesn't have trimming annotations.")] private static MethodInfo GetEmptyObjectArrayMethod() => typeof(Array).GetMethod(nameof(Array.Empty))!.MakeGenericMethod(typeof(object)); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs index 1b01c8be82666..bccac308a0133 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BinaryExpression.cs @@ -493,7 +493,7 @@ public static AssignBinaryExpression Make(Expression left, Expression right, boo public sealed override ExpressionType NodeType => ExpressionType.Assign; } - internal class ByRefAssignBinaryExpression : AssignBinaryExpression + internal sealed class ByRefAssignBinaryExpression : AssignBinaryExpression { internal ByRefAssignBinaryExpression(Expression left, Expression right) : base(left, right) diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BlockExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BlockExpression.cs index c8f12524bead5..4d6b22ec6fe73 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BlockExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/BlockExpression.cs @@ -676,7 +676,7 @@ internal override BlockExpression Rewrite(ReadOnlyCollection - internal class BlockExpressionList : IList + internal sealed class BlockExpressionList : IList { private readonly BlockExpression _block; private readonly Expression _arg0; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs index 8a359790b890b..6e6a915e76e98 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/DelegateHelpers.Generated.cs @@ -57,7 +57,7 @@ internal static TypeInfo GetNextTypeInfo(Type initialArg, TypeInfo curTypeInfo) private const int MaximumArity = 17; - internal class TypeInfo + internal sealed class TypeInfo { public Type DelegateType; public Dictionary TypeChain; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Address.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Address.cs index f0587cfd6df51..55dca2cc9c29e 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Address.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Address.cs @@ -8,7 +8,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { private void EmitAddress(Expression node, Type type) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs index 5101d8dbdeded..a888be0831e21 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs @@ -9,7 +9,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { private void EmitBinaryExpression(Expression expr) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.ControlFlow.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.ControlFlow.cs index 3e6033504fa84..72818567876c9 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.ControlFlow.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.ControlFlow.cs @@ -8,7 +8,7 @@ namespace System.Linq.Expressions.Compiler { // The part of the LambdaCompiler dealing with low level control flow // break, continue, return, exceptions, etc - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { private LabelInfo EnsureLabel(LabelTarget node) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs index 3a1e236cc0e6e..a3049f80d4927 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs @@ -12,7 +12,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { [Flags] internal enum CompilationFlags diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Lambda.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Lambda.cs index 65b987c456502..336e93318e1f0 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Lambda.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Lambda.cs @@ -12,7 +12,7 @@ namespace System.Linq.Expressions.Compiler /// Dynamic Language Runtime Compiler. /// This part compiles lambdas. /// - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { #if FEATURE_COMPILE_TO_METHODBUILDER private static int s_counter; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs index b20bff1306921..31008c476e45f 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Logical.cs @@ -8,7 +8,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { #region Conditional diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Statements.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Statements.cs index d9d6f4f9a64d3..321a38f8a069e 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Statements.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Statements.cs @@ -13,7 +13,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { private void EmitBlockExpression(Expression expr, CompilationFlags flags) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs index e84391a741499..f487d73d7c3f2 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs @@ -9,7 +9,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class LambdaCompiler + internal sealed partial class LambdaCompiler { private void EmitQuoteUnaryExpression(Expression expr) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Bindings.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Bindings.cs index 5f6c20e7ee7f0..1d1abb5982090 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Bindings.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Bindings.cs @@ -8,7 +8,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class StackSpiller + internal sealed partial class StackSpiller { private abstract class BindingRewriter { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs index be140e2748e96..d8d978dd47c21 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.ChildRewriter.cs @@ -9,7 +9,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class StackSpiller + internal sealed partial class StackSpiller { /// /// Rewrites child expressions, spilling them into temps if needed. The diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Generated.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Generated.cs index 703c00425f5a8..b1bd1f3dc6fc6 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Generated.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Generated.cs @@ -3,7 +3,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class StackSpiller + internal sealed partial class StackSpiller { private readonly StackGuard _guard = new StackGuard(); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.SpilledExpressionBlock.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.SpilledExpressionBlock.cs index b0ade4f5c7a18..99a3ed5456a0c 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.SpilledExpressionBlock.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.SpilledExpressionBlock.cs @@ -8,7 +8,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class StackSpiller + internal sealed partial class StackSpiller { /// /// Creates a special block that is marked as not allowing jumps in. diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Temps.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Temps.cs index 41222f5af4d05..d19ba67a7c111 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Temps.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/StackSpiller.Temps.cs @@ -7,7 +7,7 @@ namespace System.Linq.Expressions.Compiler { - internal partial class StackSpiller + internal sealed partial class StackSpiller { /// /// The source of temporary variables introduced during stack spilling. diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ConstantExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ConstantExpression.cs index 515c41c23c8d0..e4adc598570bd 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ConstantExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ConstantExpression.cs @@ -55,7 +55,7 @@ protected internal override Expression Accept(ExpressionVisitor visitor) } } - internal class TypedConstantExpression : ConstantExpression + internal sealed class TypedConstantExpression : ConstantExpression { internal TypedConstantExpression(object? value, Type type) : base(value) diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs index 96de71810676b..af967733a5a7a 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs @@ -401,13 +401,7 @@ protected internal override Expression VisitParameter(ParameterExpression node) protected internal override Expression VisitLambda(Expression node) { - Out( - string.Format(CultureInfo.CurrentCulture, - ".Lambda {0}<{1}>", - GetLambdaName(node), - node.Type.ToString() - ) - ); + Out($".Lambda {GetLambdaName(node)}<{node.Type}>"); if (_lambdas == null) { @@ -471,17 +465,11 @@ protected internal override Expression VisitConstant(ConstantExpression node) } else if ((value is string) && node.Type == typeof(string)) { - Out(string.Format( - CultureInfo.CurrentCulture, - "\"{0}\"", - value)); + Out($"\"{value}\""); } else if ((value is char) && node.Type == typeof(char)) { - Out(string.Format( - CultureInfo.CurrentCulture, - "'{0}'", - value)); + Out($"'{value}'"); } else if ((value is int) && node.Type == typeof(int) || (value is bool) && node.Type == typeof(bool)) @@ -498,11 +486,7 @@ protected internal override Expression VisitConstant(ConstantExpression node) } else { - Out(string.Format( - CultureInfo.CurrentCulture, - ".Constant<{0}>({1})", - node.Type.ToString(), - value)); + Out($".Constant<{node.Type}>({value})"); } } return node; @@ -994,7 +978,7 @@ protected internal override Expression VisitBlock(BlockExpression node) // last expression's type in the block. if (node.Type != node.GetExpression(node.ExpressionCount - 1).Type) { - Out(string.Format(CultureInfo.CurrentCulture, "<{0}>", node.Type.ToString())); + Out($"<{node.Type}>"); } VisitDeclarations(node.Variables); @@ -1149,7 +1133,7 @@ protected internal override Expression VisitIndex(IndexExpression node) protected internal override Expression VisitExtension(Expression node) { - Out(string.Format(CultureInfo.CurrentCulture, ".Extension<{0}>", node.GetType().ToString())); + Out($".Extension<{node.GetType()}>"); if (node.CanReduce) { @@ -1165,22 +1149,14 @@ protected internal override Expression VisitExtension(Expression node) protected internal override Expression VisitDebugInfo(DebugInfoExpression node) { - Out(string.Format( - CultureInfo.CurrentCulture, - ".DebugInfo({0}: {1}, {2} - {3}, {4})", - node.Document.FileName, - node.StartLine, - node.StartColumn, - node.EndLine, - node.EndColumn) - ); + Out($".DebugInfo({node.Document.FileName}: {node.StartLine}, {node.StartColumn} - {node.EndLine}, {node.EndColumn})"); return node; } private void DumpLabel(LabelTarget target) { - Out(string.Format(CultureInfo.CurrentCulture, ".LabelTarget {0}:", GetLabelTargetName(target))); + Out($".LabelTarget {GetLabelTargetName(target)}:"); } private string GetLabelTargetName(LabelTarget target) @@ -1198,13 +1174,7 @@ private string GetLabelTargetName(LabelTarget target) private void WriteLambda(LambdaExpression lambda) { - Out( - string.Format( - CultureInfo.CurrentCulture, - ".Lambda {0}<{1}>", - GetLambdaName(lambda), - lambda.Type.ToString()) - ); + Out($".Lambda {GetLambdaName(lambda)}<{lambda.Type}>"); VisitDeclarations(lambda.Parameters); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DynamicExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DynamicExpression.cs index 030c02e50676b..2081496c1b399 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DynamicExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/DynamicExpression.cs @@ -512,7 +512,7 @@ internal override DynamicExpression Rewrite(Expression[] args) } } - internal class TypedDynamicExpressionN : DynamicExpressionN + internal sealed class TypedDynamicExpressionN : DynamicExpressionN { internal TypedDynamicExpressionN(Type returnType, Type delegateType, CallSiteBinder binder, IReadOnlyList arguments) : base(delegateType, binder, arguments) diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.DebuggerProxy.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.DebuggerProxy.cs index c322284dde577..85064e7249a89 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.DebuggerProxy.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.DebuggerProxy.cs @@ -9,7 +9,7 @@ namespace System.Linq.Expressions { public partial class Expression { - internal class BinaryExpressionProxy + internal sealed class BinaryExpressionProxy { private readonly BinaryExpression _node; @@ -31,7 +31,7 @@ public BinaryExpressionProxy(BinaryExpression node) public Type Type => _node.Type; } - internal class BlockExpressionProxy + internal sealed class BlockExpressionProxy { private readonly BlockExpression _node; @@ -50,7 +50,7 @@ public BlockExpressionProxy(BlockExpression node) public ReadOnlyCollection Variables => _node.Variables; } - internal class CatchBlockProxy + internal sealed class CatchBlockProxy { private readonly CatchBlock _node; @@ -66,7 +66,7 @@ public CatchBlockProxy(CatchBlock node) public ParameterExpression? Variable => _node.Variable; } - internal class ConditionalExpressionProxy + internal sealed class ConditionalExpressionProxy { private readonly ConditionalExpression _node; @@ -85,7 +85,7 @@ public ConditionalExpressionProxy(ConditionalExpression node) public Type Type => _node.Type; } - internal class ConstantExpressionProxy + internal sealed class ConstantExpressionProxy { private readonly ConstantExpression _node; @@ -102,7 +102,7 @@ public ConstantExpressionProxy(ConstantExpression node) public object? Value => _node.Value; } - internal class DebugInfoExpressionProxy + internal sealed class DebugInfoExpressionProxy { private readonly DebugInfoExpression _node; @@ -124,7 +124,7 @@ public DebugInfoExpressionProxy(DebugInfoExpression node) public Type Type => _node.Type; } - internal class DefaultExpressionProxy + internal sealed class DefaultExpressionProxy { private readonly DefaultExpression _node; @@ -140,7 +140,7 @@ public DefaultExpressionProxy(DefaultExpression node) public Type Type => _node.Type; } - internal class GotoExpressionProxy + internal sealed class GotoExpressionProxy { private readonly GotoExpression _node; @@ -159,7 +159,7 @@ public GotoExpressionProxy(GotoExpression node) public Expression? Value => _node.Value; } - internal class IndexExpressionProxy + internal sealed class IndexExpressionProxy { private readonly IndexExpression _node; @@ -178,7 +178,7 @@ public IndexExpressionProxy(IndexExpression node) public Type Type => _node.Type; } - internal class InvocationExpressionProxy + internal sealed class InvocationExpressionProxy { private readonly InvocationExpression _node; @@ -196,7 +196,7 @@ public InvocationExpressionProxy(InvocationExpression node) public Type Type => _node.Type; } - internal class LabelExpressionProxy + internal sealed class LabelExpressionProxy { private readonly LabelExpression _node; @@ -214,7 +214,7 @@ public LabelExpressionProxy(LabelExpression node) public Type Type => _node.Type; } - internal class LambdaExpressionProxy + internal sealed class LambdaExpressionProxy { private readonly LambdaExpression _node; @@ -235,7 +235,7 @@ public LambdaExpressionProxy(LambdaExpression node) public Type Type => _node.Type; } - internal class ListInitExpressionProxy + internal sealed class ListInitExpressionProxy { private readonly ListInitExpression _node; @@ -253,7 +253,7 @@ public ListInitExpressionProxy(ListInitExpression node) public Type Type => _node.Type; } - internal class LoopExpressionProxy + internal sealed class LoopExpressionProxy { private readonly LoopExpression _node; @@ -272,7 +272,7 @@ public LoopExpressionProxy(LoopExpression node) public Type Type => _node.Type; } - internal class MemberExpressionProxy + internal sealed class MemberExpressionProxy { private readonly MemberExpression _node; @@ -290,7 +290,7 @@ public MemberExpressionProxy(MemberExpression node) public Type Type => _node.Type; } - internal class MemberInitExpressionProxy + internal sealed class MemberInitExpressionProxy { private readonly MemberInitExpression _node; @@ -308,7 +308,7 @@ public MemberInitExpressionProxy(MemberInitExpression node) public Type Type => _node.Type; } - internal class MethodCallExpressionProxy + internal sealed class MethodCallExpressionProxy { private readonly MethodCallExpression _node; @@ -327,7 +327,7 @@ public MethodCallExpressionProxy(MethodCallExpression node) public Type Type => _node.Type; } - internal class NewArrayExpressionProxy + internal sealed class NewArrayExpressionProxy { private readonly NewArrayExpression _node; @@ -344,7 +344,7 @@ public NewArrayExpressionProxy(NewArrayExpression node) public Type Type => _node.Type; } - internal class NewExpressionProxy + internal sealed class NewExpressionProxy { private readonly NewExpression _node; @@ -363,7 +363,7 @@ public NewExpressionProxy(NewExpression node) public Type Type => _node.Type; } - internal class ParameterExpressionProxy + internal sealed class ParameterExpressionProxy { private readonly ParameterExpression _node; @@ -381,7 +381,7 @@ public ParameterExpressionProxy(ParameterExpression node) public Type Type => _node.Type; } - internal class RuntimeVariablesExpressionProxy + internal sealed class RuntimeVariablesExpressionProxy { private readonly RuntimeVariablesExpression _node; @@ -398,7 +398,7 @@ public RuntimeVariablesExpressionProxy(RuntimeVariablesExpression node) public ReadOnlyCollection Variables => _node.Variables; } - internal class SwitchCaseProxy + internal sealed class SwitchCaseProxy { private readonly SwitchCase _node; @@ -412,7 +412,7 @@ public SwitchCaseProxy(SwitchCase node) public ReadOnlyCollection TestValues => _node.TestValues; } - internal class SwitchExpressionProxy + internal sealed class SwitchExpressionProxy { private readonly SwitchExpression _node; @@ -432,7 +432,7 @@ public SwitchExpressionProxy(SwitchExpression node) public Type Type => _node.Type; } - internal class TryExpressionProxy + internal sealed class TryExpressionProxy { private readonly TryExpression _node; @@ -452,7 +452,7 @@ public TryExpressionProxy(TryExpression node) public Type Type => _node.Type; } - internal class TypeBinaryExpressionProxy + internal sealed class TypeBinaryExpressionProxy { private readonly TypeBinaryExpression _node; @@ -470,7 +470,7 @@ public TypeBinaryExpressionProxy(TypeBinaryExpression node) public Type TypeOperand => _node.TypeOperand; } - internal class UnaryExpressionProxy + internal sealed class UnaryExpressionProxy { private readonly UnaryExpression _node; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs index f83e2caaa0dde..d9341dad16b1a 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Expression.cs @@ -31,7 +31,7 @@ public abstract partial class Expression // To support the 3.5 protected constructor, we store the fields that // used to be here in a ConditionalWeakTable. - private class ExtensionInfo + private sealed class ExtensionInfo { public ExtensionInfo(ExpressionType nodeType, Type type) { diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs index a4c704b6f8185..b0309c31fec8e 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/ExpressionStringBuilder.cs @@ -381,16 +381,7 @@ protected internal override Expression VisitConstant(ConstantExpression node) protected internal override Expression VisitDebugInfo(DebugInfoExpression node) { - string s = string.Format( - CultureInfo.CurrentCulture, - "", - node.Document.FileName, - node.StartLine, - node.StartColumn, - node.EndLine, - node.EndColumn - ); - Out(s); + Out($""); return node; } diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs index 103d1cfc5d2d9..f10233360deb0 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs @@ -387,7 +387,7 @@ public override int Run(InterpretedFrame frame) public override string ToString() => "Call(" + _target + ")"; } - internal class ByRefMethodInfoCallInstruction : MethodInfoCallInstruction + internal sealed class ByRefMethodInfoCallInstruction : MethodInfoCallInstruction { private readonly ByRefUpdater[] _byrefArgs; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs index dc058e63c469f..194af7be42011 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ControlFlowInstructions.cs @@ -142,7 +142,7 @@ public override int Run(InterpretedFrame frame) } } - internal class BranchInstruction : OffsetInstruction + internal sealed class BranchInstruction : OffsetInstruction { private static Instruction[][][]? s_caches; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs index b4238cd4711d1..784baac2de9a2 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightCompiler.cs @@ -211,7 +211,7 @@ internal sealed class DebugInfo public bool IsClear; private static readonly DebugInfoComparer s_debugComparer = new DebugInfoComparer(); - private class DebugInfoComparer : IComparer + private sealed class DebugInfoComparer : IComparer { //We allow comparison between int and DebugInfo here int IComparer.Compare(DebugInfo? d1, DebugInfo? d2) @@ -3101,7 +3101,7 @@ private void CompileNoLabelPush(Expression expr) break; } Debug.Assert(_instructions.CurrentStackDepth == startingStackDepth + (expr.Type == typeof(void) ? 0 : 1), - string.Format("{0} vs {1} for {2}", _instructions.CurrentStackDepth, startingStackDepth + (expr.Type == typeof(void) ? 0 : 1), expr.NodeType)); + $"{_instructions.CurrentStackDepth} vs {startingStackDepth + (expr.Type == typeof(void) ? 0 : 1)} for {expr.NodeType}"); } private void Compile(Expression expr) diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs index 770bc872d1ef3..3956e67b29a24 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LightLambda.cs @@ -33,7 +33,7 @@ internal LightLambda(LightDelegateCreator delegateCreator, IStrongBox[]? closure private string DebugView => new DebugViewPrinter(_interpreter).ToString(); - private class DebugViewPrinter + private sealed class DebugViewPrinter { private readonly Interpreter _interpreter; private readonly Dictionary _tryStart = new Dictionary(); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NewInstruction.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NewInstruction.cs index 680f9040899ba..966007da35ab2 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NewInstruction.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NewInstruction.cs @@ -67,7 +67,7 @@ public override int Run(InterpretedFrame frame) public override string ToString() => "New " + _constructor.DeclaringType!.Name + "(" + _constructor + ")"; } - internal class ByRefNewInstruction : NewInstruction + internal sealed class ByRefNewInstruction : NewInstruction { private readonly ByRefUpdater[] _byrefArgs; diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs index 6f429ea1af398..e802855f6bf81 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs @@ -167,7 +167,7 @@ public static void UnwrapAndRethrow(TargetInvocationException exception) /// /// A hybrid dictionary which compares based upon object identity. /// - internal class HybridReferenceDictionary where TKey : class + internal sealed class HybridReferenceDictionary where TKey : class { private KeyValuePair[]? _keysAndValues; private Dictionary? _dict; diff --git a/src/libraries/System.Linq.Expressions/src/System/Runtime/CompilerServices/ReadOnlyCollectionBuilder.cs b/src/libraries/System.Linq.Expressions/src/System/Runtime/CompilerServices/ReadOnlyCollectionBuilder.cs index a5192b47f7206..1524c49d9f08a 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Runtime/CompilerServices/ReadOnlyCollectionBuilder.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Runtime/CompilerServices/ReadOnlyCollectionBuilder.cs @@ -488,7 +488,7 @@ private static void ValidateNullValue(object? value, string argument) } } - private class Enumerator : IEnumerator, IEnumerator + private sealed class Enumerator : IEnumerator, IEnumerator { private readonly ReadOnlyCollectionBuilder _builder; private readonly int _version; diff --git a/src/libraries/System.Linq.Expressions/tests/New/NewWithTwoParametersTests.cs b/src/libraries/System.Linq.Expressions/tests/New/NewWithTwoParametersTests.cs index a4e4f692967da..8dde5c3fa07a5 100644 --- a/src/libraries/System.Linq.Expressions/tests/New/NewWithTwoParametersTests.cs +++ b/src/libraries/System.Linq.Expressions/tests/New/NewWithTwoParametersTests.cs @@ -28,7 +28,7 @@ public static void CheckNewWithTwoParametersStructWithValueTest(bool useInterpre public static void CheckNewWithTwoParametersCustom2Test(bool useInterpreter) { int[] array1 = { 0, 1, -1, int.MinValue, int.MaxValue }; - string[] array2 = { null, "", "a", "foo" }; ; + string[] array2 = { null, "", "a", "foo" }; for (int i = 0; i < array1.Length; i++) { for (int j = 0; j < array2.Length; j++) diff --git a/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj b/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj index 4b7fc689beb8b..b0a74eabbce8f 100644 --- a/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj +++ b/src/libraries/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj @@ -5,9 +5,6 @@ $(DefineConstants);FEATURE_COMPILE $(DefineConstants);FEATURE_INTERPRET $(NetCoreAppCurrent) - - true diff --git a/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj b/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj index c04ba4bf4e383..b78dd81e422a9 100644 --- a/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj +++ b/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj @@ -18,6 +18,7 @@ + @@ -123,7 +124,6 @@ - diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EmptyEnumerable.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EmptyEnumerable.cs index d4e21e184902a..3d675df1796a4 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EmptyEnumerable.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EmptyEnumerable.cs @@ -19,7 +19,7 @@ namespace System.Linq.Parallel /// implementations that always and consistently yield no elements. /// /// - internal class EmptyEnumerable : ParallelQuery + internal sealed class EmptyEnumerable : ParallelQuery { private EmptyEnumerable() : base(QuerySettings.Empty) diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EnumerableWrapperWeakToStrong.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EnumerableWrapperWeakToStrong.cs index 97c612e1045e0..88f559afe1127 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EnumerableWrapperWeakToStrong.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/EnumerableWrapperWeakToStrong.cs @@ -19,7 +19,7 @@ namespace System.Linq.Parallel /// a weakly typed IEnumerable object, allowing it to be accessed as a strongly typed /// IEnumerable{object}. /// - internal class EnumerableWrapperWeakToStrong : IEnumerable + internal sealed class EnumerableWrapperWeakToStrong : IEnumerable { private readonly IEnumerable _wrappedEnumerable; // The wrapped enumerable object. @@ -35,7 +35,7 @@ internal EnumerableWrapperWeakToStrong(IEnumerable wrappedEnumerable) IEnumerator IEnumerable.GetEnumerator() { - return ((IEnumerable)this).GetEnumerator(); + return ((IEnumerable)this).GetEnumerator(); } public IEnumerator GetEnumerator() @@ -47,7 +47,7 @@ IEnumerator IEnumerable.GetEnumerator() // A wrapper over IEnumerator that provides IEnumerator interface // - private class WrapperEnumeratorWeakToStrong : IEnumerator + private sealed class WrapperEnumeratorWeakToStrong : IEnumerator { private readonly IEnumerator _wrappedEnumerator; // The weakly typed enumerator we've wrapped. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/ParallelEnumerableWrapper.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/ParallelEnumerableWrapper.cs index 3184d6b3ee2b8..861c1c6dac627 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/ParallelEnumerableWrapper.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/ParallelEnumerableWrapper.cs @@ -17,7 +17,7 @@ namespace System.Linq.Parallel /// A simple implementation of the ParallelQuery{object} interface which wraps an /// underlying IEnumerable, such that it can be used in parallel queries. /// - internal class ParallelEnumerableWrapper : ParallelQuery + internal sealed class ParallelEnumerableWrapper : ParallelQuery { private readonly IEnumerable _source; // The wrapped enumerable object. @@ -48,7 +48,7 @@ internal override IEnumerator GetEnumeratorUntyped() /// underlying IEnumerable{T}, such that it can be used in parallel queries. /// /// - internal class ParallelEnumerableWrapper : ParallelQuery + internal sealed class ParallelEnumerableWrapper : ParallelQuery { private readonly IEnumerable _wrappedEnumerable; // The wrapped enumerable object. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RangeEnumerable.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RangeEnumerable.cs index 93a7d7009537a..5e5413d2e9ed0 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RangeEnumerable.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RangeEnumerable.cs @@ -15,7 +15,7 @@ namespace System.Linq.Parallel /// A simple enumerable type that implements the range algorithm. It also supports /// partitioning of the indices by implementing an interface that PLINQ recognizes. /// - internal class RangeEnumerable : ParallelQuery, IParallelPartitionable + internal sealed class RangeEnumerable : ParallelQuery, IParallelPartitionable { private readonly int _from; // Lowest index to include. private readonly int _count; // Number of indices to include. @@ -71,7 +71,7 @@ public override IEnumerator GetEnumerator() // The actual enumerator that walks over the specified range. // - private class RangeEnumerator : QueryOperatorEnumerator + private sealed class RangeEnumerator : QueryOperatorEnumerator { private readonly int _from; // The initial value. private readonly int _count; // How many values to yield. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RepeatEnumerable.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RepeatEnumerable.cs index b5b7081991b10..c56de6ca561cf 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RepeatEnumerable.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Enumerables/RepeatEnumerable.cs @@ -18,7 +18,7 @@ namespace System.Linq.Parallel /// partitioning of the count space by implementing an interface that PLINQ recognizes. /// /// - internal class RepeatEnumerable : ParallelQuery, IParallelPartitionable + internal sealed class RepeatEnumerable : ParallelQuery, IParallelPartitionable { private readonly TResult _element; // Element value to repeat. private readonly int _count; // Count of element values. @@ -77,7 +77,7 @@ public override IEnumerator GetEnumerator() // The actual enumerator that produces a set of repeated elements. // - private class RepeatEnumerator : QueryOperatorEnumerator + private sealed class RepeatEnumerator : QueryOperatorEnumerator { private readonly TResult _element; // The element to repeat. private readonly int _count; // The number of times to repeat it. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Helpers.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Helpers.cs deleted file mode 100644 index 54494258c2ed7..0000000000000 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Helpers.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace System.Linq.Parallel -{ - internal class JaggedArray - { - public static TElement[][] Allocate(int size1, int size2) - { - TElement[][] ret = new TElement[size1][]; - for (int i = 0; i < size1; i++) - ret[i] = new TElement[size2]; - - return ret; - } - } - - // Copied from Linq. - internal class Set - { - private int[] _buckets; - private Slot[] _slots; - private int _count; - private readonly IEqualityComparer _comparer; -#if DEBUG - private bool _haveRemoved; -#endif - - private const int InitialSize = 7; - private const int HashCodeMask = 0x7FFFFFFF; - - public Set(IEqualityComparer? comparer) - { - if (comparer == null) comparer = EqualityComparer.Default; - _comparer = comparer; - _buckets = new int[InitialSize]; - _slots = new Slot[InitialSize]; - } - - // If value is not in set, add it and return true; otherwise return false - public bool Add(TElement value) - { -#if DEBUG - Debug.Assert(!_haveRemoved, "This class is optimised for never calling Add after Remove. If your changes need to do so, undo that optimization."); -#endif - return !Find(value, true); - } - - // Check whether value is in set - public bool Contains(TElement value) - { - return Find(value, false); - } - - // If value is in set, remove it and return true; otherwise return false - public bool Remove(TElement value) - { -#if DEBUG - _haveRemoved = true; -#endif - int hashCode = InternalGetHashCode(value); - int bucket = hashCode % _buckets.Length; - int last = -1; - for (int i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i].next) - { - if (_slots[i].hashCode == hashCode && _comparer.Equals(_slots[i].value, value)) - { - if (last < 0) - { - _buckets[bucket] = _slots[i].next + 1; - } - else - { - _slots[last].next = _slots[i].next; - } - _slots[i].hashCode = -1; - _slots[i].value = default; - _slots[i].next = -1; - return true; - } - } - return false; - } - - private bool Find(TElement value, bool add) - { - int hashCode = InternalGetHashCode(value); - for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].next) - { - if (_slots[i].hashCode == hashCode && _comparer.Equals(_slots[i].value, value)) return true; - } - if (add) - { - if (_count == _slots.Length) Resize(); - int index = _count; - _count++; - int bucket = hashCode % _buckets.Length; - _slots[index].hashCode = hashCode; - _slots[index].value = value; - _slots[index].next = _buckets[bucket] - 1; - _buckets[bucket] = index + 1; - } - return false; - } - - private void Resize() - { - int newSize = checked(_count * 2 + 1); - int[] newBuckets = new int[newSize]; - Slot[] newSlots = new Slot[newSize]; - Array.Copy(_slots, newSlots, _count); - for (int i = 0; i < _count; i++) - { - int bucket = newSlots[i].hashCode % newSize; - newSlots[i].next = newBuckets[bucket] - 1; - newBuckets[bucket] = i + 1; - } - _buckets = newBuckets; - _slots = newSlots; - } - - internal int InternalGetHashCode(TElement value) - { - // Work around comparer implementations that throw when passed null - return (value == null) ? 0 : _comparer.GetHashCode(value) & HashCodeMask; - } - - internal struct Slot - { - internal int hashCode; - internal int next; - internal TElement? value; - } - } -} diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/JaggedArray.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/JaggedArray.cs new file mode 100644 index 0000000000000..14d938caf4756 --- /dev/null +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/JaggedArray.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Linq.Parallel +{ + internal static class JaggedArray + { + public static TElement[][] Allocate(int size1, int size2) + { + TElement[][] ret = new TElement[size1][]; + for (int i = 0; i < size1; i++) + ret[i] = new TElement[size2]; + + return ret; + } + } +} diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/ArrayMergeHelper.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/ArrayMergeHelper.cs index d9dbce59a5413..1be7789b710d1 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/ArrayMergeHelper.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/ArrayMergeHelper.cs @@ -25,7 +25,7 @@ namespace System.Linq.Parallel /// no extra cost for ordering. /// /// - internal class ArrayMergeHelper : IMergeHelper + internal sealed class ArrayMergeHelper : IMergeHelper { private readonly QueryResults _queryResults; // Indexable query results private readonly TInputOutput[] _outputArray; // The output array. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/DefaultMergeHelper.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/DefaultMergeHelper.cs index 3c95f227d448b..c318b3b5954e0 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/DefaultMergeHelper.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/DefaultMergeHelper.cs @@ -23,7 +23,7 @@ namespace System.Linq.Parallel /// /// /// - internal class DefaultMergeHelper : IMergeHelper + internal sealed class DefaultMergeHelper : IMergeHelper { private readonly QueryTaskGroupState _taskGroupState; // State shared among tasks. private readonly PartitionedStream _partitions; // Source partitions. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/MergeExecutor.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/MergeExecutor.cs index 160509eba2f5d..984ae2f014a28 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/MergeExecutor.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/MergeExecutor.cs @@ -23,7 +23,7 @@ namespace System.Linq.Parallel /// order preserving merge, and so forth. /// /// - internal class MergeExecutor : IEnumerable + internal sealed class MergeExecutor : IEnumerable { // Many internal algorithms are parameterized based on the data. The IMergeHelper // is the pluggable interface whose implementations perform those algorithms. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingMergeHelper.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingMergeHelper.cs index eb12e17e2f22a..e79ed5116f594 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingMergeHelper.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingMergeHelper.cs @@ -20,7 +20,7 @@ namespace System.Linq.Parallel /// /// /// - internal class OrderPreservingMergeHelper : IMergeHelper + internal sealed class OrderPreservingMergeHelper : IMergeHelper { private readonly QueryTaskGroupState _taskGroupState; // State shared among tasks. private readonly PartitionedStream _partitions; // Source partitions. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs index 821141f58decc..4153cf39a875c 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Merging/OrderPreservingPipeliningMergeHelper.cs @@ -36,7 +36,7 @@ namespace System.Linq.Parallel /// Finally, if the producer notices that its buffer has exceeded an even greater threshold, it will /// go to sleep and wait until the consumer takes the entire buffer. /// - internal class OrderPreservingPipeliningMergeHelper : IMergeHelper + internal sealed class OrderPreservingPipeliningMergeHelper : IMergeHelper { private readonly QueryTaskGroupState _taskGroupState; // State shared among tasks. private readonly PartitionedStream _partitions; // Source partitions. @@ -134,7 +134,7 @@ internal OrderPreservingPipeliningMergeHelper( if (keyComparer == Util.GetDefaultComparer()) { Debug.Assert(typeof(TKey) == typeof(int)); - _producerComparer = (IComparer>)new ProducerComparerInt(); + _producerComparer = (IComparer>)(object)ProducerComparerInt.Instance; } else { @@ -183,7 +183,7 @@ public TOutput[] GetResultsAsArray() /// x.MaxKey EQUALS y.MaxKey => x EQUALS y => return 0 /// x.MaxKey LESS_THAN y.MaxKey => x GREATER_THAN y => return + /// - private class ProducerComparer : IComparer> + private sealed class ProducerComparer : IComparer> { private readonly IComparer _keyComparer; @@ -201,7 +201,7 @@ public int Compare(Producer x, Producer y) /// /// Enumerator over the results of an order-preserving pipelining merge. /// - private class OrderedPipeliningMergeEnumerator : MergeEnumerator + private sealed class OrderedPipeliningMergeEnumerator : MergeEnumerator { /// @@ -513,8 +513,12 @@ internal Producer(TKey maxKey, int producerIndex) /// x.MaxKey EQUALS y.MaxKey => x EQUALS y => return 0 /// x.MaxKey LESS_THAN y.MaxKey => x GREATER_THAN y => return + /// - internal class ProducerComparerInt : IComparer> + internal sealed class ProducerComparerInt : IComparer> { + public static readonly ProducerComparerInt Instance = new ProducerComparerInt(); + + private ProducerComparerInt() { } + public int Compare(Producer x, Producer y) { Debug.Assert(x.MaxKey >= 0 && y.MaxKey >= 0); // Guarantees no overflow on next line diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/HashRepartitionEnumerator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/HashRepartitionEnumerator.cs index a472528fb8a69..466102746d15a 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/HashRepartitionEnumerator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/HashRepartitionEnumerator.cs @@ -21,7 +21,7 @@ namespace System.Linq.Parallel /// The kind of elements. /// The key used to distribute elements. /// The kind of keys found in the source (ignored). - internal class HashRepartitionEnumerator : QueryOperatorEnumerator, int> + internal sealed class HashRepartitionEnumerator : QueryOperatorEnumerator, int> { private const int ENUMERATION_NOT_STARTED = -1; // Sentinel to note we haven't begun enumerating yet. @@ -36,7 +36,7 @@ internal class HashRepartitionEnumerator : Q private readonly CancellationToken _cancellationToken; // A token for canceling the process. private Mutables? _mutables; // Mutable fields for this enumerator. - private class Mutables + private sealed class Mutables { internal int _currentBufferIndex; // Current buffer index. internal ListChunk>? _currentBuffer; // The buffer we're currently enumerating. @@ -206,7 +206,7 @@ private void EnumerateAndRedistributeElements() while (_source.MoveNext(ref element!, ref ignoreKey)) { if ((loopCount++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Calculate the element's destination partition index, placing it into the // appropriate buffer from which partitions will later enumerate. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionEnumerator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionEnumerator.cs index 595a7b15fdb98..b82e73c276f23 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionEnumerator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionEnumerator.cs @@ -22,7 +22,7 @@ namespace System.Linq.Parallel /// The kind of elements. /// The key used to distribute elements. /// The kind of keys found in the source. - internal class OrderedHashRepartitionEnumerator : QueryOperatorEnumerator, TOrderKey> + internal sealed class OrderedHashRepartitionEnumerator : QueryOperatorEnumerator, TOrderKey> { private const int ENUMERATION_NOT_STARTED = -1; // Sentinel to note we haven't begun enumerating yet. @@ -37,7 +37,7 @@ internal class OrderedHashRepartitionEnumerator>? _currentBuffer; // The buffer we're currently enumerating. @@ -216,7 +216,7 @@ private void EnumerateAndRedistributeElements() while (_source.MoveNext(ref element!, ref key)) { if ((loopCount++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Calculate the element's destination partition index, placing it into the // appropriate buffer from which partitions will later enumerate. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionStream.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionStream.cs index 105164330d02d..a598f1ebbd80c 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionStream.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/OrderedHashRepartitionStream.cs @@ -12,7 +12,7 @@ namespace System.Linq.Parallel { - internal class OrderedHashRepartitionStream : HashRepartitionStream + internal sealed class OrderedHashRepartitionStream : HashRepartitionStream { internal OrderedHashRepartitionStream( PartitionedStream inputStream, Func? hashKeySelector, diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/PartitionedDataSource.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/PartitionedDataSource.cs index 662c8982832d4..1d43e57759141 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/PartitionedDataSource.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/PartitionedDataSource.cs @@ -43,7 +43,7 @@ namespace System.Linq.Parallel /// This is used as the default partitioning strategy by much of the PLINQ infrastructure. /// /// - internal class PartitionedDataSource : PartitionedStream + internal sealed class PartitionedDataSource : PartitionedStream { //--------------------------------------------------------------------------------------- // Just constructs a new partition stream. @@ -219,7 +219,7 @@ internal sealed class ArrayIndexRangeEnumerator : QueryOperatorEnumerator private readonly int _sectionCount; // Precomputed in ctor: the number of sections the range is split into. private Mutables? _mutables; // Lazily allocated mutable variables. - private class Mutables + private sealed class Mutables { internal Mutables() { @@ -569,7 +569,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref T currentE // been written to perform proper synchronization. // - private class ContiguousChunkLazyEnumerator : QueryOperatorEnumerator + private sealed class ContiguousChunkLazyEnumerator : QueryOperatorEnumerator { private const int chunksPerChunkSize = 7; // the rate at which to double the chunksize (double chunksize every 'r' chunks). MUST BE == (2^n)-1 for some n. private readonly IEnumerator _source; // Data source to enumerate. @@ -579,7 +579,7 @@ private class ContiguousChunkLazyEnumerator : QueryOperatorEnumerator private readonly Shared _exceptionTracker; private Mutables? _mutables; // Any mutable fields on this enumerator. These mutables are local and persistent - private class Mutables + private sealed class Mutables { internal Mutables() { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/UnorderedHashRepartitionStream.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/UnorderedHashRepartitionStream.cs index 7089002414ec4..ab9a6096869ad 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/UnorderedHashRepartitionStream.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Partitioning/UnorderedHashRepartitionStream.cs @@ -12,7 +12,7 @@ namespace System.Linq.Parallel { - internal class UnorderedHashRepartitionStream : HashRepartitionStream + internal sealed class UnorderedHashRepartitionStream : HashRepartitionStream { //--------------------------------------------------------------------------------------- // Creates a new partition exchange operator. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/AssociativeAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/AssociativeAggregationOperator.cs index 175aeec2da7d7..bd3fa1f1e427a 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/AssociativeAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/AssociativeAggregationOperator.cs @@ -222,7 +222,7 @@ internal override bool LimitsParallelism // (possibly partitioned) data source. // - private class AssociativeAggregationOperatorEnumerator : QueryOperatorEnumerator + private sealed class AssociativeAggregationOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly AssociativeAggregationOperator _reduceOperator; // The operator. @@ -299,7 +299,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TIntermedi while (_source.MoveNext(ref input!, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); hadNext = true; accumulator = _reduceOperator._intermediateReduce(accumulator, input); } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ConcatQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ConcatQueryOperator.cs index f6b4b72099dc4..f0af4b5dbc7c7 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ConcatQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ConcatQueryOperator.cs @@ -226,7 +226,7 @@ protected override void Dispose(bool disposing) // results were indexable. // - private class ConcatQueryOperatorResults : BinaryQueryOperatorResults + private sealed class ConcatQueryOperatorResults : BinaryQueryOperatorResults { private readonly int _leftChildCount; // The number of elements in the left child result set private readonly int _rightChildCount; // The number of elements in the right child result set @@ -329,7 +329,7 @@ internal static IComparer> MakeComparer( // according to the corresponding order key. // - private class ConcatKeyComparer : IComparer> + private sealed class ConcatKeyComparer : IComparer> { private readonly IComparer _leftComparer; private readonly IComparer _rightComparer; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ExceptQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ExceptQueryOperator.cs index 3df3f4ebb8500..0c86a0c18ca67 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ExceptQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ExceptQueryOperator.cs @@ -134,12 +134,12 @@ internal override bool LimitsParallelism // elements that have not yet been seen. // - private class ExceptQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class ExceptQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TLeftKey> _leftSource; // Left data source. private readonly QueryOperatorEnumerator, int> _rightSource; // Right data source. private readonly IEqualityComparer? _comparer; // A comparer used for equality checks/hash-coding. - private Set? _hashLookup; // The hash lookup, used to produce the distinct set. + private HashSet? _hashLookup; // The hash lookup, used to produce the distinct set. private readonly CancellationToken _cancellationToken; private Shared? _outputLoopCount; @@ -177,7 +177,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp { _outputLoopCount = new Shared(0); - _hashLookup = new Set(_comparer); + _hashLookup = new HashSet(_comparer); Pair rightElement = default(Pair); int rightKeyUnused = default(int); @@ -186,7 +186,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_rightSource.MoveNext(ref rightElement, ref rightKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); _hashLookup.Add(rightElement.First); } @@ -200,7 +200,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_leftSource.MoveNext(ref leftElement, ref leftKeyUnused)) { if ((_outputLoopCount.Value++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (_hashLookup.Add(leftElement.First)) { @@ -224,7 +224,7 @@ protected override void Dispose(bool disposing) } } - private class OrderedExceptQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class OrderedExceptQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TLeftKey> _leftSource; // Left data source. private readonly QueryOperatorEnumerator, int> _rightSource; // Right data source. @@ -265,7 +265,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp // Build the set out of the left data source, if we haven't already. if (_outputEnumerator == null) { - Set rightLookup = new Set(_comparer); + HashSet rightLookup = new HashSet(_comparer); Pair rightElement = default(Pair); int rightKeyUnused = default(int); @@ -273,7 +273,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_rightSource.MoveNext(ref rightElement, ref rightKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); rightLookup.Add(rightElement.First); } @@ -287,7 +287,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_leftSource.MoveNext(ref leftElement, ref leftKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (rightLookup.Contains(leftElement.First)) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/GroupJoinQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/GroupJoinQueryOperator.cs index 58a2f557e952e..7c07b51b24e2d 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/GroupJoinQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/GroupJoinQueryOperator.cs @@ -214,7 +214,7 @@ internal override bool LimitsParallelism /// /// /// - internal class GroupJoinHashLookupBuilder : HashLookupBuilder, int, THashKey> + internal sealed class GroupJoinHashLookupBuilder : HashLookupBuilder, int, THashKey> { private readonly QueryOperatorEnumerator, TOrderKey> _dataSource; // data source. For building. private readonly IEqualityComparer? _keyComparer; // An optional key comparison object. @@ -276,7 +276,7 @@ public bool Add(THashKey hashKey, TElement element, TOrderKey orderKey) /// /// The order key is a dummy value since we are unordered. /// - private class GroupJoinHashLookup : GroupJoinHashLookup, int> + private sealed class GroupJoinHashLookup : GroupJoinHashLookup, int> { private const int OrderKey = unchecked((int)0xdeadbeef); @@ -384,7 +384,7 @@ public bool Add(THashKey hashKey, TElement element, TOrderKey orderKey) /// /// The order key is wrapped so that empty lists can be treated as less than all non-empty lists. /// - private class OrderedGroupJoinHashLookup : GroupJoinHashLookup> + private sealed class OrderedGroupJoinHashLookup : GroupJoinHashLookup> { internal OrderedGroupJoinHashLookup(HashLookup lookup) : base(lookup) @@ -407,7 +407,7 @@ private Pair Wrap(TOrderKey orderKey) /// /// A structure to hold both the elements that match a hash key and an order key for the grouping. /// - private class GroupKeyData + private sealed class GroupKeyData { internal TOrderKey _orderKey; internal OrderedGroupByGrouping _grouping; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/HashJoinQueryOperatorEnumerator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/HashJoinQueryOperatorEnumerator.cs index 8055046879846..fe1114e7ed766 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/HashJoinQueryOperatorEnumerator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/HashJoinQueryOperatorEnumerator.cs @@ -35,7 +35,7 @@ namespace System.Linq.Parallel /// /// /// - internal class HashJoinQueryOperatorEnumerator + internal sealed class HashJoinQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TLeftKey> _leftSource; // Left (outer) data source. For probing. @@ -45,7 +45,7 @@ internal class HashJoinQueryOperatorEnumerator matchValue = default(HashLookupValueList); @@ -200,7 +200,7 @@ internal abstract class HashJoinOutputKeyBuilder /// /// - internal class LeftKeyOutputKeyBuilder : HashJoinOutputKeyBuilder + internal sealed class LeftKeyOutputKeyBuilder : HashJoinOutputKeyBuilder { public override TLeftKey Combine(TLeftKey leftKey, TRightKey rightKey) { @@ -215,7 +215,7 @@ public override TLeftKey Combine(TLeftKey leftKey, TRightKey rightKey) /// /// /// - internal class PairOutputKeyBuilder : HashJoinOutputKeyBuilder> + internal sealed class PairOutputKeyBuilder : HashJoinOutputKeyBuilder> { public override Pair Combine(TLeftKey leftKey, TRightKey rightKey) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/IntersectQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/IntersectQueryOperator.cs index 3b6230f441992..6eecc4a3bc219 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/IntersectQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/IntersectQueryOperator.cs @@ -123,12 +123,12 @@ internal override bool LimitsParallelism // only returns elements that are seen twice (returning each one only once). // - private class IntersectQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class IntersectQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TLeftKey> _leftSource; // Left data source. private readonly QueryOperatorEnumerator, int> _rightSource; // Right data source. private readonly IEqualityComparer? _comparer; // Comparer to use for equality/hash-coding. - private Set? _hashLookup; // The hash lookup, used to produce the intersection. + private HashSet? _hashLookup; // The hash lookup, used to produce the intersection. private readonly CancellationToken _cancellationToken; private Shared? _outputLoopCount; @@ -164,7 +164,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp if (_hashLookup == null) { _outputLoopCount = new Shared(0); - _hashLookup = new Set(_comparer); + _hashLookup = new HashSet(_comparer); Pair rightElement = default(Pair); int rightKeyUnused = default(int); @@ -173,7 +173,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_rightSource.MoveNext(ref rightElement, ref rightKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); _hashLookup.Add(rightElement.First); } @@ -187,7 +187,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp { Debug.Assert(_outputLoopCount != null); if ((_outputLoopCount.Value++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // If we found the element in our set, and if we haven't returned it yet, // we can yield it to the caller. We also mark it so we know we've returned @@ -225,7 +225,7 @@ internal override IEnumerable AsSequentialQuery(CancellationToken } - private class OrderedIntersectQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class OrderedIntersectQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TLeftKey> _leftSource; // Left data source. private readonly QueryOperatorEnumerator, int> _rightSource; // Right data source. @@ -274,7 +274,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_leftSource.MoveNext(ref leftElement, ref leftKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // For each element, we track the smallest order key for that element that we saw so far Pair oldEntry; @@ -298,7 +298,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_rightSource.MoveNext(ref rightElement, ref rightKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // If we found the element in our set, and if we haven't returned it yet, // we can yield it to the caller. We also mark it so we know we've returned diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/JoinQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/JoinQueryOperator.cs index 56f20040e320d..b603375af3273 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/JoinQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/JoinQueryOperator.cs @@ -191,7 +191,7 @@ internal override bool LimitsParallelism /// /// /// - internal class JoinHashLookupBuilder : HashLookupBuilder + internal sealed class JoinHashLookupBuilder : HashLookupBuilder { private readonly QueryOperatorEnumerator, TOrderKey> _dataSource; // data source. For building. private readonly IEqualityComparer? _keyComparer; // An optional key comparison object. @@ -262,7 +262,7 @@ public bool Add(THashKey hashKey, TElement element, TOrderKey orderKey) /// /// Since Join operations do not require a default, this just passes the call on to the base lookup. /// - private class JoinHashLookup : HashJoinHashLookup + private sealed class JoinHashLookup : HashJoinHashLookup { private readonly HashLookup> _base; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/UnionQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/UnionQueryOperator.cs index 1d6cccfcef6e9..a9136f01a3697 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/UnionQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/UnionQueryOperator.cs @@ -180,11 +180,11 @@ internal override bool LimitsParallelism // return any duplicates. // - private class UnionQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class UnionQueryOperatorEnumerator : QueryOperatorEnumerator { private QueryOperatorEnumerator, TLeftKey>? _leftSource; // Left data source. private QueryOperatorEnumerator, TRightKey>? _rightSource; // Right data source. - private Set? _hashLookup; // The hash lookup, used to produce the union. + private HashSet? _hashLookup; // The hash lookup, used to produce the union. private readonly CancellationToken _cancellationToken; private Shared? _outputLoopCount; private readonly IEqualityComparer? _comparer; @@ -216,7 +216,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp { if (_hashLookup == null) { - _hashLookup = new Set(_comparer); + _hashLookup = new HashSet(_comparer); _outputLoopCount = new Shared(0); } @@ -234,7 +234,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_leftSource.MoveNext(ref currentLeftElement, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // We ensure we never return duplicates by tracking them in our set. if (_hashLookup.Add(currentLeftElement.First)) @@ -262,7 +262,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp { Debug.Assert(_outputLoopCount != null); if ((_outputLoopCount.Value++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // We ensure we never return duplicates by tracking them in our set. if (_hashLookup.Add(currentRightElement.First)) @@ -295,7 +295,7 @@ protected override void Dispose(bool disposing) } } - private class OrderedUnionQueryOperatorEnumerator : QueryOperatorEnumerator> + private sealed class OrderedUnionQueryOperatorEnumerator : QueryOperatorEnumerator> { private readonly QueryOperatorEnumerator, TLeftKey> _leftSource; // Left data source. private readonly QueryOperatorEnumerator, TRightKey> _rightSource; // Right data source. @@ -357,7 +357,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_leftSource.MoveNext(ref elem, ref leftKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); ConcatKey key = ConcatKey.MakeLeft(_leftOrdered ? leftKey : default); @@ -374,7 +374,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_rightSource.MoveNext(ref elem, ref rightKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); ConcatKey key = ConcatKey.MakeRight(_rightOrdered ? rightKey : default); diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ZipQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ZipQueryOperator.cs index 62d3ce0e6be73..3a170f0d72c40 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ZipQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Binary/ZipQueryOperator.cs @@ -159,7 +159,7 @@ internal override bool LimitsParallelism // QueryResults are indexable. // - internal class ZipQueryOperatorResults : QueryResults + internal sealed class ZipQueryOperatorResults : QueryResults { private readonly QueryResults _leftChildResults; private readonly QueryResults _rightChildResults; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/BinaryQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/BinaryQueryOperator.cs index c38a783944bee..dea0f4029c946 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/BinaryQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/BinaryQueryOperator.cs @@ -134,7 +134,7 @@ internal override void GivePartitionedStream(IPartitionedStreamRecipient + private sealed class LeftChildResultsRecipient : IPartitionedStreamRecipient { private readonly IPartitionedStreamRecipient _outputRecipient; private readonly BinaryQueryOperatorResults _results; @@ -167,7 +167,7 @@ public void Receive(PartitionedStream source) // WrapPartitionedStream method. // - private class RightChildResultsRecipient : IPartitionedStreamRecipient + private sealed class RightChildResultsRecipient : IPartitionedStreamRecipient { private readonly IPartitionedStreamRecipient _outputRecipient; private readonly PartitionedStream _leftPartitionedStream; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/CountAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/CountAggregationOperator.cs index fe4a7663eb8af..2a0565aa0b742 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/CountAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/CountAggregationOperator.cs @@ -74,7 +74,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class CountAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class CountAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -109,7 +109,7 @@ protected override bool MoveNextCore(ref int currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { count++; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalAverageAggregationOperator.cs index 5fe8052657e93..42815cf987313 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalAverageAggregationOperator.cs @@ -81,7 +81,7 @@ protected override QueryOperatorEnumerator, int> CreateEnume // (possibly partitioned) data source. // - private class DecimalAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class DecimalAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -118,7 +118,7 @@ protected override bool MoveNextCore(ref Pair currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalMinMaxAggregationOperator.cs index 953283aeb800d..525672e5f5319 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalMinMaxAggregationOperator.cs @@ -98,7 +98,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class DecimalMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class DecimalMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -137,7 +137,7 @@ protected override bool MoveNextCore(ref decimal currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem < currentElement) { @@ -151,7 +151,7 @@ protected override bool MoveNextCore(ref decimal currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem > currentElement) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalSumAggregationOperator.cs index 0684d155a5c0f..1f48d181806a9 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DecimalSumAggregationOperator.cs @@ -68,7 +68,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class DecimalSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class DecimalSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -103,7 +103,7 @@ protected override bool MoveNextCore(ref decimal currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); tempSum += element; } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleAverageAggregationOperator.cs index e344f06de663f..2983021b35440 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleAverageAggregationOperator.cs @@ -82,7 +82,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumer // (possibly partitioned) data source. // - private class DoubleAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class DoubleAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -119,7 +119,7 @@ protected override bool MoveNextCore(ref Pair currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleMinMaxAggregationOperator.cs index 15461d383e075..f83c915f4d2e3 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleMinMaxAggregationOperator.cs @@ -105,7 +105,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class DoubleMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class DoubleMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -144,7 +144,7 @@ protected override bool MoveNextCore(ref double currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem < currentElement || double.IsNaN(elem)) { @@ -158,7 +158,7 @@ protected override bool MoveNextCore(ref double currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem > currentElement || double.IsNaN(currentElement)) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleSumAggregationOperator.cs index e6dbad5a04c8c..a815405b18a87 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/DoubleSumAggregationOperator.cs @@ -68,7 +68,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class DoubleSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class DoubleSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -103,7 +103,7 @@ protected override bool MoveNextCore(ref double currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); tempSum += element; } while (source.MoveNext(ref element, ref keyUnused)); diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatAverageAggregationOperator.cs index 90ce556e6bb01..dd4e31c9b4c67 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatAverageAggregationOperator.cs @@ -82,7 +82,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumer // (possibly partitioned) data source. // - private class FloatAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class FloatAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -119,7 +119,7 @@ protected override bool MoveNextCore(ref Pair currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatMinMaxAggregationOperator.cs index 8fbcf61b5aed8..faf8a03b48eee 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatMinMaxAggregationOperator.cs @@ -103,7 +103,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class FloatMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class FloatMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -142,7 +142,7 @@ protected override bool MoveNextCore(ref float currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem < currentElement || float.IsNaN(elem)) { currentElement = elem; @@ -155,7 +155,7 @@ protected override bool MoveNextCore(ref float currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem > currentElement || float.IsNaN(currentElement)) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatSumAggregationOperator.cs index 5db981536f1d2..88bb4be313ff8 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/FloatSumAggregationOperator.cs @@ -69,7 +69,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class FloatSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class FloatSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -104,7 +104,7 @@ protected override bool MoveNextCore(ref double currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); tempSum += element; } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntAverageAggregationOperator.cs index 0a523082169b8..a5121c840c289 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntAverageAggregationOperator.cs @@ -81,7 +81,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumerat // (possibly partitioned) data source. // - private class IntAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class IntAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -118,7 +118,7 @@ protected override bool MoveNextCore(ref Pair currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntMinMaxAggregationOperator.cs index ae0e6d7a58c39..02e7bf56db117 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntMinMaxAggregationOperator.cs @@ -98,7 +98,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class IntMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class IntMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -137,7 +137,7 @@ protected override bool MoveNextCore(ref int currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem < currentElement) { @@ -151,7 +151,7 @@ protected override bool MoveNextCore(ref int currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem > currentElement) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntSumAggregationOperator.cs index a9b42ebfa9bb8..2f2b2bcbf6687 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/IntSumAggregationOperator.cs @@ -72,7 +72,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class IntSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class IntSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -107,7 +107,7 @@ protected override bool MoveNextCore(ref int currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongAverageAggregationOperator.cs index e18e27fd1ac04..fdb729a2ee9be 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongAverageAggregationOperator.cs @@ -81,7 +81,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumerat // (possibly partitioned) data source. // - private class LongAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class LongAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -118,7 +118,7 @@ protected override bool MoveNextCore(ref Pair currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongCountAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongCountAggregationOperator.cs index e9ad61c8a8db8..af3ff71e6888f 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongCountAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongCountAggregationOperator.cs @@ -72,7 +72,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class LongCountAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class LongCountAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -107,7 +107,7 @@ protected override bool MoveNextCore(ref long currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongMinMaxAggregationOperator.cs index ed9048134df70..713381adce7d9 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongMinMaxAggregationOperator.cs @@ -99,7 +99,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class LongMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class LongMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -138,7 +138,7 @@ protected override bool MoveNextCore(ref long currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem < currentElement) { @@ -152,7 +152,7 @@ protected override bool MoveNextCore(ref long currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem > currentElement) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongSumAggregationOperator.cs index d8fa4b98b1586..4eb7148900b05 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/LongSumAggregationOperator.cs @@ -71,7 +71,7 @@ protected override QueryOperatorEnumerator CreateEnumerator( // (possibly partitioned) data source. // - private class LongSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class LongSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -106,7 +106,7 @@ protected override bool MoveNextCore(ref long currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalAverageAggregationOperator.cs index b867776d6f172..20e7b10e6e626 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalAverageAggregationOperator.cs @@ -80,7 +80,7 @@ protected override QueryOperatorEnumerator, int> CreateEnume // (possibly partitioned) data source. // - private class NullableDecimalAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class NullableDecimalAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -114,7 +114,7 @@ protected override bool MoveNextCore(ref Pair currentElement) while (source.MoveNext(ref current, ref currentKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (current.HasValue) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalMinMaxAggregationOperator.cs index 9bbf531d24a82..5820ee653991f 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalMinMaxAggregationOperator.cs @@ -97,7 +97,7 @@ internal NullableDecimalMinMaxAggregationOperator(IEnumerable child, i // (possibly partitioned) data source. // - private class NullableDecimalMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableDecimalMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -136,7 +136,7 @@ protected override bool MoveNextCore(ref decimal? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (currentElement == null || elem < currentElement) { @@ -150,7 +150,7 @@ protected override bool MoveNextCore(ref decimal? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (currentElement == null || elem > currentElement) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalSumAggregationOperator.cs index 47b778cb3832e..91f9c79cf2e8e 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDecimalSumAggregationOperator.cs @@ -68,7 +68,7 @@ internal NullableDecimalSumAggregationOperator(IEnumerable child) : ba // (possibly partitioned) data source. // - private class NullableDecimalSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableDecimalSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -103,7 +103,7 @@ protected override bool MoveNextCore(ref decimal? currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); tempSum += element.GetValueOrDefault(); } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleAverageAggregationOperator.cs index 5efdefa7517b8..6efd863e7306e 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleAverageAggregationOperator.cs @@ -80,7 +80,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumer // (possibly partitioned) data source. // - private class NullableDoubleAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class NullableDoubleAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -116,7 +116,7 @@ protected override bool MoveNextCore(ref Pair currentElement) if (current.HasValue) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleMinMaxAggregationOperator.cs index 5c3ca70b5a3ec..40c8a20f3f79c 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleMinMaxAggregationOperator.cs @@ -105,7 +105,7 @@ internal NullableDoubleMinMaxAggregationOperator(IEnumerable child, int // (possibly partitioned) data source. // - private class NullableDoubleMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableDoubleMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -144,7 +144,7 @@ protected override bool MoveNextCore(ref double? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem == null) continue; if (currentElement == null || elem < currentElement || double.IsNaN(elem.GetValueOrDefault())) @@ -159,7 +159,7 @@ protected override bool MoveNextCore(ref double? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem == null) continue; if (currentElement == null || elem > currentElement || double.IsNaN(currentElement.GetValueOrDefault())) diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleSumAggregationOperator.cs index 8bb4f30427ea8..3ab2196d8ea86 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableDoubleSumAggregationOperator.cs @@ -68,7 +68,7 @@ internal NullableDoubleSumAggregationOperator(IEnumerable child) : base // (possibly partitioned) data source. // - private class NullableDoubleSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableDoubleSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -103,7 +103,7 @@ protected override bool MoveNextCore(ref double? currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); tempSum += element.GetValueOrDefault(); } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatAverageAggregationOperator.cs index 700560c760548..cee9a7fc97d9e 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatAverageAggregationOperator.cs @@ -80,7 +80,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumer // (possibly partitioned) data source. // - private class NullableFloatAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class NullableFloatAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -115,7 +115,7 @@ protected override bool MoveNextCore(ref Pair currentElement) while (source.MoveNext(ref current, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (current.HasValue) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatMinMaxAggregationOperator.cs index 140e30f34bcf5..66fa9b9ef6d82 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatMinMaxAggregationOperator.cs @@ -105,7 +105,7 @@ internal NullableFloatMinMaxAggregationOperator(IEnumerable child, int s // (possibly partitioned) data source. // - private class NullableFloatMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableFloatMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -144,7 +144,7 @@ protected override bool MoveNextCore(ref float? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem == null) continue; if (currentElement == null || elem < currentElement || float.IsNaN(elem.GetValueOrDefault())) @@ -159,7 +159,7 @@ protected override bool MoveNextCore(ref float? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (elem == null) continue; if (currentElement == null || elem > currentElement || float.IsNaN(currentElement.GetValueOrDefault())) diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatSumAggregationOperator.cs index 5d5e529f323f6..f4d66083de744 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableFloatSumAggregationOperator.cs @@ -68,7 +68,7 @@ internal NullableFloatSumAggregationOperator(IEnumerable child) : base(c // (possibly partitioned) data source. // - private class NullableFloatSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableFloatSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -103,7 +103,7 @@ protected override bool MoveNextCore(ref double? currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); tempSum += element.GetValueOrDefault(); } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntAverageAggregationOperator.cs index 26e16c2aaaf71..065cca0dd0596 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntAverageAggregationOperator.cs @@ -80,7 +80,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumerat // (possibly partitioned) data source. // - private class NullableIntAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class NullableIntAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -115,7 +115,7 @@ protected override bool MoveNextCore(ref Pair currentElement) while (source.MoveNext(ref current, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (current.HasValue) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntMinMaxAggregationOperator.cs index 4b14a274484bc..5d24040aeaf56 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntMinMaxAggregationOperator.cs @@ -97,7 +97,7 @@ internal NullableIntMinMaxAggregationOperator(IEnumerable child, int sign) // (possibly partitioned) data source. // - private class NullableIntMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableIntMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -136,7 +136,7 @@ protected override bool MoveNextCore(ref int? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (currentElement == null || elem < currentElement) { @@ -150,7 +150,7 @@ protected override bool MoveNextCore(ref int? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (currentElement == null || elem > currentElement) { currentElement = elem; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntSumAggregationOperator.cs index 74fbb270fbb9d..f537e541e8580 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableIntSumAggregationOperator.cs @@ -71,7 +71,7 @@ internal NullableIntSumAggregationOperator(IEnumerable child) : base(child // (possibly partitioned) data source. // - private class NullableIntSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableIntSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -106,7 +106,7 @@ protected override bool MoveNextCore(ref int? currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { tempSum += element.GetValueOrDefault(); diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongAverageAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongAverageAggregationOperator.cs index c1dde8395074a..b243a005537df 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongAverageAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongAverageAggregationOperator.cs @@ -81,7 +81,7 @@ protected override QueryOperatorEnumerator, int> CreateEnumerat // (possibly partitioned) data source. // - private class NullableLongAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> + private sealed class NullableLongAverageAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator> { private readonly QueryOperatorEnumerator _source; // The source data. @@ -116,7 +116,7 @@ protected override bool MoveNextCore(ref Pair currentElement) while (source.MoveNext(ref current, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (current.HasValue) { checked diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongMinMaxAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongMinMaxAggregationOperator.cs index e33f93a6bd1b7..92e47e9c2d536 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongMinMaxAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongMinMaxAggregationOperator.cs @@ -98,7 +98,7 @@ internal NullableLongMinMaxAggregationOperator(IEnumerable child, int sig // (possibly partitioned) data source. // - private class NullableLongMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableLongMinMaxAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max). @@ -137,7 +137,7 @@ protected override bool MoveNextCore(ref long? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (currentElement == null || elem < currentElement) { @@ -151,7 +151,7 @@ protected override bool MoveNextCore(ref long? currentElement) while (source.MoveNext(ref elem, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (currentElement == null || elem > currentElement) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongSumAggregationOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongSumAggregationOperator.cs index f5e98d13c82c2..d66b4e3e2dbae 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongSumAggregationOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Inlined/NullableLongSumAggregationOperator.cs @@ -72,7 +72,7 @@ internal NullableLongSumAggregationOperator(IEnumerable child) : base(chi // (possibly partitioned) data source. // - private class NullableLongSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator + private sealed class NullableLongSumAggregationOperatorEnumerator : InlinedAggregationOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. @@ -107,7 +107,7 @@ protected override bool MoveNextCore(ref long? currentElement) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); checked { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ListQueryResults.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ListQueryResults.cs index 78a20d6dc0def..02eb817eb4f87 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ListQueryResults.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ListQueryResults.cs @@ -15,7 +15,7 @@ namespace System.Linq.Parallel /// Class to represent an IList{T} as QueryResults{T} /// /// - internal class ListQueryResults : QueryResults + internal sealed class ListQueryResults : QueryResults { private readonly IList _source; private readonly int _partitionCount; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Options/QueryExecutionOption.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Options/QueryExecutionOption.cs index c4764e30772aa..37467935b1e77 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Options/QueryExecutionOption.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Options/QueryExecutionOption.cs @@ -16,7 +16,7 @@ namespace System.Linq.Parallel /// Represents operators that set various query execution options. /// /// - internal class QueryExecutionOption : QueryOperator + internal sealed class QueryExecutionOption : QueryOperator { private readonly QueryOperator _child; private readonly OrdinalIndexState _indexState; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionedStreamMerger.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionedStreamMerger.cs index 2a4632720f0fb..65275582d630a 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionedStreamMerger.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionedStreamMerger.cs @@ -15,7 +15,7 @@ namespace System.Linq.Parallel /// /// Partitioned stream recipient that will merge the results. /// - internal class PartitionedStreamMerger : IPartitionedStreamRecipient + internal sealed class PartitionedStreamMerger : IPartitionedStreamRecipient { private readonly bool _forEffectMerge; private readonly ParallelMergeOptions _mergeOptions; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionerQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionerQueryOperator.cs index 77c7f18c1c9dc..9ea85ac61d933 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionerQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/PartitionerQueryOperator.cs @@ -22,7 +22,7 @@ namespace System.Linq.Parallel /// /// A QueryOperator that represents the output of the query partitioner.AsParallel(). /// - internal class PartitionerQueryOperator : QueryOperator + internal sealed class PartitionerQueryOperator : QueryOperator { private readonly Partitioner _partitioner; // The partitioner to use as data source. @@ -113,7 +113,7 @@ internal override bool LimitsParallelism /// /// QueryResults for a PartitionerQueryOperator /// - private class PartitionerQueryOperatorResults : QueryResults + private sealed class PartitionerQueryOperatorResults : QueryResults { private readonly Partitioner _partitioner; // The data source for the query @@ -204,7 +204,7 @@ internal override void GivePartitionedStream(IPartitionedStreamRecipient - private class OrderablePartitionerEnumerator : QueryOperatorEnumerator + private sealed class OrderablePartitionerEnumerator : QueryOperatorEnumerator { private readonly IEnumerator> _sourceEnumerator; @@ -239,7 +239,7 @@ protected override void Dispose(bool disposing) /// Enumerator that converts an enumerator over key-value pairs exposed by a partitioner /// to a QueryOperatorEnumerator used by PLINQ internally. /// - private class PartitionerEnumerator : QueryOperatorEnumerator + private sealed class PartitionerEnumerator : QueryOperatorEnumerator { private readonly IEnumerator _sourceEnumerator; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOpeningEnumerator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOpeningEnumerator.cs index edf520c91901f..b85e3fb3a30ed 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOpeningEnumerator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOpeningEnumerator.cs @@ -20,7 +20,7 @@ namespace System.Linq.Parallel /// lazily because once GetOpenedEnumerator() is called, PLINQ starts precomputing the /// results of the query. /// - internal class QueryOpeningEnumerator : IEnumerator + internal sealed class QueryOpeningEnumerator : IEnumerator { private readonly QueryOperator _queryOperator; private IEnumerator? _openedQueryEnumerator; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOperatorEnumerator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOperatorEnumerator.cs index 9f99e5e4c8d8b..d5e4829e8c065 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOperatorEnumerator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/QueryOperatorEnumerator.cs @@ -50,7 +50,7 @@ internal IEnumerator AsClassicEnumerator() return new QueryOperatorClassicEnumerator(this); } - private class QueryOperatorClassicEnumerator : IEnumerator + private sealed class QueryOperatorClassicEnumerator : IEnumerator { private QueryOperatorEnumerator _operatorEnumerator; private TElement _current = default!; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ScanQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ScanQueryOperator.cs index 2b1e8a90a1305..6ce92ac0dbeed 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ScanQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/ScanQueryOperator.cs @@ -114,7 +114,7 @@ internal override bool LimitsParallelism get { return false; } } - private class ScanEnumerableQueryOperatorResults : QueryResults + private sealed class ScanEnumerableQueryOperatorResults : QueryResults { private readonly IEnumerable _data; // The data source for the query diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/AnyAllSearchOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/AnyAllSearchOperator.cs index 8de23e6b30b06..48a8abc01ef45 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/AnyAllSearchOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/AnyAllSearchOperator.cs @@ -146,7 +146,7 @@ internal override bool LimitsParallelism // requested. // - private class AnyAllSearchOperatorEnumerator : QueryOperatorEnumerator + private sealed class AnyAllSearchOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly Func _predicate; // The predicate. @@ -204,7 +204,7 @@ internal override bool MoveNext(ref bool currentElement, ref int currentKey) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (_resultFoundFlag.Value) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ContainsSearchOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ContainsSearchOperator.cs index edeee736a930d..eb7464f2193d0 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ContainsSearchOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ContainsSearchOperator.cs @@ -132,7 +132,7 @@ internal override bool LimitsParallelism // requested. // - private class ContainsSearchOperatorEnumerator : QueryOperatorEnumerator + private sealed class ContainsSearchOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly TInput _searchValue; // The value for which we are searching. @@ -189,7 +189,7 @@ internal override bool MoveNext(ref bool currentElement, ref int currentKey) do { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (_resultFoundFlag.Value) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DefaultIfEmptyQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DefaultIfEmptyQueryOperator.cs index a60892dc1edaf..31f1a24f95192 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DefaultIfEmptyQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DefaultIfEmptyQueryOperator.cs @@ -98,7 +98,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the default-if-empty operation. // - private class DefaultIfEmptyQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class DefaultIfEmptyQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private bool _lookedForEmpty; // Whether this partition has looked for empty yet. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DistinctQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DistinctQueryOperator.cs index 4608cecd5c2e5..27afe76c95540 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DistinctQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/DistinctQueryOperator.cs @@ -116,10 +116,10 @@ internal override bool LimitsParallelism // then doesn't return elements it has already seen before. // - private class DistinctQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class DistinctQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TKey> _source; // The data source. - private readonly Set _hashLookup; // The hash lookup, used to produce the distinct set. + private readonly HashSet _hashLookup; // The hash lookup, used to produce the distinct set. private readonly CancellationToken _cancellationToken; private Shared? _outputLoopCount; // Allocated in MoveNext to avoid false sharing. @@ -133,7 +133,7 @@ internal DistinctQueryOperatorEnumerator( { Debug.Assert(source != null); _source = source; - _hashLookup = new Set(comparer); + _hashLookup = new HashSet(comparer); _cancellationToken = cancellationToken; } @@ -156,7 +156,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_source.MoveNext(ref current, ref keyUnused)) { if ((_outputLoopCount.Value++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // We ensure we never return duplicates by tracking them in our set. if (_hashLookup.Add(current.First)) @@ -189,7 +189,7 @@ internal override IEnumerable AsSequentialQuery(CancellationToken return wrappedChild.Distinct(_comparer); } - private class OrderedDistinctQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class OrderedDistinctQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator, TKey> _source; // The data source. private readonly Dictionary, TKey> _hashLookup; // The hash lookup, used to produce the distinct set. @@ -234,7 +234,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_source.MoveNext(ref elem, ref orderKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // For each element, we track the smallest order key for that element that we saw so far TKey oldEntry; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ElementAtQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ElementAtQueryOperator.cs index a5334ddb2be96..05c275d15ad53 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ElementAtQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ElementAtQueryOperator.cs @@ -164,7 +164,7 @@ internal bool Aggregate([MaybeNullWhen(false)] out TSource result, bool withDefa // This enumerator performs the search for the element at the specified index. // - private class ElementAtQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class ElementAtQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The source data. private readonly int _index; // The index of the element to seek. @@ -201,7 +201,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TSource cu while (_source.MoveNext(ref currentElement!, ref currentKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (_resultFoundFlag.Value) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/FirstQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/FirstQueryOperator.cs index f3f9845135130..e7b1e1c94a0c7 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/FirstQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/FirstQueryOperator.cs @@ -116,7 +116,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the first operation. // - private class FirstQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class FirstQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func? _predicate; // The optional predicate used during the search. @@ -176,7 +176,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TSource cu while (_source.MoveNext(ref value!, ref key)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // If the predicate is null or the current element satisfies it, we have found the // current partition's "candidate" for the first element. Note it. @@ -231,7 +231,7 @@ protected override void Dispose(bool disposing) } } - private class FirstQueryOperatorState + private sealed class FirstQueryOperatorState { internal TKey _key = default!; internal int _partitionId = -1; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ForAllOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ForAllOperator.cs index 4afdfc4402ecb..67dac75447cd8 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ForAllOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ForAllOperator.cs @@ -121,7 +121,7 @@ internal override bool LimitsParallelism // partition is walked, invoking the per-element action for each item. // - private class ForAllEnumerator : QueryOperatorEnumerator + private sealed class ForAllEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source. private readonly Action _elementAction; // Forall operator being executed. @@ -161,7 +161,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInput cur while (_source.MoveNext(ref element, ref keyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); _elementAction(element); } diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/GroupByQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/GroupByQueryOperator.cs index 92f2a357da1eb..0b373c5ea9a0f 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/GroupByQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/GroupByQueryOperator.cs @@ -226,7 +226,7 @@ internal abstract class GroupByQueryOperatorEnumerator, ListChunk>? _hashLookup; // The lookup with key-value mappings. internal int _hashLookupIndex; // The current index within the lookup. @@ -328,7 +328,7 @@ protected override HashLookup, ListChunk> BuildHashL while (_source.MoveNext(ref sourceElement, ref sourceKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Generate a key and place it into the hashtable. Wrapper key = new Wrapper(sourceElement.Second); @@ -390,7 +390,7 @@ protected override HashLookup, ListChunk> BuildHash while (_source.MoveNext(ref sourceElement, ref sourceKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Generate a key and place it into the hashtable. Wrapper key = new Wrapper(sourceElement.Second); @@ -429,7 +429,7 @@ internal abstract class OrderedGroupByQueryOperatorEnumerator, GroupKeyData>? _hashLookup; // The lookup with key-value mappings. internal int _hashLookupIndex; // The current index within the lookup. @@ -557,7 +557,7 @@ protected override HashLookup, GroupKeyData> BuildHashLookup( while (_source.MoveNext(ref sourceElement, ref sourceOrderKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Generate a key and place it into the hashtable. Wrapper key = new Wrapper(sourceElement.Second); @@ -632,7 +632,7 @@ protected override HashLookup, GroupKeyData> BuildHashLookup( while (_source.MoveNext(ref sourceElement, ref sourceOrderKey)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Generate a key and place it into the hashtable. Wrapper key = new Wrapper(sourceElement.Second); @@ -676,7 +676,7 @@ protected override HashLookup, GroupKeyData> BuildHashLookup( // key-to-many-values mapping. // - internal class GroupByGrouping : IGrouping + internal sealed class GroupByGrouping : IGrouping { private readonly KeyValuePair, ListChunk> _keyValues; // A key value pair. @@ -722,7 +722,7 @@ IEnumerator IEnumerable.GetEnumerator() /// An ordered version of the grouping data structure. Represents an ordered group of elements that /// have the same grouping key. /// - internal class OrderedGroupByGrouping : IGrouping + internal sealed class OrderedGroupByGrouping : IGrouping { private const int INITIAL_CHUNK_SIZE = 2; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedSelectQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedSelectQueryOperator.cs index 0b197cf9fc5d1..b835b8e105992 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedSelectQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedSelectQueryOperator.cs @@ -131,7 +131,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for projecting elements as it is walked. // - private class IndexedSelectQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class IndexedSelectQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func _selector; // The actual select function. @@ -187,7 +187,7 @@ internal override IEnumerable AsSequentialQuery(CancellationToken token // results were indexable. // - private class IndexedSelectQueryOperatorResults : UnaryQueryOperatorResults + private sealed class IndexedSelectQueryOperatorResults : UnaryQueryOperatorResults { private readonly IndexedSelectQueryOperator _selectOp; // Operator that generated the results private readonly int _childCount; // The number of elements in child results diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedWhereQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedWhereQueryOperator.cs index 57c7ecbd5b30b..251e58e031a05 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedWhereQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/IndexedWhereQueryOperator.cs @@ -136,7 +136,7 @@ internal override bool LimitsParallelism // An enumerator that implements the filtering logic. // - private class IndexedWhereQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class IndexedWhereQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func _predicate; // The predicate used for filtering. @@ -173,7 +173,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_source.MoveNext(ref currentElement!, ref currentKey)) { if ((_outputLoopCount.Value++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (_predicate(currentElement, currentKey)) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/LastQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/LastQueryOperator.cs index e90df1acc1a50..e8153f3240e38 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/LastQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/LastQueryOperator.cs @@ -112,7 +112,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the last operation. // - private class LastQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class LastQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func? _predicate; // The optional predicate used during the search. @@ -173,7 +173,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TSource cu while (_source.MoveNext(ref value!, ref key)) { if ((loopCount & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // If the predicate is null or the current element satisfies it, we will remember // it as the current partition's candidate for the last element, and move on. @@ -234,7 +234,7 @@ protected override void Dispose(bool disposing) } - private class LastQueryOperatorState + private sealed class LastQueryOperatorState { internal TKey _key = default!; internal int _partitionId = -1; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ReverseQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ReverseQueryOperator.cs index e78c8a9a446ce..7fe2beafc7706 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ReverseQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/ReverseQueryOperator.cs @@ -101,7 +101,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the reverse operation. // - private class ReverseQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class ReverseQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to reverse. private readonly CancellationToken _cancellationToken; @@ -138,7 +138,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TSource cu while (_source.MoveNext(ref current!, ref key)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); _buffer.Add(new Pair(current, key)); _bufferIndex.Value++; @@ -168,7 +168,7 @@ protected override void Dispose(bool disposing) // results were indexable. // - private class ReverseQueryOperatorResults : UnaryQueryOperatorResults + private sealed class ReverseQueryOperatorResults : UnaryQueryOperatorResults { private readonly int _count; // The number of elements in child results diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectManyQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectManyQueryOperator.cs index 5c9b7421d8a37..769bacece22d6 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectManyQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectManyQueryOperator.cs @@ -230,7 +230,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the SelectMany logic. // - private class IndexedSelectManyQueryOperatorEnumerator : QueryOperatorEnumerator> + private sealed class IndexedSelectManyQueryOperatorEnumerator : QueryOperatorEnumerator> { private readonly QueryOperatorEnumerator _leftSource; // The left data source to enumerate. private readonly SelectManyQueryOperator _selectManyOperator; // The select many operator to use. @@ -239,7 +239,7 @@ private class IndexedSelectManyQueryOperatorEnumerator : QueryOperatorEnumerator private Mutables? _mutables; // bag of frequently mutated value types [allocate in moveNext to avoid false-sharing] private readonly CancellationToken _cancellationToken; - private class Mutables + private sealed class Mutables { internal int _currentRightSourceIndex = -1; // The index for the right data source. internal TLeftInput _currentLeftElement = default!; // The current element in the left data source. @@ -281,7 +281,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TOutput cu // Check cancellation every few lhs-enumerations in case none of them are producing // any outputs. Otherwise, we rely on the consumer of this operator to be performing the checks. if ((_mutables._lhsCount++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // We don't have a "current" right enumerator to use. We have to fetch the next // one. If the left has run out of elements, however, we're done and just return @@ -362,7 +362,7 @@ protected override void Dispose(bool disposing) // The enumerator type responsible for executing the SelectMany logic. // - private class SelectManyQueryOperatorEnumerator : QueryOperatorEnumerator> + private sealed class SelectManyQueryOperatorEnumerator : QueryOperatorEnumerator> { private readonly QueryOperatorEnumerator _leftSource; // The left data source to enumerate. private readonly SelectManyQueryOperator _selectManyOperator; // The select many operator to use. @@ -371,7 +371,7 @@ private class SelectManyQueryOperatorEnumerator : QueryOperatorEnumera private Mutables? _mutables; // bag of frequently mutated value types [allocate in moveNext to avoid false-sharing] private readonly CancellationToken _cancellationToken; - private class Mutables + private sealed class Mutables { internal int _currentRightSourceIndex = -1; // The index for the right data source. internal TLeftInput _currentLeftElement = default!; // The current element in the left data source. @@ -413,7 +413,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TOutput cu // Check cancellation every few lhs-enumerations in case none of them are producing // any outputs. Otherwise, we rely on the consumer of this operator to be performing the checks. if ((_mutables._lhsCount++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // We don't have a "current" right enumerator to use. We have to fetch the next // one. If the left has run out of elements, however, we're done and just return diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectQueryOperator.cs index d338c6bb8ae2a..3e9d450dd2c92 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SelectQueryOperator.cs @@ -90,7 +90,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for projecting elements as it is walked. // - private class SelectQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class SelectQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func _selector; // The actual select function. @@ -136,7 +136,7 @@ protected override void Dispose(bool disposing) // results were indexable. // - private class SelectQueryOperatorResults : UnaryQueryOperatorResults + private sealed class SelectQueryOperatorResults : UnaryQueryOperatorResults { private readonly Func _selector; // Selector function private readonly int _childCount; // The number of elements in child results diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SingleQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SingleQueryOperator.cs index e41063d62bd6f..2aa39c30ba66c 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SingleQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SingleQueryOperator.cs @@ -93,7 +93,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the Single operation. // - private class SingleQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class SingleQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func? _predicate; // The optional predicate used during the search. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SortQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SortQueryOperator.cs index 779804b96b7aa..be679efe9d760 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SortQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/SortQueryOperator.cs @@ -124,9 +124,9 @@ internal override bool LimitsParallelism } } - internal class SortQueryOperatorResults : QueryResults + internal sealed class SortQueryOperatorResults : QueryResults { - protected QueryResults _childQueryResults; // Results of the child query + private QueryResults _childQueryResults; // Results of the child query private readonly SortQueryOperator _op; // Operator that generated these results private QuerySettings _settings; // Settings collected from the query @@ -149,7 +149,7 @@ internal override void GivePartitionedStream(IPartitionedStreamRecipient + private sealed class ChildResultsRecipient : IPartitionedStreamRecipient { private readonly IPartitionedStreamRecipient _outputRecipient; private readonly SortQueryOperator _op; @@ -173,7 +173,7 @@ public void Receive(PartitionedStream childPartitioned // This enumerator performs sorting based on a key selection and comparison routine. // - internal class SortQueryOperatorEnumerator : QueryOperatorEnumerator + internal sealed class SortQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator? _source; // Data source to sort. private readonly Func _keySelector; // Key selector used when sorting. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipQueryOperator.cs index 2a6eead9bada8..b8c1188ada9f3 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipQueryOperator.cs @@ -145,7 +145,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the Take or Skip. // - private class TakeOrSkipQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class TakeOrSkipQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly int _count; // The number of elements to take or skip. @@ -205,7 +205,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TResult cu while (buffer.Count < _count && _source.MoveNext(ref current!, ref index)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Add the current element to our buffer. buffer.Add(new Pair(current, index)); @@ -325,7 +325,7 @@ internal override IEnumerable AsSequentialQuery(CancellationToken token // results were indexable. // - private class TakeOrSkipQueryOperatorResults : UnaryQueryOperatorResults + private sealed class TakeOrSkipQueryOperatorResults : UnaryQueryOperatorResults { private readonly TakeOrSkipQueryOperator _takeOrSkipOp; // The operator that generated the results private readonly int _childCount; // The number of elements in child results diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipWhileQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipWhileQueryOperator.cs index 2fcad2f945b00..1e5f51591dcfe 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipWhileQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/TakeOrSkipWhileQueryOperator.cs @@ -200,7 +200,7 @@ internal override bool LimitsParallelism // The enumerator type responsible for executing the take- or skip-while. // - private class TakeOrSkipWhileQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class TakeOrSkipWhileQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func? _predicate; // The actual predicate function. @@ -269,7 +269,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TResult cu while (_source.MoveNext(ref current!, ref key)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); // Add the current element to our buffer. buffer.Add(new Pair(current, key)); @@ -397,7 +397,7 @@ protected override void Dispose(bool disposing) } } - private class OperatorState + private sealed class OperatorState { internal volatile int _updatesDone; internal TKey _currentLowKey = default!; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/WhereQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/WhereQueryOperator.cs index e5dcfdb68f723..b189b57992c37 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/WhereQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/Unary/WhereQueryOperator.cs @@ -99,7 +99,7 @@ internal override bool LimitsParallelism // An enumerator that implements the filtering logic. // - private class WhereQueryOperatorEnumerator : QueryOperatorEnumerator + private sealed class WhereQueryOperatorEnumerator : QueryOperatorEnumerator { private readonly QueryOperatorEnumerator _source; // The data source to enumerate. private readonly Func _predicate; // The predicate used for filtering. @@ -138,7 +138,7 @@ internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref TInputOutp while (_source.MoveNext(ref currentElement!, ref currentKey)) { if ((_outputLoopCount.Value++ & CancellationState.POLL_INTERVAL) == 0) - _cancellationToken.ThrowIfCancellationRequested();; + _cancellationToken.ThrowIfCancellationRequested(); if (_predicate(currentElement)) { diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/UnaryQueryOperator.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/UnaryQueryOperator.cs index 5d73ce68859ba..e8eb8d60e5b6a 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/UnaryQueryOperator.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/QueryOperators/UnaryQueryOperator.cs @@ -138,7 +138,7 @@ internal override void GivePartitionedStream(IPartitionedStreamRecipient + private sealed class ChildResultsRecipient : IPartitionedStreamRecipient { private readonly IPartitionedStreamRecipient _outputRecipient; private readonly UnaryQueryOperator _op; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/CancellationState.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/CancellationState.cs index dd72fc42513f1..d7dc46d6c4672 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/CancellationState.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/CancellationState.cs @@ -15,7 +15,7 @@ namespace System.Linq.Parallel { - internal class CancellationState + internal sealed class CancellationState { // a cancellation signal that can be set internally to prompt early query termination. internal CancellationTokenSource? InternalCancellationTokenSource; @@ -64,7 +64,7 @@ internal CancellationState(CancellationToken externalCancellationToken) // // Inner loop code should poll once per n loop, typically via: // if ((i++ & CancellationState.POLL_INTERVAL) == 0) - // _cancellationToken.ThrowIfCancellationRequested();; + // _cancellationToken.ThrowIfCancellationRequested(); // (Note, this only behaves as expected if FREQ is of the form (2^n)-1 // Test if external cancellation was requested and occurred, and if so throw a standardize OCE with standardized message diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingPipeliningSpoolingTask.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingPipeliningSpoolingTask.cs index 81439fee3b01b..ccac54907c319 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingPipeliningSpoolingTask.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingPipeliningSpoolingTask.cs @@ -18,7 +18,7 @@ namespace System.Linq.Parallel { - internal class OrderPreservingPipeliningSpoolingTask : SpoolingTaskBase + internal sealed class OrderPreservingPipeliningSpoolingTask : SpoolingTaskBase { private readonly QueryTaskGroupState _taskGroupState; // State shared among tasks. private readonly QueryOperatorEnumerator _partition; // The source partition. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingSpoolingTask.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingSpoolingTask.cs index 87180c39f0419..36d0645dc293f 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingSpoolingTask.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/OrderPreservingSpoolingTask.cs @@ -22,7 +22,7 @@ namespace System.Linq.Parallel /// /// /// - internal class OrderPreservingSpoolingTask : SpoolingTaskBase + internal sealed class OrderPreservingSpoolingTask : SpoolingTaskBase { private readonly Shared _results; // The destination array cell into which data is placed. private readonly SortHelper _sortHelper; // A helper that performs the sorting. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/QueryTaskGroupState.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/QueryTaskGroupState.cs index 23a9b8ba7ec3d..859c74931749e 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/QueryTaskGroupState.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/QueryTaskGroupState.cs @@ -19,7 +19,7 @@ namespace System.Linq.Parallel /// convenient methods for tracing significant ETW events, waiting on tasks, propagating /// exceptions, and performing cancellation activities. /// - internal class QueryTaskGroupState + internal sealed class QueryTaskGroupState { private Task? _rootTask; // The task under which all query tasks root. private int _alreadyEnded; // Whether the tasks have been waited on already. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/SpoolingTask.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/SpoolingTask.cs index e1f8703a16180..440be3d9f9ce0 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/SpoolingTask.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/SpoolingTask.cs @@ -175,7 +175,7 @@ internal static void SpoolForAll( /// /// /// - internal class StopAndGoSpoolingTask : SpoolingTaskBase + internal sealed class StopAndGoSpoolingTask : SpoolingTaskBase { // The data source from which to pull data. private readonly QueryOperatorEnumerator _source; @@ -264,7 +264,7 @@ protected override void SpoolingFinally() /// /// /// - internal class PipelineSpoolingTask : SpoolingTaskBase + internal sealed class PipelineSpoolingTask : SpoolingTaskBase { // The data source from which to pull data. private readonly QueryOperatorEnumerator _source; @@ -355,7 +355,7 @@ protected override void SpoolingFinally() /// /// /// - internal class ForAllSpoolingTask : SpoolingTaskBase + internal sealed class ForAllSpoolingTask : SpoolingTaskBase { // The data source from which to pull data. private readonly QueryOperatorEnumerator _source; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/FixedMaxHeap.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/FixedMaxHeap.cs index 80d596c6dcd06..0e66a2e4d601b 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/FixedMaxHeap.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/FixedMaxHeap.cs @@ -16,7 +16,7 @@ namespace System.Linq.Parallel /// Very simple heap data structure, of fixed size. /// /// - internal class FixedMaxHeap + internal sealed class FixedMaxHeap { private readonly TElement[] _elements; // Element array. private int _count; // Current count. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/GrowingArray.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/GrowingArray.cs index 6c9dc42994e9a..d0b245910a7d8 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/GrowingArray.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/GrowingArray.cs @@ -15,7 +15,7 @@ namespace System.Linq.Parallel /// A growing array. Unlike List{T}, it makes the internal array available to its user. /// /// - internal class GrowingArray + internal sealed class GrowingArray { private T[] _array; private int _count; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/HashLookup.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/HashLookup.cs index 32064cec4acf4..fae7beb9589b4 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/HashLookup.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/HashLookup.cs @@ -17,7 +17,7 @@ namespace System.Linq.Parallel /// /// The kind of keys contained within. /// The kind of values contained within. - internal class HashLookup + internal sealed class HashLookup { private int[] buckets; private Slot[] slots; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/IntValueEvent.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/IntValueEvent.cs index 831e00ee8eb57..6f0eee4f6963a 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/IntValueEvent.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/IntValueEvent.cs @@ -12,7 +12,7 @@ namespace System.Linq.Parallel /// /// A ManualResetEventSlim that also remembers a value that was stored at the last Set(). /// - internal class IntValueEvent : ManualResetEventSlim + internal sealed class IntValueEvent : ManualResetEventSlim { internal int Value; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ListChunk.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ListChunk.cs index 5e2c43b675cf8..98e8aa299f8bd 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ListChunk.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ListChunk.cs @@ -16,7 +16,7 @@ namespace System.Linq.Parallel /// A linked list of array chunks. Allows direct access to its arrays. /// /// The elements held within. - internal class ListChunk : IEnumerable + internal sealed class ListChunk : IEnumerable { internal TInputOutput[] _chunk; private int _chunkCount; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Lookup.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Lookup.cs index f63fcaa95d917..60fbccd675173 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Lookup.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Lookup.cs @@ -34,7 +34,7 @@ namespace System.Linq.Parallel /// /// /// - internal class Lookup : ILookup where TKey: notnull + internal sealed class Lookup : ILookup where TKey: notnull { private readonly IDictionary> _dict; private readonly IEqualityComparer _comparer; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/PLINQETWProvider.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/PLINQETWProvider.cs index 9da7665c768b0..8cbe2929ac72b 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/PLINQETWProvider.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/PLINQETWProvider.cs @@ -50,7 +50,7 @@ internal static int NextQueryId() private const EventKeywords ALL_KEYWORDS = (EventKeywords)(-1); /// ETW tasks that have start/stop events. - public class Tasks // this name is important for EventSource + public static class Tasks // this name is important for EventSource { /// A parallel query. public const EventTask Query = (EventTask)1; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ReverseComparer.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ReverseComparer.cs index 229b5244acb10..31003137becbb 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ReverseComparer.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/ReverseComparer.cs @@ -17,7 +17,7 @@ namespace System.Linq.Parallel /// opposite answer. /// /// - internal class ReverseComparer : IComparer + internal sealed class ReverseComparer : IComparer { private readonly IComparer _comparer; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Shared.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Shared.cs index c257780fcc95c..0d29b2cec74a4 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Shared.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Shared.cs @@ -13,7 +13,7 @@ namespace System.Linq.Parallel /// A very simple primitive that allows us to share a value across multiple threads. /// /// - internal class Shared + internal sealed class Shared { internal T Value; diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Sorting.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Sorting.cs index b4a22b54be6e6..6461b11a1bb26 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Sorting.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Sorting.cs @@ -32,7 +32,7 @@ internal abstract class SortHelper internal abstract TInputOutput[] Sort(); } - internal class SortHelper : SortHelper, IDisposable + internal sealed class SortHelper : SortHelper, IDisposable { private readonly QueryOperatorEnumerator _source; // The data source from which to pull data. private readonly int _partitionCount; // The partition count. diff --git a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Util.cs b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Util.cs index aeccb2bf8abcf..71b9cb09b257d 100644 --- a/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Util.cs +++ b/src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Utils/Util.cs @@ -61,7 +61,7 @@ internal static Comparer GetDefaultComparer() private static readonly FastIntComparer s_fastIntComparer = new FastIntComparer(); - private class FastIntComparer : Comparer + private sealed class FastIntComparer : Comparer { public override int Compare(int x, int y) { @@ -71,7 +71,7 @@ public override int Compare(int x, int y) private static readonly FastLongComparer s_fastLongComparer = new FastLongComparer(); - private class FastLongComparer : Comparer + private sealed class FastLongComparer : Comparer { public override int Compare(long x, long y) { @@ -81,7 +81,7 @@ public override int Compare(long x, long y) private static readonly FastFloatComparer s_fastFloatComparer = new FastFloatComparer(); - private class FastFloatComparer : Comparer + private sealed class FastFloatComparer : Comparer { public override int Compare(float x, float y) { @@ -91,7 +91,7 @@ public override int Compare(float x, float y) private static readonly FastDoubleComparer s_fastDoubleComparer = new FastDoubleComparer(); - private class FastDoubleComparer : Comparer + private sealed class FastDoubleComparer : Comparer { public override int Compare(double x, double y) { @@ -101,7 +101,7 @@ public override int Compare(double x, double y) private static readonly FastDateTimeComparer s_fastDateTimeComparer = new FastDateTimeComparer(); - private class FastDateTimeComparer : Comparer + private sealed class FastDateTimeComparer : Comparer { public override int Compare(DateTime x, DateTime y) { diff --git a/src/libraries/System.Linq.Parallel/tests/QueryOperators/AggregateTests.cs b/src/libraries/System.Linq.Parallel/tests/QueryOperators/AggregateTests.cs index da065138d2ed2..3de0a0983bcf1 100644 --- a/src/libraries/System.Linq.Parallel/tests/QueryOperators/AggregateTests.cs +++ b/src/libraries/System.Linq.Parallel/tests/QueryOperators/AggregateTests.cs @@ -292,7 +292,7 @@ public static void Aggregate_OperationCanceledException() AssertThrows.EventuallyCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; })); AssertThrows.EventuallyCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; }, i => i)); AssertThrows.EventuallyCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; }, (i, j) => i, i => i)); - AssertThrows.EventuallyCanceled((source, canceler) => source.Aggregate(() => 0, (i, j) => { canceler(); ; return j; }, (i, j) => i, i => i)); + AssertThrows.EventuallyCanceled((source, canceler) => source.Aggregate(() => 0, (i, j) => { canceler(); return j; }, (i, j) => i, i => i)); } [Fact] @@ -302,12 +302,12 @@ public static void Aggregate_AggregateException_Wraps_OperationCanceledException AssertThrows.OtherTokenCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; })); AssertThrows.OtherTokenCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; }, i => i)); AssertThrows.OtherTokenCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; }, (i, j) => i, i => i)); - AssertThrows.OtherTokenCanceled((source, canceler) => source.Aggregate(() => 0, (i, j) => { canceler(); ; return j; }, (i, j) => i, i => i)); + AssertThrows.OtherTokenCanceled((source, canceler) => source.Aggregate(() => 0, (i, j) => { canceler(); return j; }, (i, j) => i, i => i)); AssertThrows.SameTokenNotCanceled((source, canceler) => source.Aggregate((i, j) => { canceler(); return j; })); AssertThrows.SameTokenNotCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; })); AssertThrows.SameTokenNotCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; }, i => i)); AssertThrows.SameTokenNotCanceled((source, canceler) => source.Aggregate(0, (i, j) => { canceler(); return j; }, (i, j) => i, i => i)); - AssertThrows.SameTokenNotCanceled((source, canceler) => source.Aggregate(() => 0, (i, j) => { canceler(); ; return j; }, (i, j) => i, i => i)); + AssertThrows.SameTokenNotCanceled((source, canceler) => source.Aggregate(() => 0, (i, j) => { canceler(); return j; }, (i, j) => i, i => i)); } [Fact] diff --git a/src/libraries/System.Linq.Parallel/tests/QueryOperators/OrderByThenByTests.cs b/src/libraries/System.Linq.Parallel/tests/QueryOperators/OrderByThenByTests.cs index 2df2e614e37c8..0d920b5d13f78 100644 --- a/src/libraries/System.Linq.Parallel/tests/QueryOperators/OrderByThenByTests.cs +++ b/src/libraries/System.Linq.Parallel/tests/QueryOperators/OrderByThenByTests.cs @@ -177,7 +177,7 @@ public static void OrderBy_NotPipelined(Labeled> labeled, int { int prev = 0; int seen = 0; - Assert.All(labeled.Item.OrderBy(x => x).ToList(), x => { Assert.InRange(x, prev, count - 1); ; prev = x; seen++; }); + Assert.All(labeled.Item.OrderBy(x => x).ToList(), x => { Assert.InRange(x, prev, count - 1); prev = x; seen++; }); Assert.Equal(count, seen); } diff --git a/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs b/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs index 692f4bc23ac5d..bd3497d53c658 100644 --- a/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs +++ b/src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs @@ -86,7 +86,9 @@ public static partial class Queryable public static System.Linq.IQueryable Except(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2) { throw null; } public static System.Linq.IQueryable Except(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? FirstOrDefault(this System.Linq.IQueryable source) { throw null; } + public static TSource FirstOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource? FirstOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } + public static TSource FirstOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate, TSource defaultValue) { throw null; } public static TSource First(this System.Linq.IQueryable source) { throw null; } public static TSource First(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static System.Linq.IQueryable> GroupBy(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> keySelector) { throw null; } @@ -104,7 +106,9 @@ public static partial class Queryable public static System.Linq.IQueryable Join(this System.Linq.IQueryable outer, System.Collections.Generic.IEnumerable inner, System.Linq.Expressions.Expression> outerKeySelector, System.Linq.Expressions.Expression> innerKeySelector, System.Linq.Expressions.Expression> resultSelector) { throw null; } public static System.Linq.IQueryable Join(this System.Linq.IQueryable outer, System.Collections.Generic.IEnumerable inner, System.Linq.Expressions.Expression> outerKeySelector, System.Linq.Expressions.Expression> innerKeySelector, System.Linq.Expressions.Expression> resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? LastOrDefault(this System.Linq.IQueryable source) { throw null; } + public static TSource LastOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource? LastOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } + public static TSource LastOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate, TSource defaultValue) { throw null; } public static TSource Last(this System.Linq.IQueryable source) { throw null; } public static TSource Last(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static long LongCount(this System.Linq.IQueryable source) { throw null; } @@ -129,7 +133,9 @@ public static partial class Queryable public static bool SequenceEqual(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2) { throw null; } public static bool SequenceEqual(this System.Linq.IQueryable source1, System.Collections.Generic.IEnumerable source2, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? SingleOrDefault(this System.Linq.IQueryable source) { throw null; } + public static TSource SingleOrDefault(this System.Linq.IQueryable source, TSource defaultValue) { throw null; } public static TSource? SingleOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } + public static TSource SingleOrDefault(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate, TSource defaultValue) { throw null; } public static TSource Single(this System.Linq.IQueryable source) { throw null; } public static TSource Single(this System.Linq.IQueryable source, System.Linq.Expressions.Expression> predicate) { throw null; } public static System.Linq.IQueryable SkipLast(this System.Linq.IQueryable source, int count) { throw null; } diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs index f0eb0f169bf3c..be11c326812cb 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/CachedReflection.cs @@ -284,6 +284,20 @@ public static MethodInfo FirstOrDefault_TSource_2(Type TSource) => (s_FirstOrDefault_TSource_2 ??= new Func, Expression>, object?>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_FirstOrDefault_TSource_3; + + public static MethodInfo FirstOrDefault_TSource_3(Type TSource) => + (s_FirstOrDefault_TSource_3 ?? + (s_FirstOrDefault_TSource_3 = new Func, object, object>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition())) + .MakeGenericMethod(TSource); + + private static MethodInfo? s_FirstOrDefault_TSource_4; + + public static MethodInfo FirstOrDefault_TSource_4(Type TSource) => + (s_FirstOrDefault_TSource_4 ?? + (s_FirstOrDefault_TSource_4 = new Func, Expression>, object, object>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition())) + .MakeGenericMethod(TSource); + private static MethodInfo? s_GroupBy_TSource_TKey_2; public static MethodInfo GroupBy_TSource_TKey_2(Type TSource, Type TKey) => @@ -392,6 +406,20 @@ public static MethodInfo LastOrDefault_TSource_2(Type TSource) => (s_LastOrDefault_TSource_2 ??= new Func, Expression>, object?>(Queryable.LastOrDefault).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_LastOrDefault_TSource_3; + + public static MethodInfo LastOrDefault_TSource_3(Type TSource) => + (s_LastOrDefault_TSource_3 ?? + (s_LastOrDefault_TSource_3 = new Func, object, object>(Queryable.LastOrDefault).GetMethodInfo().GetGenericMethodDefinition())) + .MakeGenericMethod(TSource); + + private static MethodInfo? s_LastOrDefault_TSource_4; + + public static MethodInfo LastOrDefault_TSource_4(Type TSource) => + (s_LastOrDefault_TSource_4 ?? + (s_LastOrDefault_TSource_4 = new Func, Expression>, object, object>(Queryable.LastOrDefault).GetMethodInfo().GetGenericMethodDefinition())) + .MakeGenericMethod(TSource); + private static MethodInfo? s_LongCount_TSource_1; public static MethodInfo LongCount_TSource_1(Type TSource) => @@ -536,6 +564,20 @@ public static MethodInfo SingleOrDefault_TSource_2(Type TSource) => (s_SingleOrDefault_TSource_2 ??= new Func, Expression>, object?>(Queryable.SingleOrDefault).GetMethodInfo().GetGenericMethodDefinition()) .MakeGenericMethod(TSource); + private static MethodInfo? s_SingleOrDefault_TSource_3; + + public static MethodInfo SingleOrDefault_TSource_3(Type TSource) => + (s_SingleOrDefault_TSource_3 ?? + (s_SingleOrDefault_TSource_3 = new Func, object, object>(Queryable.SingleOrDefault).GetMethodInfo().GetGenericMethodDefinition())) + .MakeGenericMethod(TSource); + + private static MethodInfo? s_SingleOrDefault_TSource_4; + + public static MethodInfo SingleOrDefault_TSource_4(Type TSource) => + (s_SingleOrDefault_TSource_4 ?? + (s_SingleOrDefault_TSource_4 = new Func, Expression>, object, object>(Queryable.SingleOrDefault).GetMethodInfo().GetGenericMethodDefinition())) + .MakeGenericMethod(TSource); + private static MethodInfo? s_Skip_TSource_2; public static MethodInfo Skip_TSource_2(Type TSource) => diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs index d4448372c3167..77b07b5b5c3ad 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs @@ -11,7 +11,7 @@ namespace System.Linq { - internal class EnumerableRewriter : ExpressionVisitor + internal sealed class EnumerableRewriter : ExpressionVisitor { // We must ensure that if a LabelTarget is rewritten that it is always rewritten to the same new target // or otherwise expressions using it won't match correctly. diff --git a/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs b/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs index c08bcab79af79..36a7789d464f8 100644 --- a/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs +++ b/src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs @@ -864,6 +864,18 @@ public static TSource First(this IQueryable source, Expression CachedReflectionInfo.FirstOrDefault_TSource_1(typeof(TSource)), source.Expression)); } + [DynamicDependency("FirstOrDefault`1", typeof(Enumerable))] + public static TSource FirstOrDefault(this IQueryable source, TSource defaultValue) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.FirstOrDefault_TSource_3(typeof(TSource)), + source.Expression, Expression.Constant(defaultValue, typeof(TSource)))); + } + [DynamicDependency("FirstOrDefault`1", typeof(Enumerable))] public static TSource? FirstOrDefault(this IQueryable source, Expression> predicate) { @@ -879,6 +891,21 @@ public static TSource First(this IQueryable source, Expression )); } + [DynamicDependency("FirstOrDefault`1", typeof(Enumerable))] + public static TSource FirstOrDefault(this IQueryable source, Expression> predicate, TSource defaultValue) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (predicate == null) + throw Error.ArgumentNull(nameof(predicate)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.FirstOrDefault_TSource_4(typeof(TSource)), + source.Expression, Expression.Quote(predicate), Expression.Constant(defaultValue, typeof(TSource)) + )); + } + [DynamicDependency("Last`1", typeof(Enumerable))] public static TSource Last(this IQueryable source) { @@ -916,6 +943,18 @@ public static TSource Last(this IQueryable source, Expression< CachedReflectionInfo.LastOrDefault_TSource_1(typeof(TSource)), source.Expression)); } + [DynamicDependency("LastOrDefault`1", typeof(Enumerable))] + public static TSource LastOrDefault(this IQueryable source, TSource defaultValue) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.LastOrDefault_TSource_3(typeof(TSource)), + source.Expression, Expression.Constant(defaultValue, typeof(TSource)))); + } + [DynamicDependency("LastOrDefault`1", typeof(Enumerable))] public static TSource? LastOrDefault(this IQueryable source, Expression> predicate) { @@ -931,6 +970,21 @@ public static TSource Last(this IQueryable source, Expression< )); } + [DynamicDependency("LastOrDefault`1", typeof(Enumerable))] + public static TSource LastOrDefault(this IQueryable source, Expression> predicate, TSource defaultValue) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (predicate == null) + throw Error.ArgumentNull(nameof(predicate)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.LastOrDefault_TSource_4(typeof(TSource)), + source.Expression, Expression.Quote(predicate), Expression.Constant(defaultValue, typeof(TSource)) + )); + } + [DynamicDependency("Single`1", typeof(Enumerable))] public static TSource Single(this IQueryable source) { @@ -968,6 +1022,19 @@ public static TSource Single(this IQueryable source, Expressio CachedReflectionInfo.SingleOrDefault_TSource_1(typeof(TSource)), source.Expression)); } + [DynamicDependency("SingleOrDefault`1", typeof(Enumerable))] + public static TSource SingleOrDefault(this IQueryable source, TSource defaultValue) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.SingleOrDefault_TSource_3(typeof(TSource)), + source.Expression, Expression.Constant(defaultValue, typeof(TSource)))); + + } + [DynamicDependency("SingleOrDefault`1", typeof(Enumerable))] public static TSource? SingleOrDefault(this IQueryable source, Expression> predicate) { @@ -983,6 +1050,21 @@ public static TSource Single(this IQueryable source, Expressio )); } + [DynamicDependency("SingleOrDefault`1", typeof(Enumerable))] + public static TSource SingleOrDefault(this IQueryable source, Expression> predicate, TSource defaultValue) + { + if (source == null) + throw Error.ArgumentNull(nameof(source)); + if (predicate == null) + throw Error.ArgumentNull(nameof(predicate)); + return source.Provider.Execute( + Expression.Call( + null, + CachedReflectionInfo.SingleOrDefault_TSource_4(typeof(TSource)), + source.Expression, Expression.Quote(predicate), Expression.Constant(defaultValue, typeof(TSource)) + )); + } + [DynamicDependency("ElementAt`1", typeof(Enumerable))] public static TSource ElementAt(this IQueryable source, int index) { diff --git a/src/libraries/System.Linq.Queryable/tests/FirstOrDefaultTests.cs b/src/libraries/System.Linq.Queryable/tests/FirstOrDefaultTests.cs index 95093c0e8596a..ac74ca5bd6836 100644 --- a/src/libraries/System.Linq.Queryable/tests/FirstOrDefaultTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/FirstOrDefaultTests.cs @@ -16,6 +16,15 @@ public void Empty() Assert.Equal(0, source.AsQueryable().FirstOrDefault()); } + [Fact] + public void EmptyDefault() + { + int[] source = { }; + int defaultValue = 5; + + Assert.Equal(defaultValue, source.AsQueryable().FirstOrDefault(defaultValue)); + } + [Fact] public void ManyElementsFirstIsDefault() { @@ -37,6 +46,27 @@ public void OneElementTruePredicate() Assert.Equal(4, source.AsQueryable().FirstOrDefault(i => i % 2 == 0)); } + [Fact] + public void OneElementTruePredicateDefault() + { + int[] source = { 4 }; + Assert.Equal(4, source.AsQueryable().FirstOrDefault(i => i % 2 == 0, 5)); + } + + [Fact] + public void OneElementFalsePredicate() + { + int[] source = { 3 }; + Assert.Equal(0, source.AsQueryable().FirstOrDefault(i => i % 2 == 0)); + } + + [Fact] + public void OneElementFalsePredicateDefault() + { + int[] source = { 3 }; + Assert.Equal(5, source.AsQueryable().FirstOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void ManyElementsPredicateFalseForAll() { @@ -44,23 +74,38 @@ public void ManyElementsPredicateFalseForAll() Assert.Equal(0, source.AsQueryable().FirstOrDefault(i => i % 2 == 0)); } + [Fact] + public void ManyElementsPredicateFalseForAllDefault() + { + int[] source = { 9, 5, 1, 3, 17, 21 }; + Assert.Equal(2, source.AsQueryable().FirstOrDefault(i => i % 2 == 0, 2)); + } + [Fact] public void PredicateTrueForSome() { int[] source = { 3, 7, 10, 7, 9, 2, 11, 17, 13, 8 }; Assert.Equal(10, source.AsQueryable().FirstOrDefault(i => i % 2 == 0)); } + [Fact] + public void PredicateTrueForSomeDefault() + { + int[] source = { 3, 7, 10, 7, 9, 2, 11, 17, 13, 8 }; + Assert.Equal(10, source.AsQueryable().FirstOrDefault(i => i % 2 == 0, 5)); + } [Fact] public void NullSource() { AssertExtensions.Throws("source", () => ((IQueryable)null).FirstOrDefault()); + AssertExtensions.Throws("source", () => ((IQueryable)null).FirstOrDefault(5)); } [Fact] public void NullSourcePredicateUsed() { AssertExtensions.Throws("source", () => ((IQueryable)null).FirstOrDefault(i => i != 2)); + AssertExtensions.Throws("source", () => ((IQueryable)null).FirstOrDefault(i => i != 2, 5)); } [Fact] @@ -68,6 +113,7 @@ public void NullPredicate() { Expression> predicate = null; AssertExtensions.Throws("predicate", () => Enumerable.Range(0, 3).AsQueryable().FirstOrDefault(predicate)); + AssertExtensions.Throws("predicate", () => Enumerable.Range(0, 3).AsQueryable().FirstOrDefault(predicate, 5)); } [Fact] diff --git a/src/libraries/System.Linq.Queryable/tests/LastOrDefaultTests.cs b/src/libraries/System.Linq.Queryable/tests/LastOrDefaultTests.cs index 62bfe0a9d29f2..c66c42fce4a4f 100644 --- a/src/libraries/System.Linq.Queryable/tests/LastOrDefaultTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/LastOrDefaultTests.cs @@ -14,6 +14,14 @@ public void Empty() Assert.Null(Enumerable.Empty().AsQueryable().LastOrDefault()); } + [Fact] + public void EmptyDefault() + { + int[] source = { }; + int defaultValue = 5; + Assert.Equal(defaultValue, source.AsQueryable().LastOrDefault(defaultValue)); + } + [Fact] public void OneElement() { @@ -21,6 +29,13 @@ public void OneElement() Assert.Equal(5, source.AsQueryable().LastOrDefault()); } + [Fact] + public void OneElementFalsePredicate() + { + int[] source = { 3 }; + Assert.Equal(5, source.AsQueryable().LastOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void ManyElementsLastIsDefault() { diff --git a/src/libraries/System.Linq.Queryable/tests/SingleOrDefaultTests.cs b/src/libraries/System.Linq.Queryable/tests/SingleOrDefaultTests.cs index 032ca44ba83ce..f34edae8b9f84 100644 --- a/src/libraries/System.Linq.Queryable/tests/SingleOrDefaultTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/SingleOrDefaultTests.cs @@ -22,12 +22,26 @@ public void Empty() Assert.Null(Enumerable.Empty().AsQueryable().SingleOrDefault()); } + [Fact] + public void EmptyDefault() + { + int[] source = { }; + int defaultValue = 5; + Assert.Equal(defaultValue, source.AsQueryable().SingleOrDefault(5)); + } + [Fact] public void EmptySourceWithPredicate() { Assert.Null(Enumerable.Empty().AsQueryable().SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void EmptySourceWithPredicateDefault() + { + Assert.Equal(5, Enumerable.Empty().AsQueryable().SingleOrDefault(i => i % 2 == 0, 5)); + } + [Theory] [InlineData(1, 100)] [InlineData(42, 100)] diff --git a/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs b/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs index 3d02b20ae0ec1..b1463804c6908 100644 --- a/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs +++ b/src/libraries/System.Linq.Queryable/tests/TrimCompatibilityTests.cs @@ -61,7 +61,7 @@ public static void CachedReflectionInfoMethodsNoAnnotations() .Where(m => m.GetParameters().Length > 0); // If you are adding a new method to this class, ensure the method meets these requirements - Assert.Equal(111, methods.Count()); + Assert.Equal(117, methods.Count()); foreach (MethodInfo method in methods) { ParameterInfo[] parameters = method.GetParameters(); diff --git a/src/libraries/System.Linq/ref/System.Linq.cs b/src/libraries/System.Linq/ref/System.Linq.cs index 7a86417711aa7..0f09240a0249d 100644 --- a/src/libraries/System.Linq/ref/System.Linq.cs +++ b/src/libraries/System.Linq/ref/System.Linq.cs @@ -59,7 +59,9 @@ public static System.Collections.Generic.IEnumerable< public static System.Collections.Generic.IEnumerable Except(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second) { throw null; } public static System.Collections.Generic.IEnumerable Except(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? FirstOrDefault(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource FirstOrDefault(this System.Collections.Generic.IEnumerable source, TSource defaultValue) { throw null; } public static TSource? FirstOrDefault(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } + public static TSource FirstOrDefault(this System.Collections.Generic.IEnumerable source, System.Func predicate, TSource defaultValue) { throw null; } public static TSource First(this System.Collections.Generic.IEnumerable source) { throw null; } public static TSource First(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } public static System.Collections.Generic.IEnumerable> GroupBy(this System.Collections.Generic.IEnumerable source, System.Func keySelector) { throw null; } @@ -77,7 +79,9 @@ public static System.Collections.Generic.IEnumerable< public static System.Collections.Generic.IEnumerable Join(this System.Collections.Generic.IEnumerable outer, System.Collections.Generic.IEnumerable inner, System.Func outerKeySelector, System.Func innerKeySelector, System.Func resultSelector) { throw null; } public static System.Collections.Generic.IEnumerable Join(this System.Collections.Generic.IEnumerable outer, System.Collections.Generic.IEnumerable inner, System.Func outerKeySelector, System.Func innerKeySelector, System.Func resultSelector, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? LastOrDefault(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource LastOrDefault(this System.Collections.Generic.IEnumerable source, TSource defaultValue) { throw null; } public static TSource? LastOrDefault(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } + public static TSource LastOrDefault(this System.Collections.Generic.IEnumerable source, System.Func predicate, TSource defaultValue) { throw null; } public static TSource Last(this System.Collections.Generic.IEnumerable source) { throw null; } public static TSource Last(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } public static long LongCount(this System.Collections.Generic.IEnumerable source) { throw null; } @@ -144,7 +148,9 @@ public static System.Collections.Generic.IEnumerable< public static bool SequenceEqual(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second) { throw null; } public static bool SequenceEqual(this System.Collections.Generic.IEnumerable first, System.Collections.Generic.IEnumerable second, System.Collections.Generic.IEqualityComparer? comparer) { throw null; } public static TSource? SingleOrDefault(this System.Collections.Generic.IEnumerable source) { throw null; } + public static TSource SingleOrDefault(this System.Collections.Generic.IEnumerable source, TSource defaultValue) { throw null; } public static TSource? SingleOrDefault(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } + public static TSource SingleOrDefault(this System.Collections.Generic.IEnumerable source, System.Func predicate, TSource defaultValue) { throw null; } public static TSource Single(this System.Collections.Generic.IEnumerable source) { throw null; } public static TSource Single(this System.Collections.Generic.IEnumerable source, System.Func predicate) { throw null; } public static System.Collections.Generic.IEnumerable SkipLast(this System.Collections.Generic.IEnumerable source, int count) { throw null; } diff --git a/src/libraries/System.Linq/src/ILLink/ILLinkTrim.xml b/src/libraries/System.Linq/src/ILLink/ILLink.Descriptors.xml similarity index 100% rename from src/libraries/System.Linq/src/ILLink/ILLinkTrim.xml rename to src/libraries/System.Linq/src/ILLink/ILLink.Descriptors.xml diff --git a/src/libraries/System.Linq/src/System.Linq.csproj b/src/libraries/System.Linq/src/System.Linq.csproj index 78dbcb88cfe2f..9040f668ab6f1 100644 --- a/src/libraries/System.Linq/src/System.Linq.csproj +++ b/src/libraries/System.Linq/src/System.Linq.csproj @@ -80,7 +80,6 @@ - diff --git a/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs index 777b28c916a30..d49da3a5af48f 100644 --- a/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs @@ -17,7 +17,7 @@ private abstract partial class AppendPrependIterator : IIListProvider + private sealed partial class AppendPrepend1Iterator { private TSource[] LazyToArray() { @@ -100,7 +100,7 @@ public override int GetCount(bool onlyIfCheap) } } - private partial class AppendPrependN + private sealed partial class AppendPrependN { private TSource[] LazyToArray() { diff --git a/src/libraries/System.Linq/src/System/Linq/AppendPrepend.cs b/src/libraries/System.Linq/src/System/Linq/AppendPrepend.cs index 346cd5c1c06d9..2397fbd39e282 100644 --- a/src/libraries/System.Linq/src/System/Linq/AppendPrepend.cs +++ b/src/libraries/System.Linq/src/System/Linq/AppendPrepend.cs @@ -86,7 +86,7 @@ public override void Dispose() /// Represents the insertion of an item before or after an . /// /// The type of the source enumerable. - private partial class AppendPrepend1Iterator : AppendPrependIterator + private sealed partial class AppendPrepend1Iterator : AppendPrependIterator { private readonly TSource _item; private readonly bool _appending; @@ -165,7 +165,7 @@ public override AppendPrependIterator Prepend(TSource item) /// Represents the insertion of multiple items before or after an . /// /// The type of the source enumerable. - private partial class AppendPrependN : AppendPrependIterator + private sealed partial class AppendPrependN : AppendPrependIterator { private readonly SingleLinkedNode? _prepended; private readonly SingleLinkedNode? _appended; diff --git a/src/libraries/System.Linq/src/System/Linq/Distinct.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Distinct.SpeedOpt.cs index 0af3e76984b12..2e51149fe9c1f 100644 --- a/src/libraries/System.Linq/src/System/Linq/Distinct.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Distinct.SpeedOpt.cs @@ -9,18 +9,11 @@ public static partial class Enumerable { private sealed partial class DistinctIterator : IIListProvider { - private Set FillSet() - { - var set = new Set(_comparer); - set.UnionWith(_source); - return set; - } + public TSource[] ToArray() => Enumerable.HashSetToArray(new HashSet(_source, _comparer)); - public TSource[] ToArray() => FillSet().ToArray(); + public List ToList() => Enumerable.HashSetToList(new HashSet(_source, _comparer)); - public List ToList() => FillSet().ToList(); - - public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : FillSet().Count; + public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : new HashSet(_source, _comparer).Count; } } } diff --git a/src/libraries/System.Linq/src/System/Linq/Distinct.cs b/src/libraries/System.Linq/src/System/Linq/Distinct.cs index f31e324e0fbad..5c59fc8164d32 100644 --- a/src/libraries/System.Linq/src/System/Linq/Distinct.cs +++ b/src/libraries/System.Linq/src/System/Linq/Distinct.cs @@ -28,7 +28,7 @@ private sealed partial class DistinctIterator : Iterator { private readonly IEnumerable _source; private readonly IEqualityComparer? _comparer; - private Set? _set; + private HashSet? _set; private IEnumerator? _enumerator; public DistinctIterator(IEnumerable source, IEqualityComparer? comparer) @@ -53,7 +53,7 @@ public override bool MoveNext() } TSource element = _enumerator.Current; - _set = new Set(_comparer); + _set = new HashSet(DefaultInternalSetCapacity, _comparer); _set.Add(element); _current = element; _state = 2; diff --git a/src/libraries/System.Linq/src/System/Linq/Except.cs b/src/libraries/System.Linq/src/System/Linq/Except.cs index 681cac6e1a32e..b3e0f45075d2d 100644 --- a/src/libraries/System.Linq/src/System/Linq/Except.cs +++ b/src/libraries/System.Linq/src/System/Linq/Except.cs @@ -39,8 +39,7 @@ public static IEnumerable Except(this IEnumerable fir private static IEnumerable ExceptIterator(IEnumerable first, IEnumerable second, IEqualityComparer? comparer) { - Set set = new Set(comparer); - set.UnionWith(second); + var set = new HashSet(second, comparer); foreach (TSource element in first) { diff --git a/src/libraries/System.Linq/src/System/Linq/First.cs b/src/libraries/System.Linq/src/System/Linq/First.cs index 64b89915c259f..7f59d1c63b587 100644 --- a/src/libraries/System.Linq/src/System/Linq/First.cs +++ b/src/libraries/System.Linq/src/System/Linq/First.cs @@ -31,10 +31,23 @@ public static TSource First(this IEnumerable source, Func(this IEnumerable source) => - source.TryGetFirst(out bool _); + source.TryGetFirst(out _); + + public static TSource FirstOrDefault(this IEnumerable source, TSource defaultValue) + { + TSource? first = source.TryGetFirst(out bool found); + return found ? first! : defaultValue; + } public static TSource? FirstOrDefault(this IEnumerable source, Func predicate) => - source.TryGetFirst(predicate, out bool _); + source.TryGetFirst(predicate, out _); + + public static TSource FirstOrDefault(this IEnumerable source, Func predicate, TSource defaultValue) + { + TSource? first = source.TryGetFirst(predicate, out bool found); + return found ? first! : defaultValue; + } + private static TSource? TryGetFirst(this IEnumerable source, out bool found) { diff --git a/src/libraries/System.Linq/src/System/Linq/Intersect.cs b/src/libraries/System.Linq/src/System/Linq/Intersect.cs index ab6afc24c3297..88c679106aba2 100644 --- a/src/libraries/System.Linq/src/System/Linq/Intersect.cs +++ b/src/libraries/System.Linq/src/System/Linq/Intersect.cs @@ -39,8 +39,7 @@ public static IEnumerable Intersect(this IEnumerable private static IEnumerable IntersectIterator(IEnumerable first, IEnumerable second, IEqualityComparer? comparer) { - Set set = new Set(comparer); - set.UnionWith(second); + var set = new HashSet(second, comparer); foreach (TSource element in first) { diff --git a/src/libraries/System.Linq/src/System/Linq/Last.cs b/src/libraries/System.Linq/src/System/Linq/Last.cs index f7cd74eb694bd..16318de2574cc 100644 --- a/src/libraries/System.Linq/src/System/Linq/Last.cs +++ b/src/libraries/System.Linq/src/System/Linq/Last.cs @@ -31,10 +31,23 @@ public static TSource Last(this IEnumerable source, Func(this IEnumerable source) => - source.TryGetLast(out bool _); + source.TryGetLast(out _); - public static TSource? LastOrDefault(this IEnumerable source, Func predicate) => - source.TryGetLast(predicate, out bool _); + + public static TSource LastOrDefault(this IEnumerable source, TSource defaultValue) + { + TSource? last = source.TryGetLast(out bool found); + return found ? last! : defaultValue; + } + + public static TSource? LastOrDefault(this IEnumerable source, Func predicate) + => source.TryGetLast(predicate, out _); + + public static TSource LastOrDefault(this IEnumerable source, Func predicate, TSource defaultValue) + { + var last = source.TryGetLast(predicate, out bool found); + return found ? last! : defaultValue; + } private static TSource? TryGetLast(this IEnumerable source, out bool found) { diff --git a/src/libraries/System.Linq/src/System/Linq/Set.cs b/src/libraries/System.Linq/src/System/Linq/Set.cs deleted file mode 100644 index 1d12834bc8087..0000000000000 --- a/src/libraries/System.Linq/src/System/Linq/Set.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace System.Linq -{ - /// - /// A lightweight hash set. - /// - /// The type of the set's items. - internal sealed class Set - { - /// - /// The comparer used to hash and compare items in the set. - /// - private readonly IEqualityComparer _comparer; - - /// - /// The hash buckets, which are used to index into the slots. - /// - private int[] _buckets; - - /// - /// The slots, each of which store an item and its hash code. - /// - private Slot[] _slots; - - /// - /// The number of items in this set. - /// - private int _count; - -#if DEBUG - /// - /// Whether has been called on this set. - /// - /// - /// When runs in debug builds, this flag is set to true. - /// Other methods assert that this flag is false in debug builds, because - /// they make optimizations that may not be correct if is called - /// beforehand. - /// - private bool _haveRemoved; -#endif - - /// - /// Constructs a set that compares items with the specified comparer. - /// - /// - /// The comparer. If this is null, it defaults to . - /// - public Set(IEqualityComparer? comparer) - { - _comparer = comparer ?? EqualityComparer.Default; - _buckets = new int[7]; - _slots = new Slot[7]; - } - - /// - /// Attempts to add an item to this set. - /// - /// The item to add. - /// - /// true if the item was not in the set; otherwise, false. - /// - public bool Add(TElement value) - { -#if DEBUG - Debug.Assert(!_haveRemoved, "This class is optimised for never calling Add after Remove. If your changes need to do so, undo that optimization."); -#endif - int hashCode = InternalGetHashCode(value); - for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i]._next) - { - if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value)) - { - return false; - } - } - - if (_count == _slots.Length) - { - Resize(); - } - - int index = _count; - _count++; - int bucket = hashCode % _buckets.Length; - _slots[index]._hashCode = hashCode; - _slots[index]._value = value; - _slots[index]._next = _buckets[bucket] - 1; - _buckets[bucket] = index + 1; - return true; - } - - /// - /// Attempts to remove an item from this set. - /// - /// The item to remove. - /// - /// true if the item was in the set; otherwise, false. - /// - public bool Remove(TElement value) - { -#if DEBUG - _haveRemoved = true; -#endif - int hashCode = InternalGetHashCode(value); - int bucket = hashCode % _buckets.Length; - int last = -1; - for (int i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i]._next) - { - if (_slots[i]._hashCode == hashCode && _comparer.Equals(_slots[i]._value, value)) - { - if (last < 0) - { - _buckets[bucket] = _slots[i]._next + 1; - } - else - { - _slots[last]._next = _slots[i]._next; - } - - _slots[i]._hashCode = -1; - _slots[i]._value = default!; - _slots[i]._next = -1; - return true; - } - } - - return false; - } - - /// - /// Expands the capacity of this set to double the current capacity, plus one. - /// - private void Resize() - { - int newSize = checked((_count * 2) + 1); - int[] newBuckets = new int[newSize]; - Slot[] newSlots = new Slot[newSize]; - Array.Copy(_slots, newSlots, _count); - for (int i = 0; i < _count; i++) - { - int bucket = newSlots[i]._hashCode % newSize; - newSlots[i]._next = newBuckets[bucket] - 1; - newBuckets[bucket] = i + 1; - } - - _buckets = newBuckets; - _slots = newSlots; - } - - /// - /// Creates an array from the items in this set. - /// - /// An array of the items in this set. - public TElement[] ToArray() - { -#if DEBUG - Debug.Assert(!_haveRemoved, "Optimised ToArray cannot be called if Remove has been called."); -#endif - TElement[] array = new TElement[_count]; - for (int i = 0; i != array.Length; ++i) - { - array[i] = _slots[i]._value; - } - - return array; - } - - /// - /// Creates a list from the items in this set. - /// - /// A list of the items in this set. - public List ToList() - { -#if DEBUG - Debug.Assert(!_haveRemoved, "Optimised ToList cannot be called if Remove has been called."); -#endif - int count = _count; - List list = new List(count); - for (int i = 0; i != count; ++i) - { - list.Add(_slots[i]._value); - } - - return list; - } - - /// - /// The number of items in this set. - /// - public int Count => _count; - - /// - /// Unions this set with an enumerable. - /// - /// The enumerable. - public void UnionWith(IEnumerable other) - { - Debug.Assert(other != null); - - foreach (TElement item in other) - { - Add(item); - } - } - - /// - /// Gets the hash code of the provided value with its sign bit zeroed out, so that modulo has a positive result. - /// - /// The value to hash. - /// The lower 31 bits of the value's hash code. - private int InternalGetHashCode(TElement value) => value == null ? 0 : _comparer.GetHashCode(value) & 0x7FFFFFFF; - - /// - /// An entry in the hash set. - /// - private struct Slot - { - /// - /// The hash code of the item. - /// - internal int _hashCode; - - /// - /// In the case of a hash collision, the index of the next slot to probe. - /// - internal int _next; - - /// - /// The item held by this slot. - /// - internal TElement _value; - } - } -} diff --git a/src/libraries/System.Linq/src/System/Linq/Single.cs b/src/libraries/System.Linq/src/System/Linq/Single.cs index 913405d502c2f..6455a841c9c46 100644 --- a/src/libraries/System.Linq/src/System/Linq/Single.cs +++ b/src/libraries/System.Linq/src/System/Linq/Single.cs @@ -10,82 +10,46 @@ public static partial class Enumerable { public static TSource Single(this IEnumerable source) { - if (source == null) + TSource? single = source.TryGetSingle(out bool found); + if (!found) { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); - } - - if (source is IList list) - { - switch (list.Count) - { - case 0: - ThrowHelper.ThrowNoElementsException(); - return default; - case 1: - return list[0]; - } - } - else - { - using (IEnumerator e = source.GetEnumerator()) - { - if (!e.MoveNext()) - { - ThrowHelper.ThrowNoElementsException(); - } - - TSource result = e.Current; - if (!e.MoveNext()) - { - return result; - } - } + ThrowHelper.ThrowNoElementsException(); } - ThrowHelper.ThrowMoreThanOneElementException(); - return default; + return single!; } - public static TSource Single(this IEnumerable source, Func predicate) { - if (source == null) + TSource? single = source.TryGetSingle(predicate, out bool found); + if (!found) { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); + ThrowHelper.ThrowNoMatchException(); } - if (predicate == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.predicate); - } + return single!; + } - using (IEnumerator e = source.GetEnumerator()) - { - while (e.MoveNext()) - { - TSource result = e.Current; - if (predicate(result)) - { - while (e.MoveNext()) - { - if (predicate(e.Current)) - { - ThrowHelper.ThrowMoreThanOneMatchException(); - } - } + public static TSource? SingleOrDefault(this IEnumerable source) + => source.TryGetSingle(out _); - return result; - } - } - } + public static TSource SingleOrDefault(this IEnumerable source, TSource defaultValue) + { + var single = source.TryGetSingle(out bool found); + return found ? single! : defaultValue; + } - ThrowHelper.ThrowNoMatchException(); - return default; + public static TSource? SingleOrDefault(this IEnumerable source, Func predicate) + => source.TryGetSingle(predicate, out _); + + public static TSource SingleOrDefault(this IEnumerable source, Func predicate, TSource defaultValue) + { + var single = source.TryGetSingle(predicate, out bool found); + return found ? single! : defaultValue; } - public static TSource? SingleOrDefault(this IEnumerable source) + private static TSource? TryGetSingle(this IEnumerable source, out bool found) { - if (source == null) + if (source is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source); } @@ -95,8 +59,10 @@ public static TSource Single(this IEnumerable source, Func(this IEnumerable source, Func(this IEnumerable source, Func predicate) + private static TSource? TryGetSingle(this IEnumerable source, Func predicate, out bool found) { if (source == null) { @@ -147,12 +116,13 @@ public static TSource Single(this IEnumerable source, Func ToHashSet(this IEnumerable sour // Don't pre-allocate based on knowledge of size, as potentially many elements will be dropped. return new HashSet(source, comparer); } + + /// Default initial capacity to use when creating sets for internal temporary storage. + /// This is based on the implicit size used in previous implementations, which used a custom Set type. + private const int DefaultInternalSetCapacity = 7; + + private static TSource[] HashSetToArray(HashSet set) + { + var result = new TSource[set.Count]; + set.CopyTo(result); + return result; + } + + private static List HashSetToList(HashSet set) + { + var result = new List(set.Count); + + foreach (TSource item in set) + { + result.Add(item); + } + + return result; + } } } diff --git a/src/libraries/System.Linq/src/System/Linq/Union.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Union.SpeedOpt.cs index eb21abe7dd14b..cedfa899762ea 100644 --- a/src/libraries/System.Linq/src/System/Linq/Union.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Union.SpeedOpt.cs @@ -9,9 +9,9 @@ public static partial class Enumerable { private abstract partial class UnionIterator : IIListProvider { - private Set FillSet() + private HashSet FillSet() { - var set = new Set(_comparer); + var set = new HashSet(_comparer); for (int index = 0; ; ++index) { IEnumerable? enumerable = GetEnumerable(index); @@ -24,9 +24,9 @@ private Set FillSet() } } - public TSource[] ToArray() => FillSet().ToArray(); + public TSource[] ToArray() => Enumerable.HashSetToArray(FillSet()); - public List ToList() => FillSet().ToList(); + public List ToList() => Enumerable.HashSetToList(FillSet()); public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : FillSet().Count; } diff --git a/src/libraries/System.Linq/src/System/Linq/Union.cs b/src/libraries/System.Linq/src/System/Linq/Union.cs index e5cfdb0462c72..d9b3c4bdb065b 100644 --- a/src/libraries/System.Linq/src/System/Linq/Union.cs +++ b/src/libraries/System.Linq/src/System/Linq/Union.cs @@ -34,7 +34,7 @@ private abstract partial class UnionIterator : Iterator { internal readonly IEqualityComparer? _comparer; private IEnumerator? _enumerator; - private Set? _set; + private HashSet? _set; protected UnionIterator(IEqualityComparer? comparer) { @@ -68,7 +68,7 @@ private void StoreFirst() { Debug.Assert(_enumerator != null); - Set set = new Set(_comparer); + var set = new HashSet(DefaultInternalSetCapacity, _comparer); TSource element = _enumerator.Current; set.Add(element); _current = element; @@ -80,7 +80,7 @@ private bool GetNext() Debug.Assert(_enumerator != null); Debug.Assert(_set != null); - Set set = _set; + HashSet set = _set; while (_enumerator.MoveNext()) { diff --git a/src/libraries/System.Linq/tests/FirstOrDefaultTests.cs b/src/libraries/System.Linq/tests/FirstOrDefaultTests.cs index f0642c3e8165a..b0fe384389dcb 100644 --- a/src/libraries/System.Linq/tests/FirstOrDefaultTests.cs +++ b/src/libraries/System.Linq/tests/FirstOrDefaultTests.cs @@ -39,6 +39,15 @@ private static void TestEmptyIList() Assert.Equal(expected, source.RunOnce().FirstOrDefault()); } + private static void TestEmptyIListDefault(T defaultValue) + { + T[] source = { }; + + Assert.IsAssignableFrom>(source); + + Assert.Equal(defaultValue, source.RunOnce().FirstOrDefault(defaultValue)); + } + [Fact] public void EmptyIListT() { @@ -48,6 +57,14 @@ public void EmptyIListT() TestEmptyIList(); } + [Fact] + public void EmptyIListDefault() + { + TestEmptyIListDefault(5); // int + TestEmptyIListDefault("Hello"); // string + TestEmptyIListDefault(DateTime.UnixEpoch); //DateTime + } + [Fact] public void IListTOneElement() { @@ -59,6 +76,17 @@ public void IListTOneElement() Assert.Equal(expected, source.FirstOrDefault()); } + [Fact] + public void IListOneElementDefault() + { + int[] source = { 5 }; + int expected = 5; + + Assert.IsAssignableFrom>(source); + + Assert.Equal(expected, source.FirstOrDefault(3)); + } + [Fact] public void IListTManyElementsFirstIsDefault() { @@ -96,6 +124,20 @@ static IEnumerable EmptySource() Assert.Equal(expected, source.RunOnce().FirstOrDefault()); } + private static void TestEmptyNotIListDefault(T defaultValue) + { + static IEnumerable EmptySource() + { + yield break; + } + + var source = EmptySource(); + + Assert.Null(source as IList); + + Assert.Equal(defaultValue, source.RunOnce().FirstOrDefault(defaultValue)); + } + [Fact] public void EmptyNotIListT() { @@ -146,6 +188,16 @@ public void OneElementTruePredicate() Assert.Equal(expected, source.FirstOrDefault(predicate)); } + [Fact] + public void OneElementTruePredicateDefault() + { + int[] source = { 4 }; + Func predicate = IsEven; + int expected = 4; + + Assert.Equal(expected, source.FirstOrDefault(predicate, 5)); + } + [Fact] public void ManyElementsPredicateFalseForAll() { @@ -156,6 +208,16 @@ public void ManyElementsPredicateFalseForAll() Assert.Equal(expected, source.FirstOrDefault(predicate)); } + [Fact] + public void ManyElementsPredicateFalseForAllDefault() + { + int[] source = { 9, 5, 1, 3, 17, 21 }; + Func predicate = IsEven; + int expected = 5; + + Assert.Equal(expected, source.FirstOrDefault(predicate, 5)); + } + [Fact] public void PredicateTrueOnlyForLast() { @@ -166,6 +228,16 @@ public void PredicateTrueOnlyForLast() Assert.Equal(expected, source.FirstOrDefault(predicate)); } + [Fact] + public void PredicateTrueOnlyForLastDefault() + { + int[] source = { 9, 5, 1, 3, 17, 21, 50 }; + Func predicate = IsEven; + int expected = 50; + + Assert.Equal(expected, source.FirstOrDefault(predicate, 5)); + } + [Fact] public void PredicateTrueForSome() { @@ -176,6 +248,16 @@ public void PredicateTrueForSome() Assert.Equal(expected, source.FirstOrDefault(predicate)); } + [Fact] + public void PredicateTrueForSomeDefault() + { + int[] source = { 3, 7, 10, 7, 9, 2, 11, 17, 13, 8 }; + Func predicate = IsEven; + int expected = 10; + + Assert.Equal(expected, source.FirstOrDefault(predicate, 5)); + } + [Fact] public void PredicateTrueForSomeRunOnce() { @@ -190,12 +272,14 @@ public void PredicateTrueForSomeRunOnce() public void NullSource() { AssertExtensions.Throws("source", () => ((IEnumerable)null).FirstOrDefault()); + AssertExtensions.Throws("source", () => ((IEnumerable)null).FirstOrDefault(5)); } [Fact] public void NullSourcePredicateUsed() { AssertExtensions.Throws("source", () => ((IEnumerable)null).FirstOrDefault(i => i != 2)); + AssertExtensions.Throws("source", () => ((IEnumerable)null).FirstOrDefault(i => i != 2, 5)); } [Fact] @@ -203,6 +287,7 @@ public void NullPredicate() { Func predicate = null; AssertExtensions.Throws("predicate", () => Enumerable.Range(0, 3).FirstOrDefault(predicate)); + AssertExtensions.Throws("predicate", () => Enumerable.Range(0, 3).FirstOrDefault(predicate, 5)); } } } diff --git a/src/libraries/System.Linq/tests/GroupByTests.cs b/src/libraries/System.Linq/tests/GroupByTests.cs index 6b2437f9275ee..ee487a813c236 100644 --- a/src/libraries/System.Linq/tests/GroupByTests.cs +++ b/src/libraries/System.Linq/tests/GroupByTests.cs @@ -84,7 +84,7 @@ public struct Record public void SameResultsRepeatCallsStringQuery() { var q1 = from x1 in new string[] { "Alen", "Felix", null, null, "X", "Have Space", "Clinton", "" } - select x1; ; + select x1; var q2 = from x2 in new int[] { 55, 49, 9, -100, 24, 25, -1, 0 } select x2; diff --git a/src/libraries/System.Linq/tests/LastOrDefaultTests.cs b/src/libraries/System.Linq/tests/LastOrDefaultTests.cs index ec8d6c40b6244..ab3a3fa1ad8a1 100644 --- a/src/libraries/System.Linq/tests/LastOrDefaultTests.cs +++ b/src/libraries/System.Linq/tests/LastOrDefaultTests.cs @@ -39,6 +39,15 @@ private static void TestEmptyIList() Assert.Equal(expected, source.RunOnce().LastOrDefault()); } + private static void TestEmptyIListDefault(T defaultValue) + { + T[] source = { }; + + Assert.IsAssignableFrom>(source); + + Assert.Equal(defaultValue, source.RunOnce().LastOrDefault(defaultValue)); + } + [Fact] public void EmptyIListT() { @@ -48,6 +57,14 @@ public void EmptyIListT() TestEmptyIList(); } + [Fact] + public void EmptyIList() + { + TestEmptyIListDefault(5); // int + TestEmptyIListDefault("Hello"); // string + TestEmptyIListDefault(DateTime.UnixEpoch); + } + [Fact] public void IListTOneElement() { @@ -59,6 +76,17 @@ public void IListTOneElement() Assert.Equal(expected, source.LastOrDefault()); } + [Fact] + public void IListTOneElementDefault() + { + int[] source = { 5 }; + int expected = 5; + + Assert.IsAssignableFrom>(source); + + Assert.Equal(expected, source.LastOrDefault(4)); + } + [Fact] public void IListTManyElementsLastIsDefault() @@ -82,6 +110,28 @@ public void IListTManyElementsLastIsNotDefault() Assert.Equal(expected, source.LastOrDefault()); } + [Fact] + public void IListTManyElementsLastHasDefault() + { + int?[] source = { -10, 2, 4, 3, 0, 2, null }; + int? expected = null; + + Assert.IsAssignableFrom>(source); + + Assert.Equal(expected, source.LastOrDefault(5)); + } + + [Fact] + public void IListTManyElementsLastIsHasDefault() + { + int?[] source = { -10, 2, 4, 3, 0, 2, null, 19 }; + int? expected = 19; + + Assert.IsAssignableFrom>(source); + + Assert.Equal(expected, source.LastOrDefault(5)); + } + private static IEnumerable EmptySource() { yield break; @@ -147,6 +197,16 @@ public void OneElementIListTruePredicate() Assert.Equal(expected, source.LastOrDefault(predicate)); } + [Fact] + public void OneElementIListTruePredicateDefault() + { + int[] source = { 4 }; + Func predicate = IsEven; + int expected = 4; + + Assert.Equal(expected, source.LastOrDefault(predicate, 5)); + } + [Fact] public void ManyElementsIListPredicateFalseForAll() { @@ -157,6 +217,16 @@ public void ManyElementsIListPredicateFalseForAll() Assert.Equal(expected, source.LastOrDefault(predicate)); } + [Fact] + public void ManyElementsIListPredicateFalseForAllDefault() + { + int[] source = { 9, 5, 1, 3, 17, 21 }; + Func predicate = IsEven; + int expected = 5; + + Assert.Equal(expected, source.LastOrDefault(predicate, 5)); + } + [Fact] public void IListPredicateTrueOnlyForLast() { diff --git a/src/libraries/System.Linq/tests/SelectTests.cs b/src/libraries/System.Linq/tests/SelectTests.cs index 4c0eedb7b0ea9..a95ad41aeea31 100644 --- a/src/libraries/System.Linq/tests/SelectTests.cs +++ b/src/libraries/System.Linq/tests/SelectTests.cs @@ -16,7 +16,7 @@ public class SelectTests : EnumerableTests public void SameResultsRepeatCallsStringQuery() { var q1 = from x1 in new string[] { "Alen", "Felix", null, null, "X", "Have Space", "Clinton", "" } - select x1; ; + select x1; var q2 = from x2 in new int[] { 55, 49, 9, -100, 24, 25, -1, 0 } select x2; diff --git a/src/libraries/System.Linq/tests/SingleOrDefaultTests.cs b/src/libraries/System.Linq/tests/SingleOrDefaultTests.cs index 1dfca02713808..9ac26e76a8fa2 100644 --- a/src/libraries/System.Linq/tests/SingleOrDefaultTests.cs +++ b/src/libraries/System.Linq/tests/SingleOrDefaultTests.cs @@ -36,6 +36,15 @@ public void EmptyIList() Assert.Equal(expected, source.SingleOrDefault()); } + [Fact] + public void EmptyIListDefault() + { + int?[] source = { }; + int expected = 5; + + Assert.Equal(expected, source.SingleOrDefault(5)); + } + [Fact] public void SingleElementIList() { @@ -45,6 +54,15 @@ public void SingleElementIList() Assert.Equal(expected, source.SingleOrDefault()); } + [Fact] + public void SingleElementIListDefault() + { + int[] source = { 4 }; + int expected = 4; + + Assert.Equal(expected, source.SingleOrDefault(5)); + } + [Fact] public void ManyElementIList() { @@ -53,6 +71,14 @@ public void ManyElementIList() Assert.Throws(() => source.SingleOrDefault()); } + [Fact] + public void ManyElementIListDefault() + { + int[] source = { 4, 4, 4, 4, 4 }; + + Assert.Throws(() => source.SingleOrDefault(5)); + } + [Fact] public void EmptyNotIList() { @@ -88,6 +114,15 @@ public void EmptySourceWithPredicate() Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void EmptySourceWithPredicateDefault() + { + int[] source = { }; + int expected = 5; + + Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void SingleElementPredicateTrue() { @@ -97,6 +132,15 @@ public void SingleElementPredicateTrue() Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void SingleElementPredicateTrueDefault() + { + int[] source = { 4 }; + int expected = 4; + + Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void SingleElementPredicateFalse() { @@ -106,6 +150,15 @@ public void SingleElementPredicateFalse() Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void SingleElementPredicateFalseDefault() + { + int[] source = { 3 }; + int expected = 5; + + Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void ManyElementsPredicateFalseForAll() { @@ -115,6 +168,15 @@ public void ManyElementsPredicateFalseForAll() Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void ManyElementsPredicateFalseForAllDefault() + { + int[] source = { 3, 1, 7, 9, 13, 19 }; + int expected = 5; + + Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void ManyElementsPredicateTrueForLast() { @@ -124,6 +186,15 @@ public void ManyElementsPredicateTrueForLast() Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void ManyElementsPredicateTrueForLastDefault() + { + int[] source = { 3, 1, 7, 9, 13, 19, 20 }; + int expected = 20; + + Assert.Equal(expected, source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void ManyElementsPredicateTrueForFirstAndFifth() { @@ -132,6 +203,14 @@ public void ManyElementsPredicateTrueForFirstAndFifth() Assert.Throws(() => source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void ManyElementsPredicateTrueForFirstAndFifthDefault() + { + int[] source = { 2, 3, 1, 7, 10, 13, 19, 9 }; + + Assert.Throws(() => source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Theory] [InlineData(1, 100)] [InlineData(42, 100)] @@ -156,6 +235,14 @@ public void ThrowsOnNullSource() AssertExtensions.Throws("source", () => source.SingleOrDefault(i => i % 2 == 0)); } + [Fact] + public void ThrowsOnNullSourceDefault() + { + int[] source = null; + AssertExtensions.Throws("source", () => source.SingleOrDefault(5)); + AssertExtensions.Throws("source", () => source.SingleOrDefault(i => i % 2 == 0, 5)); + } + [Fact] public void ThrowsOnNullPredicate() { @@ -163,5 +250,13 @@ public void ThrowsOnNullPredicate() Func nullPredicate = null; AssertExtensions.Throws("predicate", () => source.SingleOrDefault(nullPredicate)); } + + [Fact] + public void ThrowsOnNullPredicateDefault() + { + int[] source = { }; + Func nullPredicate = null; + AssertExtensions.Throws("predicate", () => source.SingleOrDefault(nullPredicate, 5)); + } } } diff --git a/src/libraries/System.Linq/tests/ToLookupTests.cs b/src/libraries/System.Linq/tests/ToLookupTests.cs index 307c7ed95789b..458aaa9e4dded 100644 --- a/src/libraries/System.Linq/tests/ToLookupTests.cs +++ b/src/libraries/System.Linq/tests/ToLookupTests.cs @@ -41,7 +41,7 @@ private static void AssertMatches(IEnumerable keys, IEnumerable elem public void SameResultsRepeatCall() { var q1 = from x1 in new string[] { "Alen", "Felix", null, null, "X", "Have Space", "Clinton", "" } - select x1; ; + select x1; var q2 = from x2 in new int[] { 55, 49, 9, -100, 24, 25, -1, 0 } select x2; diff --git a/src/libraries/System.Management/ref/System.Management.manual.cs b/src/libraries/System.Management/ref/System.Management.manual.cs index 59dbdc8aeb118..45b594bdb133a 100644 --- a/src/libraries/System.Management/ref/System.Management.manual.cs +++ b/src/libraries/System.Management/ref/System.Management.manual.cs @@ -8,13 +8,13 @@ namespace System.Management { [System.ComponentModel.TypeConverter(typeof(ManagementPathConverter))] public partial class ManagementPath { } - internal class ManagementPathConverter { } + internal sealed class ManagementPathConverter { } [System.ComponentModel.TypeConverter(typeof(ManagementQueryConverter))] public abstract partial class ManagementQuery { } - internal class ManagementQueryConverter { } + internal sealed class ManagementQueryConverter { } [System.ComponentModel.TypeConverter(typeof(ManagementScopeConverter))] public partial class ManagementScope { } - internal class ManagementScopeConverter { } + internal sealed class ManagementScopeConverter { } } diff --git a/src/libraries/System.Management/src/System/Management/InteropClasses/WMIInterop.cs b/src/libraries/System.Management/src/System/Management/InteropClasses/WMIInterop.cs index b2de37af52c1a..954bfabcd8f55 100644 --- a/src/libraries/System.Management/src/System/Management/InteropClasses/WMIInterop.cs +++ b/src/libraries/System.Management/src/System/Management/InteropClasses/WMIInterop.cs @@ -464,7 +464,7 @@ public int EndEnumeration_() } } - internal class MarshalWbemObject : ICustomMarshaler + internal sealed class MarshalWbemObject : ICustomMarshaler { public static ICustomMarshaler GetInstance(string cookie) { @@ -1377,10 +1377,9 @@ internal class WbemQuery } #endregion - internal class MTAHelper + internal static class MTAHelper { - - private class MTARequest + private sealed class MTARequest { public AutoResetEvent evtDone = new AutoResetEvent(false); public Type typeToCreate; @@ -1592,7 +1591,7 @@ private static bool IsWindows2000OrHigher() /// 4. Delegate with parameter and return value. /// /// - internal class ThreadDispatch + internal sealed class ThreadDispatch { #region Private fields private Thread thread; diff --git a/src/libraries/System.Management/src/System/Management/ManagementEventArgs.cs b/src/libraries/System.Management/src/System/Management/ManagementEventArgs.cs index 39a822221713f..b858c1b430c8e 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementEventArgs.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementEventArgs.cs @@ -4,12 +4,12 @@ namespace System.Management { - internal class IdentifierChangedEventArgs : EventArgs + internal sealed class IdentifierChangedEventArgs : EventArgs { internal IdentifierChangedEventArgs() { } } - internal class InternalObjectPutEventArgs : EventArgs + internal sealed class InternalObjectPutEventArgs : EventArgs { private readonly ManagementPath path; diff --git a/src/libraries/System.Management/src/System/Management/ManagementEventWatcher.cs b/src/libraries/System.Management/src/System/Management/ManagementEventWatcher.cs index b7e994e5642c5..3d0a26a575fcd 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementEventWatcher.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementEventWatcher.cs @@ -600,7 +600,7 @@ internal void FireEventArrived(EventArrivedEventArgs args) } - internal class SinkForEventQuery : IWmiEventSource + internal sealed class SinkForEventQuery : IWmiEventSource { private readonly ManagementEventWatcher eventWatcher; private readonly object context; diff --git a/src/libraries/System.Management/src/System/Management/ManagementOperationWatcher.cs b/src/libraries/System.Management/src/System/Management/ManagementOperationWatcher.cs index 94fd1f7fefd79..8aee1046e7496 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementOperationWatcher.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementOperationWatcher.cs @@ -364,7 +364,7 @@ internal void FireObjectPut(ObjectPutEventArgs args) } } - internal class WmiEventState + internal sealed class WmiEventState { private readonly Delegate d; private readonly ManagementEventArgs args; @@ -402,7 +402,7 @@ public AutoResetEvent AutoResetEvent /// WMI event as "done" until all target delegates have signalled that they are /// done. /// - internal class WmiDelegateInvoker + internal sealed class WmiDelegateInvoker { internal object sender; diff --git a/src/libraries/System.Management/src/System/Management/ManagementPath.cs b/src/libraries/System.Management/src/System/Management/ManagementPath.cs index 8bcf9b545a23e..fab177277f276 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementPath.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementPath.cs @@ -1046,7 +1046,7 @@ public bool IsSingleton /// /// Converts a String to a ManagementPath /// - internal class ManagementPathConverter : ExpandableObjectConverter + internal sealed class ManagementPathConverter : ExpandableObjectConverter { /// diff --git a/src/libraries/System.Management/src/System/Management/ManagementQuery.cs b/src/libraries/System.Management/src/System/Management/ManagementQuery.cs index 509b1cfd61df2..f044ab25fe408 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementQuery.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementQuery.cs @@ -3169,7 +3169,7 @@ public override object Clone() /// /// Converts a String to a ManagementQuery /// - internal class ManagementQueryConverter : ExpandableObjectConverter + internal sealed class ManagementQueryConverter : ExpandableObjectConverter { /// diff --git a/src/libraries/System.Management/src/System/Management/ManagementScope.cs b/src/libraries/System.Management/src/System/Management/ManagementScope.cs index 110abe48273b6..a1b10722738e2 100644 --- a/src/libraries/System.Management/src/System/Management/ManagementScope.cs +++ b/src/libraries/System.Management/src/System/Management/ManagementScope.cs @@ -1038,7 +1038,7 @@ internal SecuredIWbemServicesHandler GetSecuredIWbemServicesHandler(IWbemService }//ManagementScope - internal class SecuredIEnumWbemClassObjectHandler + internal sealed class SecuredIEnumWbemClassObjectHandler { private readonly IEnumWbemClassObject pEnumWbemClassObjectsecurityHelper; private readonly ManagementScope scope; @@ -1092,7 +1092,7 @@ internal int Skip_(int lTimeout, uint nCount) } - internal class SecuredConnectHandler + internal sealed class SecuredConnectHandler { private readonly ManagementScope scope; @@ -1142,7 +1142,7 @@ internal int ConnectNSecureIWbemServices(string path, ref IWbemServices pService } } - internal class SecuredIWbemServicesHandler + internal sealed class SecuredIWbemServicesHandler { private readonly IWbemServices pWbemServiecsSecurityHelper; private readonly ManagementScope scope; @@ -1381,7 +1381,7 @@ internal int ExecMethodAsync_(string strObjectPath, string strMethodName, int lF } - internal class SecurityHandler + internal sealed class SecurityHandler { private bool needToReset; private readonly IntPtr handle; @@ -1469,7 +1469,7 @@ internal void SecureIUnknown(object unknown) /// /// Converts a String to a ManagementScope /// - internal class ManagementScopeConverter : ExpandableObjectConverter + internal sealed class ManagementScopeConverter : ExpandableObjectConverter { /// diff --git a/src/libraries/System.Management/src/System/Management/MethodSet.cs b/src/libraries/System.Management/src/System/Management/MethodSet.cs index 1219df1d895f2..a87b28f46168f 100644 --- a/src/libraries/System.Management/src/System/Management/MethodSet.cs +++ b/src/libraries/System.Management/src/System/Management/MethodSet.cs @@ -49,7 +49,7 @@ public class MethodDataCollection : ICollection, IEnumerable { private readonly ManagementObject parent; - private class enumLock + private sealed class enumLock { } //used to lock usage of BeginMethodEnum/NextMethod diff --git a/src/libraries/System.Management/src/System/Management/Property.cs b/src/libraries/System.Management/src/System/Management/Property.cs index bac0155b51996..d86b5a043ab22 100644 --- a/src/libraries/System.Management/src/System/Management/Property.cs +++ b/src/libraries/System.Management/src/System/Management/Property.cs @@ -16,7 +16,7 @@ namespace System.Management // RuntimeHelpers.GetObjectValue. This returns reference types right back to the caller, but if passed // a boxed non-primitive value type, it will return a boxed copy. We cannot use GetObjectValue for primitives // because its implementation does not copy boxed primitives. - internal class ValueTypeSafety + internal static class ValueTypeSafety { public static object GetSafeObject(object theValue) { diff --git a/src/libraries/System.Management/src/System/Management/WMIGenerator.cs b/src/libraries/System.Management/src/System/Management/WMIGenerator.cs index 56a515ca09cb9..b983097dd5268 100644 --- a/src/libraries/System.Management/src/System/Management/WMIGenerator.cs +++ b/src/libraries/System.Management/src/System/Management/WMIGenerator.cs @@ -43,7 +43,7 @@ public enum CodeLanguage /// /// Used to generate a strongly-typed code class for a given WMI class. /// - internal class ManagementClassGenerator + internal sealed class ManagementClassGenerator { private readonly string VSVERSION = "8.0.0.0"; private string OriginalServer = string.Empty; @@ -316,7 +316,7 @@ public string ManagementClassName { //public bool AutoCommit { // get { - // return AutoCommitProp;; + // return AutoCommitProp; // } // set { // AutoCommitProp; = value; @@ -439,7 +439,7 @@ public string ManagementClassName { //Now Enumerate all the methods GenerateMethods(); - //Now declare the private class variables + //Now declare the private sealed class variables //private Wmi_SystemProps SystemProps GeneratePrivateMember(PrivateNamesUsed["SystemPropertiesObject"].ToString(), PublicNamesUsed["SystemPropertiesClass"].ToString(), null); diff --git a/src/libraries/System.Management/src/System/Management/WmiEventSink.cs b/src/libraries/System.Management/src/System/Management/WmiEventSink.cs index d70cc2c39a1e6..151b16d665a92 100644 --- a/src/libraries/System.Management/src/System/Management/WmiEventSink.cs +++ b/src/libraries/System.Management/src/System/Management/WmiEventSink.cs @@ -235,7 +235,7 @@ internal void ReleaseStub() // Special sink implementation for ManagementObject.Get // Doesn't issue ObjectReady events - internal class WmiGetEventSink : WmiEventSink + internal sealed class WmiGetEventSink : WmiEventSink { private readonly ManagementObject managementObject; diff --git a/src/libraries/System.Management/src/System/Management/wmiutil.cs b/src/libraries/System.Management/src/System/Management/wmiutil.cs index 03e67e332354a..05326c160d43e 100644 --- a/src/libraries/System.Management/src/System/Management/wmiutil.cs +++ b/src/libraries/System.Management/src/System/Management/wmiutil.cs @@ -21,7 +21,7 @@ IntPtr pObjParam } //Class for calling GetErrorInfo from managed code - internal class WbemErrorInfo + internal static class WbemErrorInfo { public static IWbemClassObjectFreeThreaded GetErrorInfo() { diff --git a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs index 4d0266a607cfd..d2bb7a7622d1d 100644 --- a/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs +++ b/src/libraries/System.Memory.Data/ref/System.Memory.Data.cs @@ -12,6 +12,7 @@ public BinaryData(byte[] data) { } public BinaryData(object? jsonSerializable, System.Text.Json.JsonSerializerOptions? options = null, System.Type? type = null) { } public BinaryData(System.ReadOnlyMemory data) { } public BinaryData(string data) { } + public static BinaryData Empty { get; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? obj) { throw null; } public static System.BinaryData FromBytes(byte[] data) { throw null; } diff --git a/src/libraries/System.Memory.Data/src/System/BinaryData.cs b/src/libraries/System.Memory.Data/src/System/BinaryData.cs index 8e9744a74c881..35c1fcf9ae1cd 100644 --- a/src/libraries/System.Memory.Data/src/System/BinaryData.cs +++ b/src/libraries/System.Memory.Data/src/System/BinaryData.cs @@ -22,6 +22,11 @@ public class BinaryData /// private readonly ReadOnlyMemory _bytes; + /// + /// Returns an empty BinaryData. + /// + public static BinaryData Empty { get; } = new BinaryData(ReadOnlyMemory.Empty); + /// /// Creates a instance by wrapping the /// provided byte array. diff --git a/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs b/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs index b3ffa187f454b..9486ac8270f46 100644 --- a/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs +++ b/src/libraries/System.Memory.Data/tests/BinaryDataTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; @@ -572,6 +573,18 @@ public void CloseStreamValidation() } + [Fact] + public void EmptyIsEmpty() + { + Assert.Equal(Array.Empty(), BinaryData.Empty.ToArray()); + } + + [Fact] + public void EmptyIsSingleton() + { + Assert.Same(BinaryData.Empty, BinaryData.Empty); + } + private class TestModel { public string A { get; set; } diff --git a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs index 4bd3eadb22fa7..62ea2342c2881 100644 --- a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs +++ b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs @@ -248,7 +248,7 @@ internal SequencePosition Seek(long offset, ExceptionArgument exceptionArgument int currentLength = startSegment.Memory.Length - startIndex; // Position in start segment, defer to single segment seek - if (currentLength > offset) + if (currentLength > offset || offset == 0) goto IsSingleSegment; if (currentLength < 0) diff --git a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs index 2020eb4f78cac..fa69d4d733971 100644 --- a/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs +++ b/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.cs @@ -62,7 +62,7 @@ public bool IsSingleSegment public SequencePosition Start { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new SequencePosition(_startObject, _startInteger); + get => new SequencePosition(_startObject, GetIndex(_startInteger)); } /// @@ -71,7 +71,7 @@ public SequencePosition Start public SequencePosition End { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new SequencePosition(_endObject, _endInteger); + get => new SequencePosition(_endObject, GetIndex(_endInteger)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -506,7 +506,7 @@ public override string ToString() } } - return string.Format("System.Buffers.ReadOnlySequence<{0}>[{1}]", typeof(T).Name, Length); + return $"System.Buffers.ReadOnlySequence<{typeof(T).Name}>[{Length}]"; } /// @@ -660,6 +660,10 @@ private enum SequenceType internal static class ReadOnlySequence { + /// + /// Flag that allows encoding the . + /// + /// public const int FlagBitMask = 1 << 31; public const int IndexBitMask = ~FlagBitMask; diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs index e6ba0879b0310..0ee99479128bc 100644 --- a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs @@ -486,7 +486,14 @@ private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap) uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F)); - return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24); + if (BitConverter.IsLittleEndian) + { + return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24); + } + else + { + return (i0 << 24) | (i1 << 16) | (i2 << 8) | i3; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -501,7 +508,14 @@ private static unsafe uint EncodeAndPadOne(byte* twoBytes, ref byte encodingMap) uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F)); uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F)); - return i0 | (i1 << 8) | (i2 << 16) | (EncodingPad << 24); + if (BitConverter.IsLittleEndian) + { + return i0 | (i1 << 8) | (i2 << 16) | (EncodingPad << 24); + } + else + { + return (i0 << 24) | (i1 << 16) | (i2 << 8) | EncodingPad; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -514,7 +528,14 @@ private static unsafe uint EncodeAndPadTwo(byte* oneByte, ref byte encodingMap) uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10)); uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F)); - return i0 | (i1 << 8) | (EncodingPad << 16) | (EncodingPad << 24); + if (BitConverter.IsLittleEndian) + { + return i0 | (i1 << 8) | (EncodingPad << 16) | (EncodingPad << 24); + } + else + { + return (i0 << 24) | (i1 << 16) | (EncodingPad << 8) | EncodingPad; + } } private const uint EncodingPad = '='; // '=', for padding diff --git a/src/libraries/System.Memory/tests/Binary/BinaryReaderUnitTests.cs b/src/libraries/System.Memory/tests/Binary/BinaryReaderUnitTests.cs index b93985c77c450..b4fb102bb1e6d 100644 --- a/src/libraries/System.Memory/tests/Binary/BinaryReaderUnitTests.cs +++ b/src/libraries/System.Memory/tests/Binary/BinaryReaderUnitTests.cs @@ -14,9 +14,15 @@ public class BinaryReaderUnitTests [Fact] public void SpanRead() { - Assert.True(BitConverter.IsLittleEndian); - - ulong value = 0x8877665544332211; // [11 22 33 44 55 66 77 88] + ulong value; // [11 22 33 44 55 66 77 88] + if (BitConverter.IsLittleEndian) + { + value = 0x8877665544332211; + } + else + { + value = 0x1122334455667788; + } Span span; unsafe { @@ -113,9 +119,15 @@ public void SpanRead() [Fact] public void ReadOnlySpanRead() { - Assert.True(BitConverter.IsLittleEndian); - - ulong value = 0x8877665544332211; // [11 22 33 44 55 66 77 88] + ulong value; // [11 22 33 44 55 66 77 88] + if (BitConverter.IsLittleEndian) + { + value = 0x8877665544332211; + } + else + { + value = 0x1122334455667788; + } ReadOnlySpan span; unsafe { @@ -282,8 +294,6 @@ public void ReadOnlySpanReadFail() [Fact] public void SpanWriteAndReadBigEndianHeterogeneousStruct() { - Assert.True(BitConverter.IsLittleEndian); - Span spanBE = new byte[Unsafe.SizeOf()]; WriteInt16BigEndian(spanBE, s_testStruct.S0); @@ -358,8 +368,6 @@ public void SpanWriteAndReadBigEndianHeterogeneousStruct() [Fact] public void SpanWriteAndReadLittleEndianHeterogeneousStruct() { - Assert.True(BitConverter.IsLittleEndian); - Span spanLE = new byte[Unsafe.SizeOf()]; WriteInt16LittleEndian(spanLE, s_testStruct.S0); @@ -434,7 +442,6 @@ public void SpanWriteAndReadLittleEndianHeterogeneousStruct() [Fact] public void ReadingStructFieldByFieldOrReadAndReverseEndianness() { - Assert.True(BitConverter.IsLittleEndian); Span spanBE = new byte[Unsafe.SizeOf()]; var testExplicitStruct = new TestHelpers.TestStructExplicit diff --git a/src/libraries/System.Memory/tests/Binary/BinaryWriterUnitTests.cs b/src/libraries/System.Memory/tests/Binary/BinaryWriterUnitTests.cs index be50576df78ae..2f9011470521a 100644 --- a/src/libraries/System.Memory/tests/Binary/BinaryWriterUnitTests.cs +++ b/src/libraries/System.Memory/tests/Binary/BinaryWriterUnitTests.cs @@ -14,8 +14,6 @@ public class BinaryWriterUnitTests [Fact] public void SpanWrite() { - Assert.True(BitConverter.IsLittleEndian); - Span span = new byte[8]; byte byteValue = 0x11; @@ -92,7 +90,6 @@ public void SpanWrite() [InlineData(0x00FF)] public void SpanWriteInt16(short value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[2]); WriteInt16BigEndian(span, value); short read = ReadInt16BigEndian(span); @@ -121,7 +118,6 @@ public void SpanWriteInt16(short value) [InlineData(0x00FF)] public void SpanWriteUInt16(ushort value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[2]); WriteUInt16BigEndian(span, value); ushort read = ReadUInt16BigEndian(span); @@ -152,7 +148,6 @@ public void SpanWriteUInt16(ushort value) [InlineData(0x000000FF)] public void SpanWriteInt32(int value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[4]); WriteInt32BigEndian(span, value); int read = ReadInt32BigEndian(span); @@ -183,7 +178,6 @@ public void SpanWriteInt32(int value) [InlineData(0x000000FF)] public void SpanWriteUInt32(uint value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[4]); WriteUInt32BigEndian(span, value); uint read = ReadUInt32BigEndian(span); @@ -218,7 +212,6 @@ public void SpanWriteUInt32(uint value) [InlineData(0x00000000000000FF)] public void SpanWriteInt64(long value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[8]); WriteInt64BigEndian(span, value); long read = ReadInt64BigEndian(span); @@ -253,7 +246,6 @@ public void SpanWriteInt64(long value) [InlineData(0x00000000000000FF)] public void SpanWriteUInt64(ulong value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[8]); WriteUInt64BigEndian(span, value); ulong read = ReadUInt64BigEndian(span); @@ -290,7 +282,6 @@ public static IEnumerable SpanWriteHalf_TestData() [MemberData(nameof(SpanWriteHalf_TestData))] public void SpanWriteHalf(Half value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[4]); WriteHalfBigEndian(span, value); Half read = ReadHalfBigEndian(span); @@ -321,7 +312,6 @@ public void SpanWriteHalf(Half value) [InlineData(float.NaN)] public void SpanWriteSingle(float value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[4]); WriteSingleBigEndian(span, value); float read = ReadSingleBigEndian(span); @@ -352,7 +342,6 @@ public void SpanWriteSingle(float value) [InlineData(double.NaN)] public void SpanWriteDouble(double value) { - Assert.True(BitConverter.IsLittleEndian); var span = new Span(new byte[8]); WriteDoubleBigEndian(span, value); double read = ReadDoubleBigEndian(span); diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesReadOnlySpan.cs b/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesReadOnlySpan.cs index 7b4408c31eef9..a3789ecd1bdda 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesReadOnlySpan.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesReadOnlySpan.cs @@ -12,7 +12,15 @@ public static partial class MemoryMarshalTests [Fact] public static void ReadOnlySpan_AsBytesUIntToByte() { - uint[] a = { 0x44332211, 0x88776655 }; + uint[] a; + if (BitConverter.IsLittleEndian) + { + a = new uint[] { 0x44332211, 0x88776655 }; + } + else + { + a = new uint[] { 0x11223344, 0x55667788 }; + } ReadOnlySpan span = new ReadOnlySpan(a); ReadOnlySpan asBytes = MemoryMarshal.AsBytes(span); diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesSpan.cs b/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesSpan.cs index 85f53e8dcffc9..d1402266c295e 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesSpan.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/AsBytesSpan.cs @@ -12,7 +12,15 @@ public static partial class MemoryMarshalTests [Fact] public static void Span_AsBytesUIntToByte() { - uint[] a = { 0x44332211, 0x88776655 }; + uint[] a; + if (BitConverter.IsLittleEndian) + { + a = new uint[] { 0x44332211, 0x88776655 }; + } + else + { + a = new uint[] { 0x11223344, 0x55667788 }; + } Span span = new Span(a); Span asBytes = MemoryMarshal.AsBytes(span); diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/CastReadOnlySpan.cs b/src/libraries/System.Memory/tests/MemoryMarshal/CastReadOnlySpan.cs index 019c9a5b42012..0c5bb81020266 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/CastReadOnlySpan.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/CastReadOnlySpan.cs @@ -12,7 +12,15 @@ public static partial class MemoryMarshalTests [Fact] public static void CastReadOnlySpanUIntToUShort() { - uint[] a = { 0x44332211, 0x88776655 }; + uint[] a; + if (BitConverter.IsLittleEndian) + { + a = new uint[] { 0x44332211, 0x88776655 }; + } + else + { + a = new uint[] { 0x22114433, 0x66558877 }; + } ReadOnlySpan span = new ReadOnlySpan(a); ReadOnlySpan asUShort = MemoryMarshal.Cast(span); @@ -23,7 +31,15 @@ public static void CastReadOnlySpanUIntToUShort() [Fact] public static void CastReadOnlySpanShortToLong() { - short[] a = { 0x1234, 0x2345, 0x3456, 0x4567, 0x5678 }; + short[] a; + if (BitConverter.IsLittleEndian) + { + a = new short[] { 0x1234, 0x2345, 0x3456, 0x4567, 0x5678 }; + } + else + { + a = new short[] { 0x4567, 0x3456, 0x2345, 0x1234, 0x5678 }; + } ReadOnlySpan span = new ReadOnlySpan(a); ReadOnlySpan asLong = MemoryMarshal.Cast(span); diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/CastSpan.cs b/src/libraries/System.Memory/tests/MemoryMarshal/CastSpan.cs index b77e853650ef4..796ea5dc05436 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/CastSpan.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/CastSpan.cs @@ -13,7 +13,15 @@ public static partial class MemoryMarshalTests [Fact] public static void CastSpanUIntToUShort() { - uint[] a = { 0x44332211, 0x88776655 }; + uint[] a; + if (BitConverter.IsLittleEndian) + { + a = new uint[] { 0x44332211, 0x88776655 }; + } + else + { + a = new uint[] { 0x22114433, 0x66558877 }; + } Span span = new Span(a); Span asUShort = MemoryMarshal.Cast(span); @@ -35,7 +43,15 @@ public static void CastSpanToEmptyStruct() [Fact] public static void CastSpanShortToLong() { - short[] a = { 0x1234, 0x2345, 0x3456, 0x4567, 0x5678 }; + short[] a; + if (BitConverter.IsLittleEndian) + { + a = new short[] { 0x1234, 0x2345, 0x3456, 0x4567, 0x5678 }; + } + else + { + a = new short[] { 0x4567, 0x3456, 0x2345, 0x1234, 0x5678 }; + } Span span = new Span(a); Span asLong = MemoryMarshal.Cast(span); diff --git a/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs b/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs index 66832ef88c699..eb78a69e2008b 100644 --- a/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs +++ b/src/libraries/System.Memory/tests/ReadOnlyBuffer/ReadOnlySequenceTests.byte.cs @@ -180,6 +180,36 @@ public void CopyTo_ThrowsWhenSourceLargerThenDestination() }); } + [Fact] + public void End_EqualToGetPositionSize() + { + ReadOnlySequence buffer = Factory.CreateOfSize(5); + Assert.Equal(buffer.End, buffer.GetPosition(5)); + } + + [Fact] + public void Start_EqualToGetPosition0() + { + ReadOnlySequence buffer = Factory.CreateOfSize(5); + Assert.Equal(buffer.Start, buffer.GetPosition(0)); + } + + [Fact] + public void InnerPositionAreNotEqualToEnd() + { + ReadOnlySequence buffer = Factory.CreateOfSize(3); + Assert.NotEqual(buffer.GetPosition(1), buffer.End); + Assert.NotEqual(buffer.GetPosition(2), buffer.End); + } + + [Fact] + public void InnerPositionAreNotEqualToStart() + { + ReadOnlySequence buffer = Factory.CreateOfSize(3); + Assert.NotEqual(buffer.GetPosition(1), buffer.Start); + Assert.NotEqual(buffer.GetPosition(2), buffer.Start); + } + public static TheoryData, ReadOnlySequence>> ValidSliceCases => new TheoryData, ReadOnlySequence>> { b => b.Slice(5), diff --git a/src/libraries/System.Memory/tests/SequenceReader/BinaryExtensions.cs b/src/libraries/System.Memory/tests/SequenceReader/BinaryExtensions.cs index 3d0c4379c74a0..5785bb5ca13a4 100644 --- a/src/libraries/System.Memory/tests/SequenceReader/BinaryExtensions.cs +++ b/src/libraries/System.Memory/tests/SequenceReader/BinaryExtensions.cs @@ -51,19 +51,19 @@ public void MultiSegmentBytesReaderNumbers() Assert.Equal(BitConverter.ToInt32(new byte[] { 0, 1, 0, 2 }), intValue); Assert.True(reader.TryReadBigEndian(out intValue)); - Assert.Equal(BitConverter.ToInt32(new byte[] { 4, 3, 2, 1 }), intValue); + Assert.Equal(0x01020304, intValue); Assert.True(reader.TryReadLittleEndian(out long longValue)); - Assert.Equal(BitConverter.ToInt64(new byte[] { 5, 6, 7, 8, 9, 0, 1, 2 }), longValue); + Assert.Equal(0x0201000908070605L, longValue); Assert.True(reader.TryReadBigEndian(out longValue)); - Assert.Equal(BitConverter.ToInt64(new byte[] { 0, 9, 8, 7, 6, 5, 4, 3 }), longValue); + Assert.Equal(0x0304050607080900L, longValue); Assert.True(reader.TryReadLittleEndian(out short shortValue)); - Assert.Equal(BitConverter.ToInt16(new byte[] { 1, 2 }), shortValue); + Assert.Equal(0x0201, shortValue); Assert.True(reader.TryReadBigEndian(out shortValue)); - Assert.Equal(BitConverter.ToInt16(new byte[] { 4, 3 }), shortValue); + Assert.Equal(0x0304, shortValue); } } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs index 5407ccdf9b476..f659c9c2f07e2 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpAuthHelper.cs @@ -7,7 +7,7 @@ namespace System.Net.Http { - internal class WinHttpAuthHelper + internal sealed class WinHttpAuthHelper { // Fast lookup table to convert WINHTTP_AUTH constants to strings. // WINHTTP_AUTH_SCHEME_BASIC = 0x00000001; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs index 67b2f99fcadaf..5e862724d0e7f 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpChannelBinding.cs @@ -10,7 +10,7 @@ namespace System.Net.Http { - internal class WinHttpChannelBinding : ChannelBinding + internal sealed class WinHttpChannelBinding : ChannelBinding { private int _size; private string _cachedToString; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpException.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpException.cs index 2afd23a347f76..54fa11b8fea5a 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpException.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpException.cs @@ -10,7 +10,7 @@ namespace System.Net.Http { [Serializable] - internal class WinHttpException : Win32Exception + internal sealed class WinHttpException : Win32Exception { public WinHttpException(int error, string message) : base(error, message) { diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs index 5db9aa5a1a862..b2d6822b2596b 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpRequestStream.cs @@ -208,8 +208,7 @@ private async Task InternalWriteChunkedModeAsync(byte[] buffer, int offset, int Debug.Assert(_chunkedMode); Debug.Assert(count > 0); - string chunkSizeString = string.Format("{0:x}\r\n", count); - byte[] chunkSize = Encoding.UTF8.GetBytes(chunkSizeString); + byte[] chunkSize = Encoding.UTF8.GetBytes($"{count:x}\r\n"); await InternalWriteDataAsync(chunkSize, 0, chunkSize.Length, token).ConfigureAwait(false); diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs index b3fb117220227..104808b472a9f 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTraceHelper.cs @@ -92,7 +92,7 @@ private static string GetStringFromInternetStatus(uint status) => Interop.WinHttp.WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE => "STATUS_GETPROXYFORURL_COMPLETE", Interop.WinHttp.WINHTTP_CALLBACK_STATUS_CLOSE_COMPLETE => "STATUS_CLOSE_COMPLETE", Interop.WinHttp.WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE => "STATUS_SHUTDOWN_COMPLETE", - _ => string.Format("0x{0:X}", status), + _ => $"0x{status:X}", }; } } diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs index 7be5a4c5a240d..1254ae8220bc7 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTrailersHelper.cs @@ -15,7 +15,7 @@ internal static class WinHttpTrailersHelper // Trailer property name was chosen to be descriptive and be unlikely to collide with a user set property. // Apps and libraries will use this key so it shouldn't change. private const string RequestMessagePropertyName = "__ResponseTrailers"; - private class HttpResponseTrailers : HttpHeaders + private sealed class HttpResponseTrailers : HttpHeaders { } #endif diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs index c3b4ddb6f6e76..1c3d5eea71799 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System/Net/Http/WinHttpTransportContext.cs @@ -8,7 +8,7 @@ namespace System.Net.Http { - internal class WinHttpTransportContext : TransportContext + internal sealed class WinHttpTransportContext : TransportContext { private WinHttpChannelBinding _channelBinding; diff --git a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/ClientCertificateHelper.cs b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/ClientCertificateHelper.cs index c284d2be59253..b0e144992937c 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/ClientCertificateHelper.cs +++ b/src/libraries/System.Net.Http.WinHttpHandler/tests/UnitTests/ClientCertificateHelper.cs @@ -13,7 +13,6 @@ public class ClientCertificateHelper private readonly X509Certificate2 _cert_KeyUsageIncludesDigitalSignature_EKUIncludesClientAuth_PrivateKey = new X509Certificate2( Convert.FromBase64String( - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Dummy certificate for testing.")] @"MIIKTgIBAzCCCgoGCSqGSIb3DQEHAaCCCfsEggn3MIIJ8zCCBgwGCSqGSIb3DQEHAaCCBf0EggX5 MIIF9TCCBfEGCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAiHDatvDr8QBQIC B9AEggTYv1r4ckwt7o6f6DCMHlb/zv4t7rPju+PP0PjoJ8kzPfj419aSeyPuE+65YH9WFDqafJed @@ -66,7 +65,6 @@ public class ClientCertificateHelper private readonly X509Certificate2 _cert_KeyUsageMissingDigitalSignature_EKUIncludesClientAuth_PrivateKey = new X509Certificate2( Convert.FromBase64String( - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Dummy certificate for testing.")] @"MIIKTgIBAzCCCgoGCSqGSIb3DQEHAaCCCfsEggn3MIIJ8zCCBgwGCSqGSIb3DQEHAaCCBf0EggX5 MIIF9TCCBfEGCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAiSNi65ZF5ZTQIC B9AEggTYRTivDtzHOWRR+MobtGFEUu6d1PiIlF1Ic84FWvmFCcJShkBmg3cBqDilqtamAkDkga4h @@ -119,7 +117,6 @@ public class ClientCertificateHelper private readonly X509Certificate2 _cert_KeyUsageIncludesDigitalSignature_EKUMissingClientAuth_PrivateKey = new X509Certificate2( Convert.FromBase64String( - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Dummy certificate for testing.")] @"MIIKRgIBAzCCCgIGCSqGSIb3DQEHAaCCCfMEggnvMIIJ6zCCBgQGCSqGSIb3DQEHAaCCBfUEggXx MIIF7TCCBekGCyqGSIb3DQEMCgECoIIE9jCCBPIwHAYKKoZIhvcNAQwBAzAOBAhCUuNQ0RqfZQIC B9AEggTQHCQRSiCiNI7egTvUaI1Z3tfeLwFWvG7B/za5v9fb97MExoyVQSDmUyUDTlVEcg3gVqJZ @@ -172,7 +169,6 @@ public class ClientCertificateHelper private readonly X509Certificate2 _cert_KeyUsageIncludesDigitalSignature_NoEKU_PrivateKey = new X509Certificate2( Convert.FromBase64String( - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Dummy certificate for testing.")] @"MIIKPgIBAzCCCfoGCSqGSIb3DQEHAaCCCesEggnnMIIJ4zCCBgwGCSqGSIb3DQEHAaCCBf0EggX5 MIIF9TCCBfEGCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAijQh1kbOZOYQIC B9AEggTY+wDp3V31Lh7f8YrsqEsyGZ+GlYvFhLWvDASjisYJi5NlQ0ONbf0KOXHVSvBj3tVyuHm4 @@ -225,7 +221,6 @@ public class ClientCertificateHelper private readonly X509Certificate2 _cert_KeyUsageIncludesDigitalSignature_EKUIncludesClientAuth_NoPrivateKey = new X509Certificate2( Convert.FromBase64String( - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Dummy certificate for testing.")] @"MIIDFjCCAf6gAwIBAgIQTm8+EF94L4FJ0nBFl5LICzANBgkqhkiG9w0BAQsFADAb MRkwFwYDVQQDDBB1c2VyQGV4YW1wbGUuY29tMCAXDTE1MTAwNTEwMDMwMFoYDzIx MTUxMDA1MTAwMzAwWjAbMRkwFwYDVQQDDBB1c2VyQGV4YW1wbGUuY29tMIIBIjAN diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml similarity index 62% rename from src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.xml rename to src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml index b84fc3ee1c1e5..e11daf0bd241f 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Suppressions.LibraryBuild.xml @@ -6,6 +6,7 @@ IL2075 member M:System.Net.Http.HttpClient.CreateDefaultHandler() + The Xamarin.iOS and Mono.Android libraries are not present when running the trimmer analysis during our build. A consuming application will get a warning if these libraries aren't present when trimming the full app. diff --git a/src/libraries/System.Net.Http/src/Resources/Strings.resx b/src/libraries/System.Net.Http/src/Resources/Strings.resx index 2c03bfb4e7390..501437eb2b0e5 100644 --- a/src/libraries/System.Net.Http/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Http/src/Resources/Strings.resx @@ -606,4 +606,7 @@ Synchronous reads are not supported, use ReadAsync instead. + + The proxy tunnel request to proxy '{0}' failed with status code '{1}'." + 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 533120791bb94..a4298dba53a70 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 @@ -324,7 +324,7 @@ protected internal override async Task SendAsync(HttpReques } } - private class WasmFetchResponse : IDisposable + private sealed class WasmFetchResponse : IDisposable { private readonly JSObject _fetchResponse; private readonly JSObject _abortController; @@ -355,29 +355,19 @@ public WasmFetchResponse(JSObject fetchResponse, JSObject abortController, Cance public Task JSON() => (Task)_fetchResponse.Invoke("json"); public void Dispose() - { - // Dispose of unmanaged resources. - Dispose(true); - } - - // Protected implementation of Dispose pattern. - protected virtual void Dispose(bool disposing) { if (_isDisposed) return; _isDisposed = true; - if (disposing) - { - _abortCts.Cancel(); - _abortCts.Dispose(); - _abortRegistration.Dispose(); - } + + _abortCts.Cancel(); + _abortCts.Dispose(); + _abortRegistration.Dispose(); _fetchResponse?.Dispose(); _abortController?.Dispose(); } - } private sealed class BrowserHttpContent : HttpContent diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs index ab88b3578df1a..4e4d730c4c188 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/DiagnosticsHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; @@ -90,7 +91,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : // Only send start event to users who subscribed for it, but start activity anyway if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ActivityStartName)) { - diagnosticListener.StartActivity(activity, new ActivityStartData(request)); + StartActivity(diagnosticListener, activity, new ActivityStartData(request)); } else { @@ -102,7 +103,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : { long timestamp = Stopwatch.GetTimestamp(); loggingRequestId = Guid.NewGuid(); - diagnosticListener.Write(DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.RequestWriteNameDeprecated, new RequestData(request, loggingRequestId, timestamp)); } @@ -138,7 +139,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : // If request was initially instrumented, Activity.Current has all necessary context for logging // Request is passed to provide some context if instrumentation was disabled and to avoid // extensive Activity.Tags usage to tunnel request properties - diagnosticListener.Write(DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ExceptionEventName, new ExceptionData(ex, request)); } throw; } @@ -147,7 +148,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : // always stop activity if it was started if (activity != null) { - diagnosticListener.StopActivity(activity, new ActivityStopData( + StopActivity(diagnosticListener, activity, new ActivityStopData( response, // If request is failed or cancelled, there is no response, therefore no information about request; // pass the request in the payload, so consumers can have it in Stop for failed/canceled requests @@ -159,7 +160,7 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : if (diagnosticListener.IsEnabled(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated)) { long timestamp = Stopwatch.GetTimestamp(); - diagnosticListener.Write(DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, + Write(diagnosticListener, DiagnosticsHandlerLoggingStrings.ResponseWriteNameDeprecated, new ResponseData( response, loggingRequestId, @@ -173,6 +174,12 @@ await base.SendAsync(request, cancellationToken).ConfigureAwait(false) : private sealed class ActivityStartData { + // matches the properties selected in https://github.com/dotnet/diagnostics/blob/ffd0254da3bcc47847b1183fa5453c0877020abd/src/Microsoft.Diagnostics.Monitoring.EventPipe/Configuration/HttpRequestSourceConfiguration.cs#L36-L40 + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.Method), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(Uri.Host), typeof(Uri))] + [DynamicDependency(nameof(Uri.Port), typeof(Uri))] internal ActivityStartData(HttpRequestMessage request) { Request = request; @@ -201,6 +208,14 @@ internal ActivityStopData(HttpResponseMessage? response, HttpRequestMessage requ private sealed class ExceptionData { + // preserve the same properties as ActivityStartData above + common Exception properties + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.Method), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(Uri.Host), typeof(Uri))] + [DynamicDependency(nameof(Uri.Port), typeof(Uri))] + [DynamicDependency(nameof(System.Exception.Message), typeof(Exception))] + [DynamicDependency(nameof(System.Exception.StackTrace), typeof(Exception))] internal ExceptionData(Exception exception, HttpRequestMessage request) { Exception = exception; @@ -215,6 +230,12 @@ internal ExceptionData(Exception exception, HttpRequestMessage request) private sealed class RequestData { + // preserve the same properties as ActivityStartData above + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.Method), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(HttpRequestMessage.RequestUri), typeof(HttpRequestMessage))] + [DynamicDependency(nameof(Uri.Host), typeof(Uri))] + [DynamicDependency(nameof(Uri.Port), typeof(Uri))] internal RequestData(HttpRequestMessage request, Guid loggingRequestId, long timestamp) { Request = request; @@ -231,6 +252,7 @@ internal RequestData(HttpRequestMessage request, Guid loggingRequestId, long tim private sealed class ResponseData { + [DynamicDependency(nameof(HttpResponseMessage.StatusCode), typeof(HttpResponseMessage))] internal ResponseData(HttpResponseMessage? response, Guid loggingRequestId, long timestamp, TaskStatus requestTaskStatus) { Response = response; @@ -316,6 +338,36 @@ private static void InjectHeaders(Activity currentActivity, HttpRequestMessage r } } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The values being passed into Write have the commonly used properties being preserved with DynamicDependency.")] + private static void Write<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + string name, + T value) + { + diagnosticSource.Write(name, value); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The args being passed into StartActivity have the commonly used properties being preserved with DynamicDependency.")] + private static Activity StartActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + Activity activity, + T? args) + { + return diagnosticSource.StartActivity(activity, args); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", + Justification = "The args being passed into StopActivity have the commonly used properties being preserved with DynamicDependency.")] + private static void StopActivity<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>( + DiagnosticSource diagnosticSource, + Activity activity, + T? args) + { + diagnosticSource.StopActivity(activity, args); + } + #endregion } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderParser.cs index f6c65298d179a..30fe2d650b64c 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/AltSvcHeaderParser.cs @@ -10,7 +10,7 @@ namespace System.Net.Http.Headers /// /// Parses Alt-Svc header values, per RFC 7838 Section 3. /// - internal class AltSvcHeaderParser : BaseHeaderParser + internal sealed class AltSvcHeaderParser : BaseHeaderParser { internal const long DefaultMaxAgeTicks = 24 * TimeSpan.TicksPerHour; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ByteArrayHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ByteArrayHeaderParser.cs index b5631b2253c8d..42c88602da00a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ByteArrayHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ByteArrayHeaderParser.cs @@ -8,7 +8,7 @@ namespace System.Net.Http.Headers { // Don't derive from BaseHeaderParser since parsing the Base64 string is delegated to Convert.FromBase64String() // which will remove leading, trailing, and whitespace in the middle of the string. - internal class ByteArrayHeaderParser : HttpHeaderParser + internal sealed class ByteArrayHeaderParser : HttpHeaderParser { internal static readonly ByteArrayHeaderParser Parser = new ByteArrayHeaderParser(); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderParser.cs index 53480344bcddc..90acab59864ed 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderParser.cs @@ -5,7 +5,7 @@ namespace System.Net.Http.Headers { - internal class CacheControlHeaderParser : BaseHeaderParser + internal sealed class CacheControlHeaderParser : BaseHeaderParser { internal static readonly CacheControlHeaderParser Parser = new CacheControlHeaderParser(); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/DateHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/DateHeaderParser.cs index cfa45a84d38ad..587622d43276e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/DateHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/DateHeaderParser.cs @@ -8,7 +8,7 @@ namespace System.Net.Http.Headers { // Don't derive from BaseHeaderParser since parsing is delegated to DateTimeOffset.TryParseExact() // which will remove leading, trailing, and whitespace in the middle of the string. - internal class DateHeaderParser : HttpHeaderParser + internal sealed class DateHeaderParser : HttpHeaderParser { internal static readonly DateHeaderParser Parser = new DateHeaderParser(); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderUtilities.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderUtilities.cs index 91967beab099c..febc456e2fdfb 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderUtilities.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderUtilities.cs @@ -357,7 +357,7 @@ internal static void DumpHeaders(StringBuilder sb, params HttpHeaders?[] headers { if (headers[i] is HttpHeaders hh) { - foreach (KeyValuePair> header in hh) + foreach (KeyValuePair header in hh.EnumerateWithoutValidation()) { foreach (string headerValue in header.Value) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs index edb3077d6a014..59a3e4a965c54 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpHeaders.cs @@ -279,28 +279,29 @@ internal IEnumerable> GetHeaderStrings() } } - internal string GetHeaderString(HeaderDescriptor descriptor, object? exclude = null) => - TryGetHeaderValue(descriptor, out object? info) ? - GetHeaderString(descriptor, info, exclude) : - string.Empty; - - private string GetHeaderString(HeaderDescriptor descriptor, object info, object? exclude = null) + internal string GetHeaderString(HeaderDescriptor descriptor, object? exclude = null) { - string[] values = GetValuesAsStrings(descriptor, info, exclude); - - if (values.Length == 1) + if (TryGetHeaderValue(descriptor, out object? info)) { - return values[0]; - } + string[] values = GetValuesAsStrings(descriptor, info, exclude); - // Note that if we get multiple values for a header that doesn't support multiple values, we'll - // just separate the values using a comma (default separator). - string? separator = HttpHeaderParser.DefaultSeparator; - if ((descriptor.Parser != null) && (descriptor.Parser.SupportsMultipleValues)) - { - separator = descriptor.Parser.Separator; + if (values.Length == 1) + { + return values[0]; + } + + // Note that if we get multiple values for a header that doesn't support multiple values, we'll + // just separate the values using a comma (default separator). + string? separator = HttpHeaderParser.DefaultSeparator; + if (descriptor.Parser != null && descriptor.Parser.SupportsMultipleValues) + { + separator = descriptor.Parser.Separator; + } + + return string.Join(separator, values); } - return string.Join(separator, values); + + return string.Empty; } #region IEnumerable>> Members @@ -348,6 +349,23 @@ private IEnumerator>> GetEnumeratorCore } } + internal IEnumerable> EnumerateWithoutValidation() + { + if (_headerStore == null) + { + yield break; + } + + foreach (KeyValuePair header in _headerStore) + { + string[] values = TryGetHeaderValue(header.Key, out object? info) ? + GetValuesAsStrings(header.Key, info) : + Array.Empty(); + + yield return new KeyValuePair(header.Key.Name, values); + } + } + #endregion #region IEnumerable Members @@ -554,18 +572,7 @@ internal virtual void AddHeaders(HttpHeaders sourceHeaders) object sourceValue = header.Value; if (sourceValue is HeaderStoreItemInfo info) { - if (!sourceHeaders.ParseRawHeaderValues(header.Key, info, removeEmptyHeader: false)) - { - // If after trying to parse source header values no value is left (i.e. all values contain - // invalid newline chars), delete it and skip to the next header. ParseRawHeaderValues takes - // a lock, and it'll only ever return false once for one thread, so we don't need to be - // concerned about concurrent removals on the HttpClient.DefaultRequestHeaders source. - sourceHeadersStore.Remove(header.Key); - } - else - { - AddHeaderInfo(header.Key, info); - } + AddHeaderInfo(header.Key, info); } else { @@ -580,18 +587,19 @@ private void AddHeaderInfo(HeaderDescriptor descriptor, HeaderStoreItemInfo sour { HeaderStoreItemInfo destinationInfo = CreateAndAddHeaderToStore(descriptor); - // We have custom header values. The parsed values are strings. + // Always copy raw values + destinationInfo.RawValue = CloneStringHeaderInfoValues(sourceInfo.RawValue); + if (descriptor.Parser == null) { - Debug.Assert((sourceInfo.RawValue == null) && (sourceInfo.InvalidValue == null), - "No raw or invalid values expected for custom headers."); - + // We have custom header values. The parsed values are strings. // Custom header values are always stored as string or list of strings. + Debug.Assert(sourceInfo.InvalidValue == null, "No invalid values expected for custom headers."); destinationInfo.ParsedValue = CloneStringHeaderInfoValues(sourceInfo.ParsedValue); } else { - // We have a parser, so we have to copy invalid values and clone parsed values. + // We have a parser, so we also have to copy invalid values and clone parsed values. // Invalid values are always strings. Strings are immutable. So we only have to clone the // collection (if there is one). @@ -1309,7 +1317,7 @@ private bool AreEqual(object value, object? storeValue, IEqualityComparer? compa #region Private Classes - internal class HeaderStoreItemInfo + internal sealed class HeaderStoreItemInfo { internal HeaderStoreItemInfo() { } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int32NumberHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int32NumberHeaderParser.cs index 63e46f5f8055e..2b052aca7be64 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int32NumberHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int32NumberHeaderParser.cs @@ -6,7 +6,7 @@ namespace System.Net.Http.Headers { - internal class Int32NumberHeaderParser : BaseHeaderParser + internal sealed class Int32NumberHeaderParser : BaseHeaderParser { // Note that we don't need a custom comparer even though we have a value type that gets boxed (comparing two // equal boxed value types returns 'false' since the object instances used for boxing the two values are diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int64NumberHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int64NumberHeaderParser.cs index 9a1029d7b5838..12768a73d03cb 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int64NumberHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/Int64NumberHeaderParser.cs @@ -6,7 +6,7 @@ namespace System.Net.Http.Headers { - internal class Int64NumberHeaderParser : BaseHeaderParser + internal sealed class Int64NumberHeaderParser : BaseHeaderParser { // Note that we don't need a custom comparer even though we have a value type that gets boxed (comparing two // equal boxed value types returns 'false' since the object instances used for boxing the two values are diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs index 42e4cf3f043fc..cd9884eff1678 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/MediaTypeHeaderParser.cs @@ -5,7 +5,7 @@ namespace System.Net.Http.Headers { - internal class MediaTypeHeaderParser : BaseHeaderParser + internal sealed class MediaTypeHeaderParser : BaseHeaderParser { private readonly bool _supportsMultipleValues; private readonly Func _mediaTypeCreator; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ProductInfoHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ProductInfoHeaderParser.cs index b0a17a4620c48..f224eeb10481d 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ProductInfoHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ProductInfoHeaderParser.cs @@ -8,7 +8,7 @@ namespace System.Net.Http.Headers { // Don't derive from BaseHeaderParser since empty values are not supported. After a ' ' separator a valid value // must follow. Also leading separators are not allowed. - internal class ProductInfoHeaderParser : HttpHeaderParser + internal sealed class ProductInfoHeaderParser : HttpHeaderParser { // Unlike most other headers, User-Agent and Server use whitespace as separators private const string separator = " "; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TimeSpanHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TimeSpanHeaderParser.cs index 6aeaac12b9b0e..1d5f56c8803d5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TimeSpanHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TimeSpanHeaderParser.cs @@ -6,7 +6,7 @@ namespace System.Net.Http.Headers { - internal class TimeSpanHeaderParser : BaseHeaderParser + internal sealed class TimeSpanHeaderParser : BaseHeaderParser { internal static readonly TimeSpanHeaderParser Parser = new TimeSpanHeaderParser(); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TransferCodingHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TransferCodingHeaderParser.cs index 1faee74b7a899..6d87ed5096a29 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TransferCodingHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/TransferCodingHeaderParser.cs @@ -5,7 +5,7 @@ namespace System.Net.Http.Headers { - internal class TransferCodingHeaderParser : BaseHeaderParser + internal sealed class TransferCodingHeaderParser : BaseHeaderParser { private readonly Func _transferCodingCreator; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/UriHeaderParser.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/UriHeaderParser.cs index 37c71f52accbe..08d15634942ec 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/UriHeaderParser.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/UriHeaderParser.cs @@ -9,7 +9,7 @@ namespace System.Net.Http.Headers { // Don't derive from BaseHeaderParser since parsing is delegated to Uri.TryCreate() // which will remove leading and trailing whitespace. - internal class UriHeaderParser : HttpHeaderParser + internal sealed class UriHeaderParser : HttpHeaderParser { private readonly UriKind _uriKind; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs index b2d27035c5534..c44dac794eeef 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs @@ -12,12 +12,13 @@ namespace System.Net.Http { - internal partial class AuthenticationHelper + internal static partial class AuthenticationHelper { // Define digest constants private const string Qop = "qop"; private const string Auth = "auth"; private const string AuthInt = "auth-int"; + private const string Domain = "domain"; private const string Nonce = "nonce"; private const string NC = "nc"; private const string Realm = "realm"; @@ -240,7 +241,7 @@ private static string ComputeHash(string data, string algorithm) } } - internal class DigestResponse + internal sealed class DigestResponse { internal readonly Dictionary Parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); internal const string NonceCount = "00000001"; @@ -402,9 +403,13 @@ private void Parse(string challenge) // Get the value. string? value = GetNextValue(challenge, parsedIndex, MustValueBeQuoted(key), out parsedIndex); + if (value == null) + break; + // Ensure value is valid. - if (string.IsNullOrEmpty(value) - && (value == null || !key.Equals(Opaque, StringComparison.OrdinalIgnoreCase))) + // Opaque and Domain can have empty string + if (value == string.Empty && + (!key.Equals(Opaque, StringComparison.OrdinalIgnoreCase) && !key.Equals(Domain, StringComparison.OrdinalIgnoreCase))) break; // Add the key-value pair to Parameters. diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index e4218588f8073..c16c2b18b9012 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -11,7 +11,7 @@ namespace System.Net.Http { - internal partial class AuthenticationHelper + internal static partial class AuthenticationHelper { private static Task InnerSendAsync(HttpRequestMessage request, bool async, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken) { @@ -63,15 +63,7 @@ private static async Task SendWithNtAuthAsync(HttpRequestMe if (response.Headers.ConnectionClose.GetValueOrDefault()) { // Server is closing the connection and asking us to authenticate on a new connection. - // expression returns null connection on error, was not able to use '!' for the expression -#pragma warning disable CS8600 - (connection, response) = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); -#pragma warning restore CS8600 - if (response != null) - { - return response; - } - + connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); connectionPool.IncrementConnectionCount(); connection!.Acquire(); isNewConnection = true; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.cs index 44d5a48c8cf9a..88b3f64d0ec7a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.cs @@ -10,7 +10,7 @@ namespace System.Net.Http { - internal partial class AuthenticationHelper + internal static partial class AuthenticationHelper { private const string BasicScheme = "Basic"; private const string DigestScheme = "Digest"; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingReadStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingReadStream.cs index 837358cf82173..5d0fb49db747a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingReadStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingReadStream.cs @@ -10,7 +10,7 @@ namespace System.Net.Http { - internal partial class HttpConnection + internal sealed partial class HttpConnection { private sealed class ChunkedEncodingReadStream : HttpContentReadStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs index e66dbf095cef9..1411e672c70f5 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ChunkedEncodingWriteStream.cs @@ -7,7 +7,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : IDisposable + internal sealed partial class HttpConnection : IDisposable { private sealed class ChunkedEncodingWriteStream : HttpContentWriteStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionCloseReadStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionCloseReadStream.cs index d061ae0969419..0fd011037b931 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionCloseReadStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionCloseReadStream.cs @@ -7,7 +7,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : IDisposable + internal sealed partial class HttpConnection : IDisposable { private sealed class ConnectionCloseReadStream : HttpContentReadStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthReadStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthReadStream.cs index 96c66654ae4ae..dd667f0706030 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthReadStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthReadStream.cs @@ -8,7 +8,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : IDisposable + internal sealed partial class HttpConnection : IDisposable { private sealed class ContentLengthReadStream : HttpContentReadStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs index e51c5e06cbe71..7ea59751c1825 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ContentLengthWriteStream.cs @@ -7,7 +7,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : IDisposable + internal sealed partial class HttpConnection : IDisposable { private sealed class ContentLengthWriteStream : HttpContentWriteStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs index 6f2b2112b9e1a..3b800986d7715 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs @@ -1587,12 +1587,19 @@ public bool IsExpired(long nowTicks, (_httpStreams.Count == 0) && ((nowTicks - _idleSinceTickCount) > connectionIdleTimeout.TotalMilliseconds)) { - if (NetEventSource.Log.IsEnabled()) Trace($"Connection no longer usable. Idle {TimeSpan.FromMilliseconds(nowTicks - _idleSinceTickCount)} > {connectionIdleTimeout}."); + if (NetEventSource.Log.IsEnabled()) Trace($"HTTP/2 connection no longer usable. Idle {TimeSpan.FromMilliseconds(nowTicks - _idleSinceTickCount)} > {connectionIdleTimeout}."); return true; } - return LifetimeExpired(nowTicks, connectionLifetime); + if (LifetimeExpired(nowTicks, connectionLifetime)) + { + if (NetEventSource.Log.IsEnabled()) Trace($"HTTP/2 connection no longer usable. Lifetime {TimeSpan.FromMilliseconds(nowTicks - CreationTickCount)} > {connectionLifetime}."); + + return true; + } + + return false; } private void AbortStreams(Exception abortException) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs index 07b8f1cd652b6..6eb28b57543a9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3Connection.cs @@ -652,7 +652,6 @@ private async Task ProcessServerControlStreamAsync(QuicStream stream, ArrayBuffe case Http3FrameType.Headers: // Servers should not send these frames to a control stream. case Http3FrameType.Data: case Http3FrameType.MaxPushId: - case Http3FrameType.DuplicatePush: case Http3FrameType.ReservedHttp2Priority: // These frames are explicitly reserved and must never be sent. case Http3FrameType.ReservedHttp2Ping: case Http3FrameType.ReservedHttp2WindowUpdate: diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs index 2b2fb56ae9692..2e6bea241c825 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs @@ -753,7 +753,6 @@ private void BufferBytes(ReadOnlySpan span) case Http3FrameType.ReservedHttp2WindowUpdate: case Http3FrameType.ReservedHttp2Continuation: throw new Http3ConnectionException(Http3ErrorCode.UnexpectedFrame); - case Http3FrameType.DuplicatePush: case Http3FrameType.PushPromise: case Http3FrameType.CancelPush: // Because we haven't sent any MAX_PUSH_ID frames, any of these push-related diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs index 9d5aa5dac2d97..1098317cc626b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs @@ -17,7 +17,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : HttpConnectionBase, IDisposable + internal sealed partial class HttpConnection : HttpConnectionBase, IDisposable { /// Default size of the read buffer used for the connection. private const int InitialReadBufferSize = @@ -102,7 +102,7 @@ public HttpConnection( public void Dispose() => Dispose(disposing: true); - protected void Dispose(bool disposing) + private void Dispose(bool disposing) { // Ensure we're only disposed once. Dispose could be called concurrently, for example, // if the request and the response were running concurrently and both incurred an exception. @@ -135,54 +135,84 @@ protected void Dispose(bool disposing) } } - /// Do a non-blocking poll to see whether the connection has data available or has been closed. - /// If we don't have direct access to the underlying socket, we instead use a read-ahead task. - public bool PollRead() + /// Prepare an idle connection to be used for a new request. + /// Indicates whether the coming request will be sync or async. + /// True if connection can be used, false if it is invalid due to receiving EOF or unexpected data. + public bool PrepareForReuse(bool async) { - if (_socket != null) // may be null if we don't have direct access to the socket + // We may already have a read-ahead task if we did a previous scavenge and haven't used the connection since. + // If the read-ahead task is completed, then we've received either EOF or erroneous data the connection, so it's not usable. + if (_readAheadTask is not null) { + return !_readAheadTask.Value.IsCompleted; + } + + // Check to see if we've received anything on the connection; if we have, that's + // either erroneous data (we shouldn't have received anything yet) or the connection + // has been closed; either way, we can't use it. + if (!async && _socket is not null) + { + // Directly poll the socket rather than doing an async read, so that we can + // issue an appropriate sync read when we actually need it. try { - return _socket.Poll(0, SelectMode.SelectRead); + return !_socket.Poll(0, SelectMode.SelectRead); } catch (Exception e) when (e is SocketException || e is ObjectDisposedException) { // Poll can throw when used on a closed socket. - return true; + return false; } } else { - return EnsureReadAheadAndPollRead(); + // Perform an async read on the stream, since we're going to need to read from it + // anyway, and in doing so we can avoid the extra syscall. + try + { +#pragma warning disable CA2012 // we're very careful to ensure the ValueTask is only consumed once, even though it's stored into a field + _readAheadTask = _stream.ReadAsync(new Memory(_readBuffer)); +#pragma warning restore CA2012 + return !_readAheadTask.Value.IsCompleted; + } + catch (Exception error) + { + // If reading throws, eat the error and don't reuse the connection. + if (NetEventSource.Log.IsEnabled()) Trace($"Error performing read ahead: {error}"); + return false; + } } } - /// - /// Issues a read-ahead on the connection, which will serve both as the first read on the - /// response as well as a polling indication of whether the connection is usable. - /// - /// true if there's data available on the connection or it's been closed; otherwise, false. - public bool EnsureReadAheadAndPollRead() + /// Check whether a currently idle connection is still usable, or should be scavenged. + /// True if connection can be used, false if it is invalid due to receiving EOF or unexpected data. + public bool CheckUsabilityOnScavenge() { - try + // We may already have a read-ahead task if we did a previous scavenge and haven't used the connection since. + if (_readAheadTask is null) { - Debug.Assert(_readAheadTask == null || _socket == null, "Should only already have a read-ahead task if we don't have a socket to poll"); - if (_readAheadTask == null) - { #pragma warning disable CA2012 // we're very careful to ensure the ValueTask is only consumed once, even though it's stored into a field - _readAheadTask = _stream.ReadAsync(new Memory(_readBuffer)); + _readAheadTask = ReadAheadWithZeroByteReadAsync(); #pragma warning restore CA2012 - } } - catch (Exception error) + + // If the read-ahead task is completed, then we've received either EOF or erroneous data the connection, so it's not usable. + return !_readAheadTask.Value.IsCompleted; + + async ValueTask ReadAheadWithZeroByteReadAsync() { - // If reading throws, eat the error and don't pool the connection. - if (NetEventSource.Log.IsEnabled()) Trace($"Error performing read ahead: {error}"); - Dispose(); - _readAheadTask = new ValueTask(0); - } + Debug.Assert(_readAheadTask is null); + Debug.Assert(RemainingBuffer.Length == 0); + + // Issue a zero-byte read. + // If the underlying stream supports it, this will not complete until the stream has data available, + // which will avoid pinning the connection's read buffer (and possibly allow us to release it to the buffer pool in the future, if desired). + // If not, it will complete immediately. + await _stream.ReadAsync(Memory.Empty).ConfigureAwait(false); - return _readAheadTask.Value.IsCompleted; // equivalent to polling + // We don't know for sure that the stream actually has data available, so we need to issue a real read now. + return await _stream.ReadAsync(new Memory(_readBuffer)).ConfigureAwait(false); + } } private ValueTask? ConsumeReadAheadTask() diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs index 36a57147822a8..d0e82bc6d46b6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionBase.cs @@ -60,17 +60,13 @@ protected void TraceConnection(Stream stream) } } - private long CreationTickCount { get; } = Environment.TickCount64; + public long CreationTickCount { get; } = Environment.TickCount64; // Check if lifetime expired on connection. public bool LifetimeExpired(long nowTicks, TimeSpan lifetime) { - bool expired = - lifetime != Timeout.InfiniteTimeSpan && + return lifetime != Timeout.InfiniteTimeSpan && (lifetime == TimeSpan.Zero || (nowTicks - CreationTickCount) > lifetime.TotalMilliseconds); - - if (expired && NetEventSource.Log.IsEnabled()) Trace($"Connection no longer usable. Alive {TimeSpan.FromMilliseconds((nowTicks - CreationTickCount))} > {lifetime}."); - return expired; } internal static bool IsDigit(byte c) => (uint)(c - '0') <= '9' - '0'; diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs index 4b644f6784eed..aa4f860cc48b9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs @@ -334,7 +334,7 @@ public byte[] Http2AltSvcOriginUri /// Object used to synchronize access to state in the pool. private object SyncObj => _idleConnections; - private ValueTask<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)> + private ValueTask<(HttpConnectionBase connection, bool isNewConnection)> GetConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { // Do not even attempt at getting/creating a connection if it's already obvious we cannot provided the one requested. @@ -342,12 +342,12 @@ public byte[] Http2AltSvcOriginUri { if (request.Version.Major == 3 && !_http3Enabled) { - return ValueTask.FromException<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)>( + return ValueTask.FromException<(HttpConnectionBase connection, bool isNewConnection)>( new HttpRequestException(SR.Format(SR.net_http_requested_version_not_enabled, request.Version, request.VersionPolicy, 3))); } if (request.Version.Major == 2 && !_http2Enabled) { - return ValueTask.FromException<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)>( + return ValueTask.FromException<(HttpConnectionBase connection, bool isNewConnection)>( new HttpRequestException(SR.Format(SR.net_http_requested_version_not_enabled, request.Version, request.VersionPolicy, 2))); } } @@ -365,7 +365,7 @@ public byte[] Http2AltSvcOriginUri { if (IsAltSvcBlocked(authority)) { - return ValueTask.FromException<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)>( + return ValueTask.FromException<(HttpConnectionBase connection, bool isNewConnection)>( new HttpRequestException(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, 3))); } @@ -376,7 +376,7 @@ public byte[] Http2AltSvcOriginUri // If we got here, we cannot provide HTTP/3 connection. Do not continue if downgrade is not allowed. if (request.Version.Major >= 3 && request.VersionPolicy != HttpVersionPolicy.RequestVersionOrLower) { - return ValueTask.FromException<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)>( + return ValueTask.FromException<(HttpConnectionBase connection, bool isNewConnection)>( new HttpRequestException(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, 3))); } @@ -389,32 +389,11 @@ public byte[] Http2AltSvcOriginUri // If we got here, we cannot provide HTTP/2 connection. Do not continue if downgrade is not allowed. if (request.Version.Major >= 2 && request.VersionPolicy != HttpVersionPolicy.RequestVersionOrLower) { - return ValueTask.FromException<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)>( + return ValueTask.FromException<(HttpConnectionBase connection, bool isNewConnection)>( new HttpRequestException(SR.Format(SR.net_http_requested_version_cannot_establish, request.Version, request.VersionPolicy, 2))); } - return GetHttpConnectionAsync(request, async, cancellationToken); - } - - private static bool IsUsableHttp11Connection(HttpConnection connection, long nowTicks, TimeSpan lifetime, bool async) - { - if (connection.LifetimeExpired(nowTicks, lifetime)) - { - return false; - } - - // Check to see if we've received anything on the connection; if we have, that's - // either erroneous data (we shouldn't have received anything yet) or the connection - // has been closed; either way, we can't use it. If this is an async request, we - // perform an async read on the stream, since we're going to need to read from it - // anyway, and in doing so we can avoid the extra syscall. For sync requests, we - // try to directly poll the socket rather than doing an async read, so that we can - // issue an appropriate sync read when we actually need it. We don't have the - // underlying socket in all cases, though, so PollRead may fall back to an async - // read in some cases. - return async ? - !connection.EnsureReadAheadAndPollRead() : - !connection.PollRead(); + return GetHttp11ConnectionAsync(request, async, cancellationToken); } private ValueTask GetOrReserveHttp11ConnectionAsync(bool async, CancellationToken cancellationToken) @@ -474,16 +453,22 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now } } - if (IsUsableHttp11Connection(connection, nowTicks, _poolManager.Settings._pooledConnectionLifetime, async)) + if (connection.LifetimeExpired(nowTicks, _poolManager.Settings._pooledConnectionLifetime)) { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Found usable connection in pool."); - return new ValueTask(connection); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found expired connection in pool."); + connection.Dispose(); + continue; } - else + + if (!connection.PrepareForReuse(async)) { if (NetEventSource.Log.IsEnabled()) connection.Trace("Found invalid connection in pool."); connection.Dispose(); + continue; } + + if (NetEventSource.Log.IsEnabled()) connection.Trace("Found usable connection in pool."); + return new ValueTask(connection); } // We are at the connection limit. Wait for an available connection or connection count. @@ -509,27 +494,21 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now } } - private async ValueTask<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)> - GetHttpConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)> + GetHttp11ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { HttpConnection? connection = await GetOrReserveHttp11ConnectionAsync(async, cancellationToken).ConfigureAwait(false); if (connection != null) { - return (connection, false, null); + return (connection, false); } if (NetEventSource.Log.IsEnabled()) Trace("Creating new connection for pool."); try { - HttpResponseMessage? failureResponse; - (connection, failureResponse) = await CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); - if (connection == null) - { - Debug.Assert(failureResponse != null); - DecrementConnectionCount(); - } - return (connection, true, failureResponse); + connection = await CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); + return (connection, true); } catch { @@ -538,7 +517,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now } } - private async ValueTask<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)> + private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)> GetHttp2ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { Debug.Assert(_kind == HttpConnectionKind.Https || _kind == HttpConnectionKind.SslProxyTunnel || _kind == HttpConnectionKind.Http); @@ -551,7 +530,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now // Connection exists and it is still good to use. if (NetEventSource.Log.IsEnabled()) Trace("Using existing HTTP2 connection."); _usedSinceLastCleanup = true; - return (http2Connection, false, null); + return (http2Connection, false); } // Ensure that the connection creation semaphore is created @@ -579,7 +558,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now http2Connection = GetExistingHttp2Connection(); if (http2Connection != null) { - return (http2Connection, false, null); + return (http2Connection, false); } // Recheck if HTTP2 has been disabled by a previous attempt. @@ -590,16 +569,9 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now Trace("Attempting new HTTP2 connection."); } - HttpResponseMessage? failureResponse; - - (socket, stream, transportContext, failureResponse) = + (socket, stream, transportContext) = await ConnectAsync(request, async, cancellationToken).ConfigureAwait(false); - if (failureResponse != null) - { - return (null, true, failureResponse); - } - Debug.Assert(stream != null); sslStream = stream as SslStream; @@ -613,7 +585,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now Trace("New unencrypted HTTP2 connection established."); } - return (http2Connection, true, null); + return (http2Connection, true); } Debug.Assert(sslStream != null); @@ -635,7 +607,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now Trace("New HTTP2 connection established."); } - return (http2Connection, true, null); + return (http2Connection, true); } } } @@ -682,7 +654,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now if (canUse) { - return (await ConstructHttp11ConnectionAsync(async, socket, stream!, transportContext, request, cancellationToken).ConfigureAwait(false), true, null); + return (await ConstructHttp11ConnectionAsync(async, socket, stream!, transportContext, request, cancellationToken).ConfigureAwait(false), true); } else { @@ -696,7 +668,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now } // If we reach this point, it means we need to fall back to a (new or existing) HTTP/1.1 connection. - return await GetHttpConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); + return await GetHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); } private Http2Connection? GetExistingHttp2Connection() @@ -716,6 +688,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now if (http2Connection.LifetimeExpired(Environment.TickCount64, pooledConnectionLifetime)) { // Connection expired. + if (NetEventSource.Log.IsEnabled()) http2Connection.Trace("Found expired HTTP2 connection."); http2Connection.Dispose(); InvalidateHttp2Connection(http2Connection); } @@ -746,7 +719,7 @@ private void AddHttp2Connection(Http2Connection newConnection) } } - private async ValueTask<(HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse)> + private async ValueTask<(HttpConnectionBase connection, bool isNewConnection)> GetHttp3ConnectionAsync(HttpRequestMessage request, HttpAuthority authority, CancellationToken cancellationToken) { Debug.Assert(_kind == HttpConnectionKind.Https); @@ -760,6 +733,7 @@ private void AddHttp2Connection(Http2Connection newConnection) if (http3Connection.LifetimeExpired(Environment.TickCount64, pooledConnectionLifetime) || http3Connection.Authority != authority) { // Connection expired. + if (NetEventSource.Log.IsEnabled()) http3Connection.Trace("Found expired HTTP3 connection."); http3Connection.Dispose(); InvalidateHttp3Connection(http3Connection); } @@ -768,7 +742,7 @@ private void AddHttp2Connection(Http2Connection newConnection) // Connection exists and it is still good to use. if (NetEventSource.Log.IsEnabled()) Trace("Using existing HTTP3 connection."); _usedSinceLastCleanup = true; - return (http3Connection, false, null); + return (http3Connection, false); } } @@ -796,7 +770,7 @@ private void AddHttp2Connection(Http2Connection newConnection) Trace("Using existing HTTP3 connection."); } - return (_http3Connection, false, null); + return (_http3Connection, false); } if (NetEventSource.Log.IsEnabled()) @@ -833,7 +807,7 @@ private void AddHttp2Connection(Http2Connection newConnection) Trace("New HTTP3 connection established."); } - return (http3Connection, true, null); + return (http3Connection, true); } finally { @@ -847,14 +821,7 @@ public async ValueTask SendWithRetryAsync(HttpRequestMessag { // Loop on connection failures and retry if possible. - (HttpConnectionBase? connection, bool isNewConnection, HttpResponseMessage? failureResponse) = await GetConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); - if (failureResponse != null) - { - // Proxy tunnel failure; return proxy response - Debug.Assert(isNewConnection); - Debug.Assert(connection == null); - return failureResponse; - } + (HttpConnectionBase connection, bool isNewConnection) = await GetConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); HttpResponseMessage response; @@ -1214,7 +1181,7 @@ public ValueTask SendAsync(HttpRequestMessage request, bool return SendWithProxyAuthAsync(request, async, doRequestAuth, cancellationToken); } - private async ValueTask<(Socket?, Stream?, TransportContext?, HttpResponseMessage?)> ConnectAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + private async ValueTask<(Socket?, Stream, TransportContext?)> ConnectAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { // If a non-infinite connect timeout has been set, create and use a new CancellationToken that will be canceled // when either the original token is canceled or a connect timeout occurs. @@ -1245,14 +1212,7 @@ public ValueTask SendAsync(HttpRequestMessage request, bool case HttpConnectionKind.ProxyTunnel: case HttpConnectionKind.SslProxyTunnel: - HttpResponseMessage? response; - (stream, response) = await EstablishProxyTunnelAsync(async, request.HasHeaders ? request.Headers : null, cancellationToken).ConfigureAwait(false); - if (response != null) - { - // Return non-success response from proxy. - response.RequestMessage = request; - return (null, null, null, response); - } + stream = await EstablishProxyTunnelAsync(async, request.HasHeaders ? request.Headers : null, cancellationToken).ConfigureAwait(false); break; } @@ -1272,7 +1232,7 @@ public ValueTask SendAsync(HttpRequestMessage request, bool stream = sslStream; } - return (socket, stream, transportContext, null); + return (socket, stream, transportContext); } finally { @@ -1336,17 +1296,12 @@ public ValueTask SendAsync(HttpRequestMessage request, bool } } - internal async ValueTask<(HttpConnection?, HttpResponseMessage?)> CreateHttp11ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) + internal async ValueTask CreateHttp11ConnectionAsync(HttpRequestMessage request, bool async, CancellationToken cancellationToken) { - (Socket? socket, Stream? stream, TransportContext? transportContext, HttpResponseMessage? failureResponse) = + (Socket? socket, Stream? stream, TransportContext? transportContext) = await ConnectAsync(request, async, cancellationToken).ConfigureAwait(false); - if (failureResponse != null) - { - return (null, failureResponse); - } - - return (await ConstructHttp11ConnectionAsync(async, socket, stream!, transportContext, request, cancellationToken).ConfigureAwait(false), null); + return await ConstructHttp11ConnectionAsync(async, socket, stream!, transportContext, request, cancellationToken).ConfigureAwait(false); } private SslClientAuthenticationOptions GetSslOptionsForRequest(HttpRequestMessage request) @@ -1427,11 +1382,10 @@ private async ValueTask ConstructHttp2ConnectionAsync(Stream st return http2Connection; } - - // Returns the established stream or an HttpResponseMessage from the proxy indicating failure. - private async ValueTask<(Stream?, HttpResponseMessage?)> EstablishProxyTunnelAsync(bool async, HttpRequestHeaders? headers, CancellationToken cancellationToken) + private async ValueTask EstablishProxyTunnelAsync(bool async, HttpRequestHeaders? headers, CancellationToken cancellationToken) { Debug.Assert(_originAuthority != null); + // Send a CONNECT request to the proxy server to establish a tunnel. HttpRequestMessage tunnelRequest = new HttpRequestMessage(HttpMethod.Connect, _proxyUri); tunnelRequest.Headers.Host = $"{_originAuthority.IdnHost}:{_originAuthority.Port}"; // This specifies destination host/port to connect to @@ -1445,12 +1399,19 @@ private async ValueTask ConstructHttp2ConnectionAsync(Stream st if (tunnelResponse.StatusCode != HttpStatusCode.OK) { - return (null, tunnelResponse); + tunnelResponse.Dispose(); + throw new HttpRequestException(SR.Format(SR.net_http_proxy_tunnel_returned_failure_status_code, _proxyUri, (int)tunnelResponse.StatusCode)); } - Stream stream = tunnelResponse.Content.ReadAsStream(cancellationToken); - - return (stream, null); + try + { + return tunnelResponse.Content.ReadAsStream(cancellationToken); + } + catch + { + tunnelResponse.Dispose(); + throw; + } } /// Enqueues a waiter to the waiters list. @@ -1470,24 +1431,6 @@ private async ValueTask ConstructHttp2ConnectionAsync(Stream st return waiter; } - private bool HasWaiter() - { - Debug.Assert(Monitor.IsEntered(SyncObj)); - - return (_waiters != null && _waiters.Count > 0); - } - - /// Dequeues a waiter from the waiters list. The list must not be empty. - /// The dequeued waiter. - private TaskCompletionSourceWithCancellation DequeueWaiter() - { - Debug.Assert(Monitor.IsEntered(SyncObj)); - Debug.Assert(Settings._maxConnectionsPerServer != int.MaxValue); - Debug.Assert(_idleConnections.Count == 0, $"With {_idleConnections.Count} idle connections, we shouldn't have a waiter."); - - return _waiters!.Dequeue(); - } - private void IncrementConnectionCountNoLock() { Debug.Assert(Monitor.IsEntered(SyncObj), $"Expected to be holding {nameof(SyncObj)}"); @@ -1513,9 +1456,16 @@ private bool TransferConnection(HttpConnection? connection) { Debug.Assert(Monitor.IsEntered(SyncObj)); - while (HasWaiter()) + if (_waiters == null) { - TaskCompletionSource waiter = DequeueWaiter(); + return false; + } + + Debug.Assert(_maxConnections != int.MaxValue, "_waiters queue is allocated but no connection limit is set??"); + + while (_waiters.TryDequeue(out TaskCompletionSourceWithCancellation? waiter)) + { + Debug.Assert(_idleConnections.Count == 0, $"With {_idleConnections.Count} idle connections, we shouldn't have a waiter."); // Try to complete the task. If it's been cancelled already, this will fail. if (waiter.TrySetResult(connection)) @@ -1562,61 +1512,51 @@ public void DecrementConnectionCount() /// The connection to return. public void ReturnConnection(HttpConnection connection) { - bool lifetimeExpired = connection.LifetimeExpired(Environment.TickCount64, _poolManager.Settings._pooledConnectionLifetime); + if (connection.LifetimeExpired(Environment.TickCount64, _poolManager.Settings._pooledConnectionLifetime)) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing connection return to pool. Connection lifetime expired."); + connection.Dispose(); + return; + } - if (!lifetimeExpired) + List list = _idleConnections; + lock (SyncObj) { - List list = _idleConnections; - lock (SyncObj) - { - Debug.Assert(list.Count <= _maxConnections, $"Expected {list.Count} <= {_maxConnections}"); + Debug.Assert(list.Count <= _maxConnections, $"Expected {list.Count} <= {_maxConnections}"); - // Mark the pool as still being active. - _usedSinceLastCleanup = true; + // Mark the pool as still being active. + _usedSinceLastCleanup = true; - // If there's someone waiting for a connection and this one's still valid, simply transfer this one to them rather than pooling it. - // Note that while we checked connection lifetime above, we don't check idle timeout, as even if idle timeout - // is zero, we consider a connection that's just handed from one use to another to never actually be idle. - bool receivedUnexpectedData = false; - if (HasWaiter()) - { - receivedUnexpectedData = connection.EnsureReadAheadAndPollRead(); - if (!receivedUnexpectedData && TransferConnection(connection)) - { - if (NetEventSource.Log.IsEnabled()) connection.Trace("Transferred connection to waiter."); - return; - } - } + // If there's someone waiting for a connection and this one's still valid, simply transfer this one to them rather than pooling it. + // Note that while we checked connection lifetime above, we don't check idle timeout, as even if idle timeout + // is zero, we consider a connection that's just handed from one use to another to never actually be idle. + if (TransferConnection(connection)) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace("Transferred connection to waiter."); + return; + } - // If the connection is still valid, add it to the list. + if (_poolManager.Settings._pooledConnectionIdleTimeout == TimeSpan.Zero) + { + if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing connection returned to pool. Zero idle timeout."); + } + else if (_disposed) + { // If the pool has been disposed of, dispose the connection being returned, // as the pool is being deactivated. We do this after the above in order to // use pooled connections to satisfy any requests that pended before the - // the pool was disposed of. We also dispose of connections if connection - // timeouts are such that the connection would immediately expire, anyway, as - // well as for connections that have unexpectedly received extraneous data / EOF. - if (!receivedUnexpectedData && - !_disposed && - _poolManager.Settings._pooledConnectionIdleTimeout != TimeSpan.Zero) - { - // Pool the connection by adding it to the list. - list.Add(new CachedConnection(connection)); - if (NetEventSource.Log.IsEnabled()) connection.Trace("Stored connection in pool."); - return; - } + // the pool was disposed of. + if (NetEventSource.Log.IsEnabled()) connection.Trace("Disposing connection returned to pool. Pool was disposed."); + } + else + { + // Pool the connection by adding it to the list. + list.Add(new CachedConnection(connection)); + if (NetEventSource.Log.IsEnabled()) connection.Trace("Stored connection in pool."); + return; } } - // The connection could be not be reused. Dispose of it. - // Disposing it will alert any waiters that a connection slot has become available. - if (NetEventSource.Log.IsEnabled()) - { - connection.Trace( - lifetimeExpired ? "Disposing connection return to pool. Connection lifetime expired." : - _poolManager.Settings._pooledConnectionIdleTimeout == TimeSpan.Zero ? "Disposing connection returned to pool. Zero idle timeout." : - _disposed ? "Disposing connection returned to pool. Pool was disposed." : - "Disposing connection returned to pool. Read-ahead unexpectedly completed."); - } connection.Dispose(); } @@ -1941,11 +1881,24 @@ public bool IsUsable( if ((pooledConnectionIdleTimeout != Timeout.InfiniteTimeSpan) && ((nowTicks - _returnedTickCount) > pooledConnectionIdleTimeout.TotalMilliseconds)) { - if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Connection no longer usable. Idle {TimeSpan.FromMilliseconds((nowTicks - _returnedTickCount))} > {pooledConnectionIdleTimeout}."); + if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Scavenging connection. Idle {TimeSpan.FromMilliseconds((nowTicks - _returnedTickCount))} > {pooledConnectionIdleTimeout}."); + return false; + } + + // Validate that the connection lifetime has not been exceeded. + if (_connection.LifetimeExpired(nowTicks, pooledConnectionLifetime)) + { + if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Scavenging connection. Lifetime {TimeSpan.FromMilliseconds((nowTicks - _connection.CreationTickCount))} > {pooledConnectionLifetime}."); + return false; + } + + if (!_connection.CheckUsabilityOnScavenge()) + { + if (NetEventSource.Log.IsEnabled()) _connection.Trace($"Scavenging connection. Unexpected data or EOF received."); return false; } - return IsUsableHttp11Connection(_connection, nowTicks, pooledConnectionLifetime, false); + return true; } public bool Equals(CachedConnection other) => ReferenceEquals(other._connection, _connection); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs index c4e8252e6a0c2..4daad8f2b0ec7 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentReadStream.cs @@ -7,7 +7,7 @@ namespace System.Net.Http { - internal partial class HttpConnection + internal sealed partial class HttpConnection { internal abstract class HttpContentReadStream : HttpContentStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs index 97aac11d4f164..5777ac8e40f74 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpContentWriteStream.cs @@ -8,7 +8,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : IDisposable + internal sealed partial class HttpConnection : IDisposable { private abstract class HttpContentWriteStream : HttpContentStream { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RawConnectionStream.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RawConnectionStream.cs index 4459b2ec7fb8f..44376b01a95b2 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RawConnectionStream.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RawConnectionStream.cs @@ -8,7 +8,7 @@ namespace System.Net.Http { - internal partial class HttpConnection : IDisposable + internal sealed partial class HttpConnection : IDisposable { private sealed class RawConnectionStream : HttpContentStream { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs index c3b2610bde766..eb15585b4c93f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/AssemblyInfo.cs @@ -5,5 +5,5 @@ using Xunit; [assembly: SkipOnCoreClr("System.Net.Tests are flaky and/or long running: https://github.com/dotnet/runtime/issues/131", RuntimeConfiguration.Checked)] -[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/131", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime), nameof(PlatformDetection.IsNotBrowser))] // System.Net.Tests are flaky and/or long running +[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/131", ~(TestPlatforms.Android | TestPlatforms.Browser), TargetFrameworkMonikers.Any, TestRuntimes.Mono)] // System.Net.Tests are flaky and/or long running diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs index 969415dde4252..5589de00d6289 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Cancellation.cs @@ -37,7 +37,7 @@ await Assert.ThrowsAnyAsync(() => new UriBuilder(uri) { Scheme = "https" }.ToString()) { Version = UseVersion }, default)); sw.Stop(); - Assert.InRange(sw.ElapsedMilliseconds, 500, 60_000); + Assert.InRange(sw.ElapsedMilliseconds, 500, 85_000); releaseServer.SetResult(); } }, server => releaseServer.Task); // doesn't establish SSL connection diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs index ddd39b89d840e..2ffffbdc966c0 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpContentTest.cs @@ -471,7 +471,7 @@ public async Task LoadIntoBufferAsync_ThrowIOExceptionInOverriddenAsyncMethod_Th public async Task Dispose_GetReadStreamThenDispose_ReadStreamGetsDisposed(bool readStreamAsync) { var content = new MockContent(); - MockMemoryStream s = (MockMemoryStream)await content.ReadAsStreamAsync(readStreamAsync);; + MockMemoryStream s = (MockMemoryStream)await content.ReadAsStreamAsync(readStreamAsync); Assert.Equal(1, content.CreateContentReadStreamCount); Assert.Equal(0, s.DisposeCount); diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs index 2929a612e79d7..167d5a0504c96 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs @@ -204,18 +204,23 @@ public void ToString_DefaultAndNonDefaultInstance_DumpAllFields() rm.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain", 0.2)); rm.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml", 0.1)); + rm.Headers.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.5"); // validate this remains unparsed rm.Headers.Add("Custom-Request-Header", "value1"); rm.Content.Headers.Add("Custom-Content-Header", "value2"); - Assert.Equal( - "Method: PUT, RequestUri: 'http://a.com/', Version: 1.0, Content: " + typeof(StringContent).ToString() + ", Headers:" + Environment.NewLine + - "{" + Environment.NewLine + - " Accept: text/plain; q=0.2" + Environment.NewLine + - " Accept: text/xml; q=0.1" + Environment.NewLine + - " Custom-Request-Header: value1" + Environment.NewLine + - " Content-Type: text/plain; charset=utf-8" + Environment.NewLine + - " Custom-Content-Header: value2" + Environment.NewLine + - "}", rm.ToString()); + for (int i = 0; i < 2; i++) // make sure ToString() doesn't impact subsequent use + { + Assert.Equal( + "Method: PUT, RequestUri: 'http://a.com/', Version: 1.0, Content: " + typeof(StringContent).ToString() + ", Headers:" + Environment.NewLine + + "{" + Environment.NewLine + + " Accept: text/plain; q=0.2" + Environment.NewLine + + " Accept: text/xml; q=0.1" + Environment.NewLine + + " Accept-Language: en-US,en;q=0.5" + Environment.NewLine + + " Custom-Request-Header: value1" + Environment.NewLine + + " Content-Type: text/plain; charset=utf-8" + Environment.NewLine + + " Custom-Content-Header: value2" + Environment.NewLine + + "}", rm.ToString()); + } } [Theory] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index a3359f238f9d6..7bf08ccfddf21 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -112,6 +112,27 @@ private sealed class SetOnFinalized public sealed class SocketsHttpHandler_HttpProtocolTests : HttpProtocolTests { public SocketsHttpHandler_HttpProtocolTests(ITestOutputHelper output) : base(output) { } + + [Fact] + public async Task DefaultRequestHeaders_SentUnparsed() + { + await LoopbackServer.CreateClientAndServerAsync(async uri => + { + using (HttpClient client = CreateHttpClient()) + { + client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.5"); // validation would add spaces + client.DefaultRequestHeaders.TryAddWithoutValidation("From", "invalidemail"); // would fail to parse if validated + + var m = new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }; + (await client.SendAsync(TestAsync, m)).Dispose(); + } + }, async server => + { + List headers = await server.AcceptConnectionSendResponseAndCloseAsync(); + Assert.Contains(headers, header => header.Contains("Accept-Language: en-US,en;q=0.5")); + Assert.Contains(headers, header => header.Contains("From: invalidemail")); + }); + } } [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] @@ -683,7 +704,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( [Theory] [InlineData("Age", "1")] - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy authorisation header.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy authorisation header.")] [InlineData("Authorization", "Basic YWxhZGRpbjpvcGVuc2VzYW1l")] [InlineData("Cache-Control", "no-cache")] [InlineData("Content-Encoding", "gzip")] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs index 51f0550ebf4aa..464378cf61228 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/StreamContentTest.cs @@ -43,7 +43,7 @@ public void ContentLength_SetStreamSupportingSeeking_StreamLengthMatchesHeaderVa var source = new MockStream(new byte[10], true, true); // Supports seeking. var content = new StreamContent(source); - Assert.Equal(source.Length, content.Headers.ContentLength);; + Assert.Equal(source.Length, content.Headers.ContentLength); } [Fact] diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs index 74468056113e8..6ac5e43bfc12b 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/TelemetryTest.cs @@ -64,10 +64,11 @@ public void EventSource_SuccessfulRequest_LogsStartStop(string testMethod) Version version = Version.Parse(useVersionString); using var listener = new TestEventListener("System.Net.Http", EventLevel.Verbose, eventCounterInterval: 0.1d); + listener.AddActivityTracking(); bool buffersResponse = false; - var events = new ConcurrentQueue(); - await listener.RunWithCallbackAsync(events.Enqueue, async () => + var events = new ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)>(); + await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), async () => { await GetFactoryForVersion(version).CreateClientAndServerAsync( async uri => @@ -163,23 +164,17 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - await Task.Delay(300); + await WaitForEventCountersAsync(events); await connection.ReadRequestDataAsync(); await connection.SendResponseAsync(content: new string('a', ResponseContentLength)); }); }); - await Task.Delay(300); + await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself + Assert.DoesNotContain(events, e => e.Event.EventId == 0); // errors from the EventSource itself - EventWrittenEventArgs start = Assert.Single(events, e => e.EventName == "RequestStart"); - ValidateStartEventPayload(start); - - EventWrittenEventArgs stop = Assert.Single(events, e => e.EventName == "RequestStop"); - Assert.Empty(stop.Payload); - - Assert.DoesNotContain(events, e => e.EventName == "RequestFailed"); + ValidateStartFailedStopEvents(events); ValidateConnectionEstablishedClosed(events, version); @@ -189,7 +184,7 @@ await server.AcceptConnectionAsync(async connection => responseContentLength: buffersResponse ? ResponseContentLength : null, count: 1); - VerifyEventCounters(events, requestCount: 1, shouldHaveFailures: false); + ValidateEventCounters(events, requestCount: 1, shouldHaveFailures: false); }, UseVersion.ToString(), testMethod).Dispose(); } @@ -208,9 +203,10 @@ public void EventSource_UnsuccessfulRequest_LogsStartFailedStop(string testMetho { Version version = Version.Parse(useVersionString); using var listener = new TestEventListener("System.Net.Http", EventLevel.Verbose, eventCounterInterval: 0.1d); + listener.AddActivityTracking(); - var events = new ConcurrentQueue(); - await listener.RunWithCallbackAsync(events.Enqueue, async () => + var events = new ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)>(); + await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), async () => { var semaphore = new SemaphoreSlim(0, 1); var cts = new CancellationTokenSource(); @@ -276,28 +272,22 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - cts.CancelAfter(TimeSpan.FromMilliseconds(300)); - Assert.True(await semaphore.WaitAsync(TimeSpan.FromSeconds(5))); + await WaitForEventCountersAsync(events); + cts.Cancel(); + Assert.True(await semaphore.WaitAsync(TimeSpan.FromSeconds(30))); connection.Dispose(); }); }); - await Task.Delay(300); + await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself - - EventWrittenEventArgs start = Assert.Single(events, e => e.EventName == "RequestStart"); - ValidateStartEventPayload(start); + Assert.DoesNotContain(events, e => e.Event.EventId == 0); // errors from the EventSource itself - EventWrittenEventArgs failure = Assert.Single(events, e => e.EventName == "RequestFailed"); - Assert.Empty(failure.Payload); - - EventWrittenEventArgs stop = Assert.Single(events, e => e.EventName == "RequestStop"); - Assert.Empty(stop.Payload); + ValidateStartFailedStopEvents(events, shouldHaveFailures: true); ValidateConnectionEstablishedClosed(events, version); - VerifyEventCounters(events, requestCount: 1, shouldHaveFailures: true); + ValidateEventCounters(events, requestCount: 1, shouldHaveFailures: true); }, UseVersion.ToString(), testMethod).Dispose(); } @@ -324,9 +314,10 @@ public void EventSource_SendingRequestContent_LogsRequestContentStartStop(string Version version = Version.Parse(useVersionString); using var listener = new TestEventListener("System.Net.Http", EventLevel.Verbose, eventCounterInterval: 0.1d); + listener.AddActivityTracking(); - var events = new ConcurrentQueue(); - await listener.RunWithCallbackAsync(events.Enqueue, async () => + var events = new ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)>(); + await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), async () => { await GetFactoryForVersion(version).CreateClientAndServerAsync( async uri => @@ -377,23 +368,17 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync( { await server.AcceptConnectionAsync(async connection => { - await Task.Delay(300); + await WaitForEventCountersAsync(events); await connection.ReadRequestDataAsync(); await connection.SendResponseAsync(content: new string('a', ResponseContentLength)); }); }); - await Task.Delay(300); + await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself - - EventWrittenEventArgs start = Assert.Single(events, e => e.EventName == "RequestStart"); - ValidateStartEventPayload(start); - - EventWrittenEventArgs stop = Assert.Single(events, e => e.EventName == "RequestStop"); - Assert.Empty(stop.Payload); + Assert.DoesNotContain(events, e => e.Event.EventId == 0); // errors from the EventSource itself - Assert.DoesNotContain(events, e => e.EventName == "RequestFailed"); + ValidateStartFailedStopEvents(events); ValidateConnectionEstablishedClosed(events, version); @@ -403,27 +388,46 @@ await server.AcceptConnectionAsync(async connection => responseContentLength: testMethod.StartsWith("InvokerSend") ? null : ResponseContentLength, count: 1); - VerifyEventCounters(events, requestCount: 1, shouldHaveFailures: false); + ValidateEventCounters(events, requestCount: 1, shouldHaveFailures: false); }, UseVersion.ToString(), testMethod).Dispose(); } - private static void ValidateStartEventPayload(EventWrittenEventArgs startEvent) + private static void ValidateStartFailedStopEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, bool shouldHaveFailures = false, int count = 1) { - Assert.Equal("RequestStart", startEvent.EventName); - Assert.Equal(7, startEvent.Payload.Count); - - Assert.StartsWith("http", (string)startEvent.Payload[0]); - Assert.NotEmpty((string)startEvent.Payload[1]); // host - Assert.True(startEvent.Payload[2] is int port && port >= 0 && port <= 65535); - Assert.NotEmpty((string)startEvent.Payload[3]); // pathAndQuery - Assert.True(startEvent.Payload[4] is byte versionMajor && (versionMajor == 1 || versionMajor == 2)); - Assert.True(startEvent.Payload[5] is byte versionMinor && (versionMinor == 1 || versionMinor == 0)); - Assert.InRange((HttpVersionPolicy)startEvent.Payload[6], HttpVersionPolicy.RequestVersionOrLower, HttpVersionPolicy.RequestVersionExact); + (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == "RequestStart").ToArray(); + foreach (EventWrittenEventArgs startEvent in starts.Select(e => e.Event)) + { + Assert.Equal(7, startEvent.Payload.Count); + Assert.StartsWith("http", (string)startEvent.Payload[0]); + Assert.NotEmpty((string)startEvent.Payload[1]); // host + Assert.True(startEvent.Payload[2] is int port && port >= 0 && port <= 65535); + Assert.NotEmpty((string)startEvent.Payload[3]); // pathAndQuery + Assert.True(startEvent.Payload[4] is byte versionMajor && (versionMajor == 1 || versionMajor == 2)); + Assert.True(startEvent.Payload[5] is byte versionMinor && (versionMinor == 1 || versionMinor == 0)); + Assert.InRange((HttpVersionPolicy)startEvent.Payload[6], HttpVersionPolicy.RequestVersionOrLower, HttpVersionPolicy.RequestVersionExact); + } + Assert.Equal(count, starts.Length); + + (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == "RequestStop").ToArray(); + Assert.All(stops, stopEvent => Assert.Empty(stopEvent.Event.Payload)); + + ValidateSameActivityIds(starts, stops); + + (EventWrittenEventArgs Event, Guid ActivityId)[] failures = events.Where(e => e.Event.EventName == "RequestFailed").ToArray(); + Assert.All(failures, failedEvent => Assert.Empty(failedEvent.Event.Payload)); + if (shouldHaveFailures) + { + ValidateSameActivityIds(starts, failures); + } + else + { + Assert.Empty(failures); + } } - private static void ValidateConnectionEstablishedClosed(ConcurrentQueue events, Version version, int count = 1) + private static void ValidateConnectionEstablishedClosed(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, Version version, int count = 1) { - EventWrittenEventArgs[] connectionsEstablished = events.Where(e => e.EventName == "ConnectionEstablished").ToArray(); + EventWrittenEventArgs[] connectionsEstablished = events.Select(e => e.Event).Where(e => e.EventName == "ConnectionEstablished").ToArray(); Assert.Equal(count, connectionsEstablished.Length); foreach (EventWrittenEventArgs connectionEstablished in connectionsEstablished) { @@ -432,7 +436,7 @@ private static void ValidateConnectionEstablishedClosed(ConcurrentQueue e.EventName == "ConnectionClosed").ToArray(); + EventWrittenEventArgs[] connectionsClosed = events.Select(e => e.Event).Where(e => e.EventName == "ConnectionClosed").ToArray(); Assert.Equal(count, connectionsClosed.Length); foreach (EventWrittenEventArgs connectionClosed in connectionsClosed) { @@ -442,49 +446,69 @@ private static void ValidateConnectionEstablishedClosed(ConcurrentQueue events, int? requestContentLength, int? responseContentLength, int count) + private static void ValidateRequestResponseStartStopEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, int? requestContentLength, int? responseContentLength, int count) { - EventWrittenEventArgs[] requestHeadersStarts = events.Where(e => e.EventName == "RequestHeadersStart").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] requestHeadersStarts = events.Where(e => e.Event.EventName == "RequestHeadersStart").ToArray(); Assert.Equal(count, requestHeadersStarts.Length); - Assert.All(requestHeadersStarts, r => Assert.Empty(r.Payload)); + Assert.All(requestHeadersStarts, r => Assert.Empty(r.Event.Payload)); - EventWrittenEventArgs[] requestHeadersStops = events.Where(e => e.EventName == "RequestHeadersStop").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] requestHeadersStops = events.Where(e => e.Event.EventName == "RequestHeadersStop").ToArray(); Assert.Equal(count, requestHeadersStops.Length); - Assert.All(requestHeadersStops, r => Assert.Empty(r.Payload)); + Assert.All(requestHeadersStops, r => Assert.Empty(r.Event.Payload)); + + ValidateSameActivityIds(requestHeadersStarts, requestHeadersStops); - EventWrittenEventArgs[] requestContentStarts = events.Where(e => e.EventName == "RequestContentStart").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] requestContentStarts = events.Where(e => e.Event.EventName == "RequestContentStart").ToArray(); Assert.Equal(requestContentLength.HasValue ? count : 0, requestContentStarts.Length); - Assert.All(requestContentStarts, r => Assert.Empty(r.Payload)); + Assert.All(requestContentStarts, r => Assert.Empty(r.Event.Payload)); - EventWrittenEventArgs[] requestContentStops = events.Where(e => e.EventName == "RequestContentStop").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] requestContentStops = events.Where(e => e.Event.EventName == "RequestContentStop").ToArray(); Assert.Equal(requestContentLength.HasValue ? count : 0, requestContentStops.Length); - foreach (EventWrittenEventArgs requestContentStop in requestContentStops) + foreach (EventWrittenEventArgs requestContentStop in requestContentStops.Select(e => e.Event)) { object payload = Assert.Single(requestContentStop.Payload); Assert.True(payload is long); Assert.Equal(requestContentLength.Value, (long)payload); } - EventWrittenEventArgs[] responseHeadersStarts = events.Where(e => e.EventName == "ResponseHeadersStart").ToArray(); + ValidateSameActivityIds(requestContentStarts, requestContentStops); + + (EventWrittenEventArgs Event, Guid ActivityId)[] responseHeadersStarts = events.Where(e => e.Event.EventName == "ResponseHeadersStart").ToArray(); Assert.Equal(count, responseHeadersStarts.Length); - Assert.All(responseHeadersStarts, r => Assert.Empty(r.Payload)); + Assert.All(responseHeadersStarts, r => Assert.Empty(r.Event.Payload)); - EventWrittenEventArgs[] responseHeadersStops = events.Where(e => e.EventName == "ResponseHeadersStop").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] responseHeadersStops = events.Where(e => e.Event.EventName == "ResponseHeadersStop").ToArray(); Assert.Equal(count, responseHeadersStops.Length); - Assert.All(responseHeadersStops, r => Assert.Empty(r.Payload)); + Assert.All(responseHeadersStops, r => Assert.Empty(r.Event.Payload)); - EventWrittenEventArgs[] responseContentStarts = events.Where(e => e.EventName == "ResponseContentStart").ToArray(); + ValidateSameActivityIds(responseHeadersStarts, responseHeadersStops); + + (EventWrittenEventArgs Event, Guid ActivityId)[] responseContentStarts = events.Where(e => e.Event.EventName == "ResponseContentStart").ToArray(); Assert.Equal(responseContentLength.HasValue ? count : 0, responseContentStarts.Length); - Assert.All(responseContentStarts, r => Assert.Empty(r.Payload)); + Assert.All(responseContentStarts, r => Assert.Empty(r.Event.Payload)); - EventWrittenEventArgs[] responseContentStops = events.Where(e => e.EventName == "ResponseContentStop").ToArray(); + (EventWrittenEventArgs Event, Guid ActivityId)[] responseContentStops = events.Where(e => e.Event.EventName == "ResponseContentStop").ToArray(); Assert.Equal(responseContentLength.HasValue ? count : 0, responseContentStops.Length); - Assert.All(responseContentStops, r => Assert.Empty(r.Payload)); + Assert.All(responseContentStops, r => Assert.Empty(r.Event.Payload)); + + ValidateSameActivityIds(responseContentStarts, responseContentStops); } - private static void VerifyEventCounters(ConcurrentQueue events, int requestCount, bool shouldHaveFailures, int requestsLeftQueueVersion = -1) + private static void ValidateSameActivityIds((EventWrittenEventArgs Event, Guid ActivityId)[] a, (EventWrittenEventArgs Event, Guid ActivityId)[] b) + { + Assert.Equal(a.Length, b.Length); + + for (int i = 0; i < a.Length; i++) + { + Assert.NotEqual(Guid.Empty, a[i].ActivityId); + Assert.Equal(a[i].ActivityId, b[i].ActivityId); + } + } + + private static void ValidateEventCounters(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, int requestCount, bool shouldHaveFailures, int requestsLeftQueueVersion = -1) { Dictionary eventCounters = events + .Select(e => e.Event) .Where(e => e.EventName == "EventCounters") .Select(e => (IDictionary)e.Payload.Single()) .GroupBy(d => (string)d["Name"], d => (double)(d.ContainsKey("Mean") ? d["Mean"] : d["Increment"])) @@ -554,9 +578,10 @@ public void EventSource_ConnectionPoolAtMaxConnections_LogsRequestLeftQueue() { Version version = Version.Parse(useVersionString); using var listener = new TestEventListener("System.Net.Http", EventLevel.Verbose, eventCounterInterval: 0.1d); + listener.AddActivityTracking(); - var events = new ConcurrentQueue(); - await listener.RunWithCallbackAsync(events.Enqueue, async () => + var events = new ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)>(); + await listener.RunWithCallbackAsync(e => events.Enqueue((e, e.ActivityId)), async () => { var firstRequestReceived = new SemaphoreSlim(0, 1); var secondRequestSent = new SemaphoreSlim(0, 1); @@ -605,7 +630,7 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync( await connection.ReadRequestDataAsync(readBody: false); firstRequestReceived.Release(); Assert.True(await secondRequestSent.WaitAsync(TimeSpan.FromSeconds(10))); - await Task.Delay(100); + await WaitForEventCountersAsync(events); await connection.SendResponseAsync(); // Second request @@ -614,33 +639,51 @@ await GetFactoryForVersion(version).CreateClientAndServerAsync( }; }); - await Task.Delay(300); + await WaitForEventCountersAsync(events); }); - Assert.DoesNotContain(events, ev => ev.EventId == 0); // errors from the EventSource itself + Assert.DoesNotContain(events, e => e.Event.EventId == 0); // errors from the EventSource itself - EventWrittenEventArgs[] starts = events.Where(e => e.EventName == "RequestStart").ToArray(); - Assert.Equal(3, starts.Length); - Assert.All(starts, s => ValidateStartEventPayload(s)); - - EventWrittenEventArgs[] stops = events.Where(e => e.EventName == "RequestStop").ToArray(); - Assert.Equal(3, stops.Length); - Assert.All(stops, s => Assert.Empty(s.Payload)); - - Assert.DoesNotContain(events, e => e.EventName == "RequestFailed"); + ValidateStartFailedStopEvents(events, count: 3); ValidateConnectionEstablishedClosed(events, version); - EventWrittenEventArgs requestLeftQueue = Assert.Single(events, e => e.EventName == "RequestLeftQueue"); + (EventWrittenEventArgs requestLeftQueue, Guid requestLeftQueueId) = Assert.Single(events, e => e.Event.EventName == "RequestLeftQueue"); Assert.Equal(3, requestLeftQueue.Payload.Count); Assert.True((double)requestLeftQueue.Payload.Count > 0); // timeSpentOnQueue Assert.Equal(version.Major, (byte)requestLeftQueue.Payload[1]); Assert.Equal(version.Minor, (byte)requestLeftQueue.Payload[2]); + Assert.Equal(requestLeftQueueId, events.Where(e => e.Event.EventName == "RequestStart").Last().ActivityId); + ValidateRequestResponseStartStopEvents(events, requestContentLength: null, responseContentLength: 0, count: 3); - VerifyEventCounters(events, requestCount: 3, shouldHaveFailures: false, requestsLeftQueueVersion: version.Major); + ValidateEventCounters(events, requestCount: 3, shouldHaveFailures: false, requestsLeftQueueVersion: version.Major); }, UseVersion.ToString()).Dispose(); } + + private static async Task WaitForEventCountersAsync(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events) + { + DateTime startTime = DateTime.UtcNow; + int startCount = events.Count; + + while (events.Skip(startCount).Count(e => IsRequestsStartedEventCounter(e.Event)) < 2) + { + if (DateTime.UtcNow.Subtract(startTime) > TimeSpan.FromSeconds(30)) + throw new TimeoutException($"Timed out waiting for EventCounters"); + + await Task.Delay(100); + } + + static bool IsRequestsStartedEventCounter(EventWrittenEventArgs e) + { + if (e.EventName != "EventCounters") + return false; + + var dictionary = (IDictionary)e.Payload.Single(); + + return (string)dictionary["Name"] == "requests-started"; + } + } } public sealed class TelemetryTest_Http11 : TelemetryTest diff --git a/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs index 4a9588cadc46a..9af55fed4cbb9 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/Headers/CacheControlHeaderValueTest.cs @@ -457,13 +457,13 @@ public void GetCacheControlLength_DifferentValidScenariosAndNoExistingCacheContr expected = new CacheControlHeaderValue(); expected.Public = true; expected.Private = true; - expected.PrivateHeaders.Add("token1"); + expected.PrivateHeaders.Add("PLACEHOLDER"); expected.MustRevalidate = true; expected.ProxyRevalidate = true; expected.Extensions.Add(new NameValueHeaderValue("c", "d")); expected.Extensions.Add(new NameValueHeaderValue("a", "b")); - CheckGetCacheControlLength(",public, , private=\"token1\", must-revalidate, c=d, proxy-revalidate, a=b", 0, - null, 72, expected); + CheckGetCacheControlLength(",public, , private=\"PLACEHOLDER\", must-revalidate, c=d, proxy-revalidate, a=b", 0, + null, 77, expected); expected = new CacheControlHeaderValue(); expected.Private = true; @@ -491,11 +491,11 @@ public void GetCacheControlLength_DifferentValidScenariosAndExistingCacheControl expected = new CacheControlHeaderValue(); expected.Private = true; expected.PrivateHeaders.Add("token1"); - expected.PrivateHeaders.Add("token2"); + expected.PrivateHeaders.Add("PLACEHOLDER"); expected.NoCache = true; expected.NoCacheHeaders.Add("token1"); expected.NoCacheHeaders.Add("token2"); - CheckGetCacheControlLength("private=\"token2\", no-cache=\"token1, , token2,\"", 0, storeValue, 46, + CheckGetCacheControlLength("private=\"PLACEHOLDER\", no-cache=\"token1, , token2,\"", 0, storeValue, 51, expected); storeValue = new CacheControlHeaderValue(); @@ -505,7 +505,7 @@ public void GetCacheControlLength_DifferentValidScenariosAndExistingCacheControl expected = new CacheControlHeaderValue(); expected.Public = true; expected.Private = true; - expected.PrivateHeaders.Add("token1"); + expected.PrivateHeaders.Add("PLACEHOLDER"); expected.MustRevalidate = true; expected.ProxyRevalidate = true; expected.NoTransform = true; @@ -513,8 +513,8 @@ public void GetCacheControlLength_DifferentValidScenariosAndExistingCacheControl expected.Extensions.Add(new NameValueHeaderValue("a", "\"b\"")); expected.Extensions.Add(new NameValueHeaderValue("c", "d")); expected.Extensions.Add(new NameValueHeaderValue("x", "y")); // from store result - CheckGetCacheControlLength(",public, , private=\"token1\", must-revalidate, c=d, proxy-revalidate, a=\"b\"", - 0, storeValue, 74, expected); + CheckGetCacheControlLength(",public, , private=\"PLACEHOLDER\", must-revalidate, c=d, proxy-revalidate, a=\"b\"", + 0, storeValue, 79, expected); storeValue = new CacheControlHeaderValue(); storeValue.MaxStale = true; diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs index c0ddb1e504b5a..d6c3d12237c86 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs @@ -10,6 +10,7 @@ namespace System.Net.Http.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class HttpEnvironmentProxyTest { private readonly ITestOutputHelper _output; @@ -132,11 +133,11 @@ public void HttpProxy_EnvironmentProxy_Loaded() [InlineData("http://1.1.1.5:3005", "1.1.1.5", "3005", null, null)] [InlineData("http://foo@1.1.1.5", "1.1.1.5", "80", "foo", "")] [InlineData("http://[::1]:80", "[::1]", "80", null, null)] - [InlineData("foo:bar@[::1]:3128", "[::1]", "3128", "foo", "bar")] + [InlineData("foo:PLACEHOLDER@[::1]:3128", "[::1]", "3128", "foo", "PLACEHOLDER")] [InlineData("foo:Pass$!#\\.$@127.0.0.1:3128", "127.0.0.1", "3128", "foo", "Pass$!#\\.$")] [InlineData("[::1]", "[::1]", "80", null, null)] - [InlineData("domain\\foo:bar@1.1.1.1", "1.1.1.1", "80", "foo", "bar")] - [InlineData("domain%5Cfoo:bar@1.1.1.1", "1.1.1.1", "80", "foo", "bar")] + [InlineData("domain\\foo:PLACEHOLDER@1.1.1.1", "1.1.1.1", "80", "foo", "PLACEHOLDER")] + [InlineData("domain%5Cfoo:PLACEHOLDER@1.1.1.1", "1.1.1.1", "80", "foo", "PLACEHOLDER")] [InlineData("HTTP://ABC.COM/", "abc.com", "80", null, null)] [InlineData("http://10.30.62.64:7890/", "10.30.62.64", "7890", null, null)] [InlineData("http://1.2.3.4:8888/foo", "1.2.3.4", "8888", null, null)] @@ -184,7 +185,7 @@ public void HttpProxy_CredentialParsing_Basic() { IWebProxy p; - Environment.SetEnvironmentVariable("all_proxy", "http://foo:bar@1.1.1.1:3000"); + Environment.SetEnvironmentVariable("all_proxy", "http://foo:PLACEHOLDER@1.1.1.1:3000"); Assert.True(HttpEnvironmentProxy.TryCreate(out p)); Assert.NotNull(p); Assert.NotNull(p.Credentials); @@ -196,7 +197,7 @@ public void HttpProxy_CredentialParsing_Basic() Assert.NotNull(p.Credentials); // Use different user for http and https - Environment.SetEnvironmentVariable("https_proxy", "http://foo1:bar1@1.1.1.1:3000"); + Environment.SetEnvironmentVariable("https_proxy", "http://foo1:PLACEHOLDER1@1.1.1.1:3000"); Assert.True(HttpEnvironmentProxy.TryCreate(out p)); Assert.NotNull(p); Uri u = p.GetProxy(fooHttp); @@ -217,7 +218,7 @@ public void HttpProxy_Exceptions_Match() IWebProxy p; Environment.SetEnvironmentVariable("no_proxy", ".test.com,, foo.com"); - Environment.SetEnvironmentVariable("all_proxy", "http://foo:bar@1.1.1.1:3000"); + Environment.SetEnvironmentVariable("all_proxy", "http://foo:PLACEHOLDER@1.1.1.1:3000"); Assert.True(HttpEnvironmentProxy.TryCreate(out p)); Assert.NotNull(p); @@ -241,7 +242,7 @@ public static IEnumerable HttpProxyNoProxyEnvVarMemberData() [MemberData(nameof(HttpProxyNoProxyEnvVarMemberData))] public void HttpProxy_TryCreate_CaseInsensitiveVariables(string proxyEnvVar, string noProxyEnvVar) { - string proxy = "http://foo:bar@1.1.1.1:3000"; + string proxy = "http://foo:PLACEHOLDER@1.1.1.1:3000"; var options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables.Add(proxyEnvVar, proxy); @@ -274,7 +275,7 @@ public static IEnumerable HttpProxyCgiEnvVarMemberData() public void HttpProxy_TryCreateAndPossibleCgi_HttpProxyUpperCaseDisabledInCgi( string proxyEnvVar, bool cgi, bool expectedProxyUse) { - string proxy = "http://foo:bar@1.1.1.1:3000"; + string proxy = "http://foo:PLACEHOLDER@1.1.1.1:3000"; var options = new RemoteInvokeOptions(); options.StartInfo.EnvironmentVariables.Add(proxyEnvVar, proxy); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/AuthenticationTypes.cs b/src/libraries/System.Net.HttpListener/src/System/Net/AuthenticationTypes.cs index 978da9c7f0fb5..82f4d9392e558 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/AuthenticationTypes.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/AuthenticationTypes.cs @@ -3,7 +3,7 @@ namespace System.Net { - internal class AuthenticationTypes + internal static class AuthenticationTypes { internal const string NTLM = "NTLM"; internal const string Negotiate = "Negotiate"; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequest.cs b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequest.cs index 4540239e76b4e..c070c93dfc680 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequest.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequest.cs @@ -471,7 +471,7 @@ private static string UrlDecodeStringFromStringInternal(string s, Encoding e) return helper.GetString(); } - private class UrlDecoder + private sealed class UrlDecoder { private readonly int _bufferSize; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkStream.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkStream.cs index 9590c689a2be4..0d87301a10e5d 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkStream.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkStream.cs @@ -51,7 +51,7 @@ private enum State Trailer } - private class Chunk + private sealed class Chunk { public byte[] Bytes; public int Offset; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs index 3b8e200302db5..40dbf3c94ceb4 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ChunkedInputStream.cs @@ -39,7 +39,7 @@ internal sealed class ChunkedInputStream : HttpRequestStream private readonly HttpListenerContext _context; private bool _no_more_data; - private class ReadBufferState + private sealed class ReadBufferState { public byte[] Buffer; public int Offset; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs index 2ea835a7edd6b..fe4fc17f851d4 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpConnection.cs @@ -451,11 +451,9 @@ public void SendError(string? msg, int status) response.StatusCode = status; response.ContentType = "text/html"; string? description = HttpStatusDescription.Get(status); - string str; - if (msg != null) - str = string.Format("

{0} ({1})

", description, msg); - else - str = string.Format("

{0}

", description); + string str = msg != null ? + $"

{description} ({msg})

" : + $"

{description}

"; byte[] error = Encoding.Default.GetBytes(str); response.Close(error, false); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs index 156ddb5c5f68c..f990cb8ebed2f 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpHeaderStrings.cs @@ -3,7 +3,7 @@ namespace System.Net { - internal class HttpHeaderStrings + internal static class HttpHeaderStrings { internal const string Charset = "charset="; internal const string NetCoreServerName = "Microsoft-NetCore/2.0"; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs index cdcda5884bd81..558570b28cfbd 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs @@ -43,7 +43,7 @@ namespace System.Net { public sealed partial class HttpListenerRequest { - private class Context : TransportContext + private sealed class Context : TransportContext { public override ChannelBinding? GetChannelBinding(ChannelBindingKind kind) { @@ -195,7 +195,7 @@ internal void FinishInitialization() if (colon >= 0) host = host.Substring(0, colon); - string base_uri = string.Format("{0}://{1}:{2}", RequestScheme, host, LocalEndPoint!.Port); + string base_uri = $"{RequestScheme}://{host}:{LocalEndPoint!.Port}"; if (!Uri.TryCreate(base_uri + path, UriKind.Absolute, out _requestUri)) { @@ -418,7 +418,7 @@ private IAsyncResult BeginGetClientCertificateCore(AsyncCallback? requestCallbac private Uri? RequestUri => _requestUri; private bool SupportsWebSockets => true; - private class GetClientCertificateAsyncResult : LazyAsyncResult + private sealed class GetClientCertificateAsyncResult : LazyAsyncResult { public GetClientCertificateAsyncResult(object myObject, object? myState, AsyncCallback? myCallBack) : base(myObject, myState, myCallBack) { } } diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs index b084166107dfe..13a9d26c9890c 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpResponseStream.Managed.cs @@ -39,7 +39,7 @@ namespace System.Net { - internal partial class HttpResponseStream : Stream + internal sealed partial class HttpResponseStream : Stream { private HttpListenerResponse _response; private bool _ignore_errors; @@ -129,11 +129,8 @@ internal async Task WriteWebSocketHandshakeHeadersAsync() } private static byte[] s_crlf = new byte[] { 13, 10 }; - private static byte[] GetChunkSizeBytes(int size, bool final) - { - string str = string.Format("{0:x}\r\n{1}", size, final ? "\r\n" : ""); - return Encoding.ASCII.GetBytes(str); - } + private static byte[] GetChunkSizeBytes(int size, bool final) => + Encoding.ASCII.GetBytes($"{size:x}\r\n{(final ? "\r\n" : "")}"); internal void InternalWrite(byte[] buffer, int offset, int count) { diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs index 349fb288a2825..d0c5b1c847092 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpStreamAsyncResult.cs @@ -34,7 +34,7 @@ namespace System.Net { - internal class HttpStreamAsyncResult : IAsyncResult + internal sealed class HttpStreamAsyncResult : IAsyncResult { private object _locker = new object(); private ManualResetEvent? _handle; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs index d5c2f56cd1aa0..91cdec0c0a0c4 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/ListenerAsyncResult.Managed.cs @@ -36,7 +36,7 @@ namespace System.Net { - internal class ListenerAsyncResult : IAsyncResult + internal sealed class ListenerAsyncResult : IAsyncResult { private ManualResetEvent? _handle; private bool _synch; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/ServiceNameStore.cs b/src/libraries/System.Net.HttpListener/src/System/Net/ServiceNameStore.cs index 9c92cf20bb5a5..7ccc90973af94 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/ServiceNameStore.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/ServiceNameStore.cs @@ -9,7 +9,7 @@ namespace System.Net { - internal class ServiceNameStore + internal sealed class ServiceNameStore { private readonly List _serviceNames; private ServiceNameCollection? _serviceNameCollection; @@ -118,8 +118,7 @@ public ServiceNameStore() string normalizedHost = constructedUri.GetComponents( UriComponents.NormalizedHost, UriFormat.SafeUnescaped); - string normalizedServiceName = string.Format(CultureInfo.InvariantCulture, - "{0}{1}{2}{3}", prefix, normalizedHost, port, distinguisher); + string normalizedServiceName = prefix + normalizedHost + port + distinguisher; // Don't return the new one unless we absolutely have to. It may have only changed casing. if (inputServiceName.Equals(normalizedServiceName, StringComparison.OrdinalIgnoreCase)) diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs index 2d47b0ff70c40..13562495cb46b 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs @@ -926,9 +926,7 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) if (decodedOutgoingBlob != null) { // Prefix SPNEGO token/NTLM challenge with scheme per RFC 4559, MS-NTHT - outBlob = string.Format("{0} {1}", - headerScheme == AuthenticationSchemes.Ntlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Negotiate, - Convert.ToBase64String(decodedOutgoingBlob)); + outBlob = $"{(headerScheme == AuthenticationSchemes.Ntlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Negotiate)} {Convert.ToBase64String(decodedOutgoingBlob)}"; } if (!error) @@ -1780,7 +1778,7 @@ private static unsafe int GetTokenSizeFromBlob(IntPtr blob) } - private class DisconnectAsyncResult : IAsyncResult + private sealed class DisconnectAsyncResult : IAsyncResult { private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(WaitCallback); diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequestContext.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequestContext.cs index d49bc4aff1a7f..e9dab38b2edf6 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequestContext.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerRequestContext.cs @@ -7,7 +7,7 @@ namespace System.Net { - internal class HttpListenerRequestContext : TransportContext + internal sealed class HttpListenerRequestContext : TransportContext { private readonly HttpListenerRequest _request; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs index 4f7fa86e43c85..84d4222f8cd11 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBase.cs @@ -2005,7 +2005,7 @@ public static KeepAliveTracker Create(TimeSpan keepAliveInterval) return new DisabledKeepAliveTracker(); } - private class DisabledKeepAliveTracker : KeepAliveTracker + private sealed class DisabledKeepAliveTracker : KeepAliveTracker { public override void OnDataReceived() { @@ -2033,7 +2033,7 @@ public override void Dispose() } } - private class DefaultKeepAliveTracker : KeepAliveTracker + private sealed class DefaultKeepAliveTracker : KeepAliveTracker { private static readonly TimerCallback s_KeepAliveTimerElapsedCallback = new TimerCallback(OnKeepAlive); private readonly TimeSpan _keepAliveInterval; @@ -2128,7 +2128,7 @@ private TimeSpan GetTimeElapsed(Stopwatch watch) } } - private class OutstandingOperationHelper : IDisposable + private sealed class OutstandingOperationHelper : IDisposable { private volatile int _operationsOutstanding; private volatile CancellationTokenSource? _cancellationTokenSource; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs index aa217407e6683..838963d98d8d8 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketBuffer.cs @@ -21,7 +21,7 @@ namespace System.Net.WebSockets // // *RBS = ReceiveBufferSize, *SBS = SendBufferSize // *PBS = PropertyBufferSize (32-bit: 16, 64 bit: 20 bytes) - internal class WebSocketBuffer : IDisposable + internal sealed class WebSocketBuffer : IDisposable { private const int NativeOverheadBufferSize = 144; private static readonly int s_PropertyBufferSize = 3 * sizeof(uint) + IntPtr.Size; @@ -680,7 +680,7 @@ private static class SendBufferState public const int SendPayloadSpecified = 1; } - private class PayloadReceiveResult + private sealed class PayloadReceiveResult { public int Count { get; set; } public bool EndOfMessage { get; } diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs index 1a07f99cb8686..a3702c28d23aa 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/WebSockets/WebSocketHttpListenerDuplexStream.cs @@ -31,7 +31,7 @@ internal sealed class WebSocketHttpListenerDuplexStream : Stream, WebSocketBase. private int _cleanedUp; #if DEBUG - private class OutstandingOperations + private sealed class OutstandingOperations { internal int _reads; internal int _writes; @@ -723,7 +723,7 @@ private static void OnReadCompleted(object? sender, HttpListenerAsyncEventArgs e } } - internal class HttpListenerAsyncEventArgs : EventArgs, IDisposable + internal sealed class HttpListenerAsyncEventArgs : EventArgs, IDisposable { private const int Free = 0; private const int InProgress = 1; @@ -874,7 +874,7 @@ public event EventHandler Completed } } - protected virtual void OnCompleted(HttpListenerAsyncEventArgs e) + private void OnCompleted(HttpListenerAsyncEventArgs e) { m_Completed?.Invoke(e._currentStream, e); } diff --git a/src/libraries/System.Net.Mail/src/System/Net/BufferedReadStream.cs b/src/libraries/System.Net.Mail/src/System/Net/BufferedReadStream.cs index 325e064d4a44f..8c8b778f74c79 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/BufferedReadStream.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/BufferedReadStream.cs @@ -8,7 +8,7 @@ namespace System.Net { - internal class BufferedReadStream : DelegatedStream + internal sealed class BufferedReadStream : DelegatedStream { private byte[]? _storedBuffer; private int _storedLength; @@ -157,7 +157,7 @@ internal void Push(byte[] buffer, int offset, int count) Buffer.BlockCopy(buffer, offset, _storedBuffer!, _storedOffset, count); } - private class ReadAsyncResult : LazyAsyncResult + private sealed class ReadAsyncResult : LazyAsyncResult { private readonly BufferedReadStream _parent; private int _read; diff --git a/src/libraries/System.Net.Mail/src/System/Net/CloseableStream.cs b/src/libraries/System.Net.Mail/src/System/Net/CloseableStream.cs index b4f3cdac9c1a0..adf9f88c0c10d 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/CloseableStream.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/CloseableStream.cs @@ -7,7 +7,7 @@ namespace System.Net { /// Provides a stream that notifies an event when the Close method is called. - internal class ClosableStream : DelegatedStream + internal sealed class ClosableStream : DelegatedStream { private readonly EventHandler? _onClose; private int _closed; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailMessage.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailMessage.cs index 9da31d1b9d9c2..072168565d8d2 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailMessage.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailMessage.cs @@ -426,17 +426,15 @@ private void SetContent(bool allowUnicode) part = new MimeMultiPart(MimeMultiPartType.Mixed); part.Parts.Add(viewsPart); - MimeMultiPart attachmentsPart = new MimeMultiPart(MimeMultiPartType.Mixed); foreach (Attachment attachment in Attachments) { if (attachment != null) { //ensure we can read from the stream. attachment.PrepareForSending(allowUnicode); - attachmentsPart.Parts.Add(attachment.MimePart); + part.Parts.Add(attachment.MimePart); } } - part.Parts.Add(attachmentsPart); _message.Content = part; } // If there is no Attachement, AND only "1" Alternate View AND !!no body!! diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailPriority.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailPriority.cs index 4b4fabb800fca..aed784ce95b50 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailPriority.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailPriority.cs @@ -15,7 +15,7 @@ public enum MailPriority High = 2 } - internal class Message + internal sealed class Message { #region Fields @@ -235,7 +235,7 @@ internal HeaderCollection EnvelopeHeaders } [DisallowNull] - internal virtual MimeBasePart? Content + internal MimeBasePart? Content { get { @@ -277,7 +277,7 @@ internal void EmptySendCallback(IAsyncResult result) context._result.InvokeCallback(e); } - internal class EmptySendContext + internal sealed class EmptySendContext { internal EmptySendContext(BaseWriter writer, LazyAsyncResult result) { @@ -289,7 +289,7 @@ internal EmptySendContext(BaseWriter writer, LazyAsyncResult result) internal BaseWriter _writer; } - internal virtual IAsyncResult BeginSend(BaseWriter writer, bool sendEnvelope, bool allowUnicode, + internal IAsyncResult BeginSend(BaseWriter writer, bool sendEnvelope, bool allowUnicode, AsyncCallback? callback, object? state) { PrepareHeaders(sendEnvelope, allowUnicode); @@ -312,7 +312,7 @@ internal virtual IAsyncResult BeginSend(BaseWriter writer, bool sendEnvelope, bo } } - internal virtual void EndSend(IAsyncResult asyncResult) + internal void EndSend(IAsyncResult asyncResult) { if (asyncResult == null) { @@ -346,7 +346,7 @@ internal virtual void EndSend(IAsyncResult asyncResult) } } - internal virtual void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode) + internal void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode) { if (sendEnvelope) { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailWriter.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailWriter.cs index 6761539a2d411..f41b6992e6563 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailWriter.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailWriter.cs @@ -8,7 +8,7 @@ namespace System.Net.Mail { - internal class MailWriter : BaseWriter + internal sealed class MailWriter : BaseWriter { /// /// ctor. diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.Auth.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.Auth.cs index 362abc10db353..0fa1b892fd9a5 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.Auth.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.Auth.cs @@ -11,7 +11,7 @@ internal enum SupportedAuth GSSAPI = 4 }; - internal partial class SmtpConnection + internal sealed partial class SmtpConnection { private bool _serverSupportsEai; private bool _dsnEnabled; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs index 1bebf2255e864..e883bfe897701 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpConnection.cs @@ -17,7 +17,7 @@ namespace System.Net.Mail { - internal partial class SmtpConnection + internal sealed partial class SmtpConnection { private static readonly ContextCallback s_AuthenticateCallback = new ContextCallback(AuthenticateCallback); @@ -333,7 +333,7 @@ private static void AuthenticateCallback(object? state) context._result = context._module.Authenticate(null, context._credential, context._thisPtr, context._spn, context._token); } - private class AuthenticateCallbackContext + private sealed class AuthenticateCallbackContext { internal AuthenticateCallbackContext(SmtpConnection thisPtr, ISmtpAuthenticationModule module, NetworkCredential credential, string? spn, ChannelBinding? Token) { @@ -374,7 +374,7 @@ private void OnClose(object? sender, EventArgs args) DataStopCommand.Send(this); } - private class ConnectAndHandshakeAsyncResult : LazyAsyncResult + private sealed class ConnectAndHandshakeAsyncResult : LazyAsyncResult { private string? _authResponse; private readonly SmtpConnection _connection; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReader.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReader.cs index ddc227fe28e9c..e82db3d7e9ebc 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReader.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReader.cs @@ -7,7 +7,7 @@ namespace System.Net.Mail { //streams are read only; return of 0 means end of server's reply - internal class SmtpReplyReader + internal sealed class SmtpReplyReader { private readonly SmtpReplyReaderFactory _reader; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReaderFactory.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReaderFactory.cs index 3cdc18526c891..099a10ecab784 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReaderFactory.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpReplyReaderFactory.cs @@ -10,7 +10,7 @@ namespace System.Net.Mail { //Streams created are read only and return 0 once a full server reply has been read //To get the next server reply, call GetNextReplyReader - internal class SmtpReplyReaderFactory + internal sealed class SmtpReplyReaderFactory { private enum ReadState { @@ -367,7 +367,7 @@ internal LineInfo[] ReadLines(SmtpReplyReader caller, bool oneLine) } } - private class ReadLinesAsyncResult : LazyAsyncResult + private sealed class ReadLinesAsyncResult : LazyAsyncResult { private StringBuilder? _builder; private ArrayList? _lines; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs index 2e427ec372ebd..623bff7d5755d 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpTransport.cs @@ -10,7 +10,7 @@ namespace System.Net.Mail { - internal class SmtpTransport + internal sealed class SmtpTransport { internal const int DefaultPort = 25; @@ -253,7 +253,7 @@ internal MailWriter SendMail(MailAddress sender, MailAddressCollection recipient } } - internal class SendMailAsyncResult : LazyAsyncResult + internal sealed class SendMailAsyncResult : LazyAsyncResult { private readonly SmtpConnection _connection; private readonly MailAddress _from; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64Encoder.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64Encoder.cs index 3ddd40bf6384e..1c08b36f0536a 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64Encoder.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64Encoder.cs @@ -5,7 +5,7 @@ namespace System.Net.Mime { - internal class Base64Encoder : ByteEncoder + internal sealed class Base64Encoder : ByteEncoder { private static ReadOnlySpan Base64EncodeMap => new byte[] { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64WriteStateInfo.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64WriteStateInfo.cs index ba534e09994d9..726a0b988bb03 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64WriteStateInfo.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/Base64WriteStateInfo.cs @@ -3,7 +3,7 @@ namespace System.Net.Mime { - internal class Base64WriteStateInfo : WriteStateInfoBase + internal sealed class Base64WriteStateInfo : WriteStateInfoBase { internal Base64WriteStateInfo() { } diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/EightBitStream.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/EightBitStream.cs index 16fa22cd74302..55b71f81a09f4 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/EightBitStream.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/EightBitStream.cs @@ -21,7 +21,7 @@ namespace System.Net.Mime /// For legacy (app-compat) reasons we have chosen to remove the enforcement /// and rename the class from SevenBitStream to EightBitStream. /// - internal class EightBitStream : DelegatedStream, IEncodableStream + internal sealed class EightBitStream : DelegatedStream, IEncodableStream { private WriteStateInfoBase? _writeState; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/EncodedStreamFactory.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/EncodedStreamFactory.cs index 270cb00956655..a82d3676f5ddf 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/EncodedStreamFactory.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/EncodedStreamFactory.cs @@ -5,7 +5,7 @@ namespace System.Net.Mime { - internal class EncodedStreamFactory + internal sealed class EncodedStreamFactory { //RFC 2822: no encoded-word line should be longer than 76 characters not including the soft CRLF //since the header length is unknown (if there even is one) we're going to be slightly more conservative @@ -20,7 +20,7 @@ internal class EncodedStreamFactory internal IEncodableStream GetEncoderForHeader(Encoding encoding, bool useBase64Encoding, int headerTextLength) { byte[] header = CreateHeader(encoding, useBase64Encoding); - byte[] footer = CreateFooter(); + byte[] footer = s_footer; WriteStateInfoBase writeState; if (useBase64Encoding) @@ -36,10 +36,10 @@ internal IEncodableStream GetEncoderForHeader(Encoding encoding, bool useBase64E //Create the header for what type of byte encoding is going to be used //based on the encoding type and if base64 encoding should be forced //sample header: =?utf-8?B? - protected byte[] CreateHeader(Encoding encoding, bool useBase64Encoding) => + private byte[] CreateHeader(Encoding encoding, bool useBase64Encoding) => Encoding.ASCII.GetBytes("=?" + encoding.HeaderName + "?" + (useBase64Encoding ? "B?" : "Q?")); - //creates the footer that marks the end of a quoted string of some sort - protected byte[] CreateFooter() => new byte[] { (byte)'?', (byte)'=' }; + //The footer that marks the end of a quoted string of some sort + private static readonly byte[] s_footer = new byte[] { (byte)'?', (byte)'=' }; } } diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs index 239b3ba59d88f..89f988930f6ef 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/HeaderCollection.cs @@ -10,7 +10,7 @@ namespace System.Net.Mime /// /// Summary description for HeaderCollection. /// - internal class HeaderCollection : NameValueCollection + internal sealed class HeaderCollection : NameValueCollection { // default constructor // intentionally override the default comparer in the derived base class diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeBasePart.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeBasePart.cs index b5e0abab07948..be3092fa30629 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeBasePart.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeBasePart.cs @@ -252,7 +252,7 @@ internal void EndSend(IAsyncResult asyncResult) } } - internal class MimePartAsyncResult : LazyAsyncResult + internal sealed class MimePartAsyncResult : LazyAsyncResult { internal MimePartAsyncResult(MimeBasePart part, object? state, AsyncCallback? callback) : base(part, state, callback) { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs index 95c5362d6fac3..bb300289dfa8c 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeMultiPart.cs @@ -10,7 +10,7 @@ namespace System.Net.Mime { - internal class MimeMultiPart : MimeBasePart + internal sealed class MimeMultiPart : MimeBasePart { private Collection? _parts; private static int s_boundary; @@ -213,7 +213,7 @@ internal override IAsyncResult BeginSend(BaseWriter writer, AsyncCallback? callb return result; } - internal class MimePartContext + internal sealed class MimePartContext { internal MimePartContext(BaseWriter writer, LazyAsyncResult result, IEnumerator partsEnumerator) { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimePart.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimePart.cs index 886e00192e2fc..218df997c6e22 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimePart.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimePart.cs @@ -14,7 +14,7 @@ namespace System.Net.Mime /// /// Summary description for MimePart. /// - internal class MimePart : MimeBasePart, IDisposable + internal sealed class MimePart : MimeBasePart, IDisposable { private Stream? _stream; private bool _streamSet; @@ -301,7 +301,7 @@ internal void ContentStreamCallback(IAsyncResult result) } } - internal class MimePartContext + internal sealed class MimePartContext { internal MimePartContext(BaseWriter writer, LazyAsyncResult result) { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeWriter.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeWriter.cs index 3974d68407976..8439cfa8f48f4 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeWriter.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/MimeWriter.cs @@ -12,7 +12,7 @@ namespace System.Net.Mime /// Provides an abstraction for writing a MIME multi-part /// message. /// - internal class MimeWriter : BaseWriter + internal sealed class MimeWriter : BaseWriter { private static readonly byte[] s_DASHDASH = new byte[] { (byte)'-', (byte)'-' }; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncodedStream.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncodedStream.cs index 21e2624e4f387..5aa6273c42c62 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncodedStream.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncodedStream.cs @@ -13,7 +13,7 @@ namespace System.Net.Mime /// buffer as the data being encoded will most likely grow. /// Encoding and decoding is done transparently to the caller. /// - internal class QEncodedStream : DelegatedStream, IEncodableStream + internal sealed class QEncodedStream : DelegatedStream, IEncodableStream { private static ReadOnlySpan HexDecodeMap => new byte[] // rely on C# compiler optimization to eliminate allocation @@ -232,7 +232,7 @@ private sealed class ReadStateInfo internal short Byte { get; set; } = -1; } - private class WriteAsyncResult : LazyAsyncResult + private sealed class WriteAsyncResult : LazyAsyncResult { private static readonly AsyncCallback s_onWrite = OnWrite; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncoder.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncoder.cs index 76809cbd33def..ce4a0f57227fe 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncoder.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/QEncoder.cs @@ -3,7 +3,7 @@ namespace System.Net.Mime { - internal class QEncoder : ByteEncoder + internal sealed class QEncoder : ByteEncoder { private const int SizeOfQEncodedChar = 3; // e.g. "=3A" diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/QuotedPrintableStream.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/QuotedPrintableStream.cs index eadab7b9f6d0f..0fa988eca3a28 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/QuotedPrintableStream.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/QuotedPrintableStream.cs @@ -16,7 +16,7 @@ namespace System.Net.Mime /// This stream should only be used for the e-mail content. /// Use QEncodedStream for encoding headers. /// - internal class QuotedPrintableStream : DelegatedStream, IEncodableStream + internal sealed class QuotedPrintableStream : DelegatedStream, IEncodableStream { //should we encode CRLF or not? private readonly bool _encodeCRLF; diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mime/SmtpDateTime.cs b/src/libraries/System.Net.Mail/src/System/Net/Mime/SmtpDateTime.cs index 4371d99ec3ad2..106fc5540a63c 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mime/SmtpDateTime.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mime/SmtpDateTime.cs @@ -31,7 +31,7 @@ namespace System.Net.Mime // stores a Date and a Time Zone. These are parsed and formatted according to the // rules in RFC 2822 section 3.3. // This class is immutable - internal class SmtpDateTime + internal sealed class SmtpDateTime { #region constants diff --git a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs index 4520e439c4e83..e269cc6eeea77 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs @@ -26,7 +26,7 @@ public class LoopbackSmtpServer : IDisposable private bool _disposed = false; private readonly Socket _listenSocket; private readonly ConcurrentBag _socketsToDispose; - private long _messageCounter = new Random().Next(1000, 2000); + private long _messageCounter = Random.Shared.Next(1000, 2000); public readonly int Port; public SmtpClient CreateClient() => new SmtpClient("localhost", Port); diff --git a/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs b/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs index 1d7f0e236ef4e..e891a35d8b35d 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/MailMessageTest.cs @@ -12,6 +12,7 @@ using System.IO; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using Xunit; namespace System.Net.Mail.Tests @@ -27,7 +28,7 @@ public MailMessageTest() messageWithSubjectAndBody.Subject = "the subject"; messageWithSubjectAndBody.Body = "hello"; messageWithSubjectAndBody.AlternateViews.Add(AlternateView.CreateAlternateViewFromString("hello", null, "text/html")); - Attachment a = Attachment.CreateAttachmentFromString("blah blah", "text/plain"); + Attachment a = Attachment.CreateAttachmentFromString("blah blah", "AttachmentName"); messageWithSubjectAndBody.Attachments.Add(a); emptyMessage = new MailMessage("from@example.com", "r1@t1.com, r2@t1.com"); @@ -65,6 +66,8 @@ public void AttachmentTest() Assert.Equal(1, messageWithSubjectAndBody.Attachments.Count); Attachment at = messageWithSubjectAndBody.Attachments[0]; Assert.Equal("text/plain", at.ContentType.MediaType); + Assert.Equal("AttachmentName", at.ContentType.Name); + Assert.Equal("AttachmentName", at.Name); } [Fact] @@ -145,6 +148,59 @@ public void SubjectAndEncodingTest() Assert.Equal(Encoding.UTF8.CodePage, msg.SubjectEncoding.CodePage); } + [Fact] + [PlatformSpecific(~TestPlatforms.Browser)] // Not passing as internal System.Net.Mail.MailWriter stripped from build + public void SendMailMessageTest() + { + string expected = @"X-Sender: from@example.com +X-Receiver: to@example.com +MIME-Version: 1.0 +From: from@example.com +To: to@example.com +Date: DATE +Subject: the subject +Content-Type: multipart/mixed; + boundary=--boundary_1_GUID + + +----boundary_1_GUID +Content-Type: multipart/alternative; + boundary=--boundary_0_GUID + + +----boundary_0_GUID +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: quoted-printable + +hello +----boundary_0_GUID +Content-Type: text/html; charset=us-ascii +Content-Transfer-Encoding: quoted-printable + +hello +----boundary_0_GUID-- + +----boundary_1_GUID +Content-Type: text/plain; charset=us-ascii +Content-Disposition: attachment +Content-Transfer-Encoding: quoted-printable + +blah blah +----boundary_1_GUID-- +"; + expected = expected.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n"); + + string sent = DecodeSentMailMessage(messageWithSubjectAndBody).Raw; + sent = Regex.Replace(sent, "Date:.*?\r\n", "Date: DATE\r\n"); + sent = Regex.Replace(sent, @"_.{8}-.{4}-.{4}-.{4}-.{12}", "_GUID"); + + // name and charset can appear in different order + Assert.Contains("; name=AttachmentName", sent); + sent = sent.Replace("; name=AttachmentName", string.Empty); + + Assert.Equal(expected, sent); + } + [Fact] [PlatformSpecific(~TestPlatforms.Browser)] // Not passing as internal System.Net.Mail.MailWriter stripped from build public void SentSpecialLengthMailAttachment_Base64Decode_Success() @@ -161,7 +217,9 @@ public void SentSpecialLengthMailAttachment_Base64Decode_Success() { var message = new MailMessage("sender@test.com", "user1@pop.local", "testSubject", "testBody"); message.Attachments.Add(new Attachment(tempFile.Path)); - string decodedAttachment = DecodeSentMailMessage(message); + + string attachment = DecodeSentMailMessage(message).Attachment; + string decodedAttachment = Encoding.UTF8.GetString(Convert.FromBase64String(attachment)); // Make sure last byte is not encoded twice. Assert.Equal(specialLength, decodedAttachment.Length); @@ -169,7 +227,7 @@ public void SentSpecialLengthMailAttachment_Base64Decode_Success() } } - private static string DecodeSentMailMessage(MailMessage mail) + private static (string Raw, string Attachment) DecodeSentMailMessage(MailMessage mail) { // Create a MIME message that would be sent using System.Net.Mail. var stream = new MemoryStream(); @@ -192,11 +250,9 @@ private static string DecodeSentMailMessage(MailMessage mail) // Decode contents. string result = Encoding.UTF8.GetString(stream.ToArray()); - string encodedAttachment = result.Split(new[] { "attachment" }, StringSplitOptions.None)[1].Trim().Split('-')[0].Trim(); - byte[] data = Convert.FromBase64String(encodedAttachment); - string decodedString = Encoding.UTF8.GetString(data); + string attachment = result.Split(new[] { "attachment" }, StringSplitOptions.None)[1].Trim().Split('-')[0].Trim(); - return decodedString; + return (result, attachment); } } } diff --git a/src/libraries/System.Net.Mail/tests/Unit/MessageTests/MessageHeaderBehaviorTest.cs b/src/libraries/System.Net.Mail/tests/Unit/MessageTests/MessageHeaderBehaviorTest.cs index 8040f4c793172..f31df3d6f754f 100644 --- a/src/libraries/System.Net.Mail/tests/Unit/MessageTests/MessageHeaderBehaviorTest.cs +++ b/src/libraries/System.Net.Mail/tests/Unit/MessageTests/MessageHeaderBehaviorTest.cs @@ -172,7 +172,7 @@ public void MessageSubject_WhenEmpty_WithHeaderSet_ShouldDiscardHeaderAndLeaveSu [Fact] public void MessageSubject_Unicode_Accepted() { - _message.From = new MailAddress("from@example.com"); ; + _message.From = new MailAddress("from@example.com"); string input = "Hi \u00DC Bob"; _message.Subject = input; @@ -188,7 +188,7 @@ public void MessageSubject_Unicode_Accepted() [Fact] public void MessageSubject_Utf8EncodedUnicode_Accepted() { - _message.From = new MailAddress("from@example.com"); ; + _message.From = new MailAddress("from@example.com"); string input = "=?utf-8?B?SGkgw5wgQm9i?="; _message.Subject = input; @@ -204,7 +204,7 @@ public void MessageSubject_Utf8EncodedUnicode_Accepted() [Fact] public void MessageSubject_UnknownEncodingEncodedUnicode_Accepted() { - _message.From = new MailAddress("from@example.com"); ; + _message.From = new MailAddress("from@example.com"); string input = "=?utf-99?B?SGkgw5wgQm9i?="; _message.Subject = input; @@ -220,7 +220,7 @@ public void MessageSubject_UnknownEncodingEncodedUnicode_Accepted() [Fact] public void MessageSubject_BadBase64EncodedUnicode_Accepted() { - _message.From = new MailAddress("from@example.com"); ; + _message.From = new MailAddress("from@example.com"); string input = "=?utf-8?B?SGkgw5.wgQm9i?="; // Extra . in the middle _message.Subject = input; @@ -236,7 +236,7 @@ public void MessageSubject_BadBase64EncodedUnicode_Accepted() [Fact] public void MessageSubject_MultiLineEncodedUnicode_Accepted() { - _message.From = new MailAddress("from@example.com"); ; + _message.From = new MailAddress("from@example.com"); string input = "=?utf-8?B?SGkgw5wg?=\r\n =?utf-8?B?Qm9i?="; _message.Subject = input; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPGlobalProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPGlobalProperties.cs index c4b5d6c38bbec..9ed00ca897125 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPGlobalProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPGlobalProperties.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIPGlobalProperties : UnixIPGlobalProperties + internal sealed class BsdIPGlobalProperties : UnixIPGlobalProperties { private unsafe TcpConnectionInformation[] GetTcpConnections(bool listeners) { diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4GlobalStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4GlobalStatistics.cs index 83229a5cebbb1..c39a9170d4d1e 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4GlobalStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4GlobalStatistics.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIPv4GlobalStatistics : IPGlobalStatistics + internal sealed class BsdIPv4GlobalStatistics : IPGlobalStatistics { private readonly long _outboundPackets; private readonly long _outputPacketsNoRoute; @@ -91,7 +91,7 @@ public unsafe BsdIPv4GlobalStatistics() public override long PacketReassemblyFailures { get { return _cantFrags; } } - public override long PacketReassemblyTimeout { get { throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform); ; } } + public override long PacketReassemblyTimeout { get { throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform); } } public override long PacketsFragmented { get { return _datagramsFragmented; } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceProperties.cs index aebe697c06c63..f847538cd353e 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceProperties.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIPv4InterfaceProperties : UnixIPv4InterfaceProperties + internal sealed class BsdIPv4InterfaceProperties : UnixIPv4InterfaceProperties { private readonly int _mtu; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceStatistics.cs index 6286570a3352d..4a9545432824b 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv4InterfaceStatistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIPv4InterfaceStatistics : IPv4InterfaceStatistics + internal sealed class BsdIPv4InterfaceStatistics : IPv4InterfaceStatistics { private readonly BsdIpInterfaceStatistics _statistics; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv6InterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv6InterfaceProperties.cs index 6c54c0463cab1..8ef7674e78a88 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv6InterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIPv6InterfaceProperties.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIPv6InterfaceProperties : UnixIPv6InterfaceProperties + internal sealed class BsdIPv6InterfaceProperties : UnixIPv6InterfaceProperties { private readonly int _mtu; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV4Statistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV4Statistics.cs index 2e2266d2c969b..d8322222a851f 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV4Statistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV4Statistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIcmpV4Statistics : IcmpV4Statistics + internal sealed class BsdIcmpV4Statistics : IcmpV4Statistics { private readonly long _addressMaskRepliesReceived; private readonly long _addressMaskRepliesSent; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV6Statistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV6Statistics.cs index 34596a51d3b6b..fe23a313fede2 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV6Statistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIcmpV6Statistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIcmpV6Statistics : IcmpV6Statistics + internal sealed class BsdIcmpV6Statistics : IcmpV6Statistics { private readonly long _destinationUnreachableMessagesReceived; private readonly long _destinationUnreachableMessagesSent; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceProperties.cs index 9a1c548bc402a..93b92532b42c3 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceProperties.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIpInterfaceProperties : UnixIPInterfaceProperties + internal sealed class BsdIpInterfaceProperties : UnixIPInterfaceProperties { private readonly BsdIPv4InterfaceProperties _ipv4Properties; private readonly BsdIPv6InterfaceProperties _ipv6Properties; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceStatistics.cs index b74fa0e5dc806..94bd148613a69 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdIpInterfaceStatistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdIpInterfaceStatistics : IPInterfaceStatistics + internal sealed class BsdIpInterfaceStatistics : IPInterfaceStatistics { private readonly long _outputQueueLength; private readonly long _inPackets; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdNetworkInterface.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdNetworkInterface.cs index 70d2e37d38cd5..16aa0ed05ef47 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdNetworkInterface.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdNetworkInterface.cs @@ -5,14 +5,14 @@ namespace System.Net.NetworkInformation { - internal class BsdNetworkInterface : UnixNetworkInterface + internal sealed class BsdNetworkInterface : UnixNetworkInterface { private readonly BsdIpInterfaceProperties _ipProperties; private readonly OperationalStatus _operationalStatus; private readonly bool _supportsMulticast; private readonly long _speed; - protected unsafe BsdNetworkInterface(string name, int index) : base(name) + private unsafe BsdNetworkInterface(string name, int index) : base(name) { _index = index; Interop.Sys.NativeIPInterfaceStatistics nativeStats; @@ -158,6 +158,6 @@ public override IPv4InterfaceStatistics GetIPv4Statistics() public override bool SupportsMulticast { get { return _supportsMulticast; } } - public override bool IsReceiveOnly { get { throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform); } } + public override bool IsReceiveOnly { get { return false; } } } } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdTcpStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdTcpStatistics.cs index adaa3d9b54600..ba8b649320539 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdTcpStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdTcpStatistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class BsdTcpStatistics : TcpStatistics + internal sealed class BsdTcpStatistics : TcpStatistics { private readonly long _connectionsAccepted; private readonly long _connectionsInitiated; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdUdpStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdUdpStatistics.cs index 8b7df8ac4fb89..c60a62173e6da 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdUdpStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/BsdUdpStatistics.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { - internal class BsdUdpStatistics : UdpStatistics + internal sealed class BsdUdpStatistics : UdpStatistics { private readonly long _datagramsReceived; private readonly long _datagramsSent; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/InternalIPAddressCollection.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/InternalIPAddressCollection.cs index 03e7236943a78..0020c73b1c14f 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/InternalIPAddressCollection.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/InternalIPAddressCollection.cs @@ -5,11 +5,11 @@ namespace System.Net.NetworkInformation { - internal class InternalIPAddressCollection : IPAddressCollection + internal sealed class InternalIPAddressCollection : IPAddressCollection { private readonly List _addresses; - protected internal InternalIPAddressCollection() + internal InternalIPAddressCollection() { _addresses = new List(); } diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPAddressInformation.cs index b053e499c498f..e2bc3f91a70a2 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPAddressInformation.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPAddressInformation : IPAddressInformation + internal sealed class LinuxIPAddressInformation : IPAddressInformation { private readonly IPAddress _address; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalProperties.cs index 792cea3877961..52f09dbed8319 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalProperties.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPGlobalProperties : UnixIPGlobalProperties + internal sealed class LinuxIPGlobalProperties : UnixIPGlobalProperties { public override TcpConnectionInformation[] GetActiveTcpConnections() { diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalStatistics.cs index 99c5faa298ee4..6254c1d44531c 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPGlobalStatistics.cs @@ -6,7 +6,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPGlobalStatistics : IPGlobalStatistics + internal sealed class LinuxIPGlobalStatistics : IPGlobalStatistics { // MIB-II statistics data. private readonly IPGlobalStatisticsTable _table; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceProperties.cs index aae4821a4490b..99295ca32ca39 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceProperties.cs @@ -6,7 +6,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPInterfaceProperties : UnixIPInterfaceProperties + internal sealed class LinuxIPInterfaceProperties : UnixIPInterfaceProperties { private readonly LinuxNetworkInterface _linuxNetworkInterface; private readonly GatewayIPAddressInformationCollection _gatewayAddresses; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceStatistics.cs index 12c6b640527b1..5f7d62841fab4 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPInterfaceStatistics.cs @@ -9,7 +9,7 @@ namespace System.Net.NetworkInformation /// IPInterfaceStatistics provider for Linux. /// Reads information out of /proc/net/dev and other locations. /// - internal class LinuxIPInterfaceStatistics : IPInterfaceStatistics + internal sealed class LinuxIPInterfaceStatistics : IPInterfaceStatistics { // /proc/net/dev statistics table for network interface private readonly IPInterfaceStatisticsTable _table; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceProperties.cs index b4af668bd4395..0f0557b7af96e 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceProperties.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPv4InterfaceProperties : UnixIPv4InterfaceProperties + internal sealed class LinuxIPv4InterfaceProperties : UnixIPv4InterfaceProperties { private readonly LinuxNetworkInterface _linuxNetworkInterface; private readonly bool _isForwardingEnabled; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceStatistics.cs index 708dee0213092..ec0b7a2642cc1 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv4InterfaceStatistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPv4InterfaceStatistics : IPv4InterfaceStatistics + internal sealed class LinuxIPv4InterfaceStatistics : IPv4InterfaceStatistics { private readonly LinuxIPInterfaceStatistics _statistics; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv6InterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv6InterfaceProperties.cs index 90d4d14797bbe..400a92d0c4909 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv6InterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIPv6InterfaceProperties.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIPv6InterfaceProperties : UnixIPv6InterfaceProperties + internal sealed class LinuxIPv6InterfaceProperties : UnixIPv6InterfaceProperties { private readonly LinuxNetworkInterface _linuxNetworkInterface; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV4Statistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV4Statistics.cs index d5d3fc700b822..85e073b977afb 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV4Statistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV4Statistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIcmpV4Statistics : IcmpV4Statistics + internal sealed class LinuxIcmpV4Statistics : IcmpV4Statistics { private readonly Icmpv4StatisticsTable _table; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV6Statistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV6Statistics.cs index 44121ae4001a7..50ba8bd305e6c 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV6Statistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxIcmpV6Statistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxIcmpV6Statistics : IcmpV6Statistics + internal sealed class LinuxIcmpV6Statistics : IcmpV6Statistics { private readonly Icmpv6StatisticsTable _table; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs index d881a924bc367..c835deebd2396 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxNetworkInterface.cs @@ -11,7 +11,7 @@ namespace System.Net.NetworkInformation /// /// Implements a NetworkInterface on Linux. /// - internal class LinuxNetworkInterface : UnixNetworkInterface + internal sealed class LinuxNetworkInterface : UnixNetworkInterface { private OperationalStatus _operationalStatus; private bool _supportsMulticast; @@ -20,7 +20,7 @@ internal class LinuxNetworkInterface : UnixNetworkInterface private NetworkInterfaceType _interfaceType = NetworkInterfaceType.Unknown; private readonly LinuxIPInterfaceProperties _ipProperties; - internal class LinuxNetworkInterfaceSystemProperties + internal sealed class LinuxNetworkInterfaceSystemProperties { internal string[]? IPv4Routes; internal string[]? IPv6Routes; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxTcpStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxTcpStatistics.cs index 2d8d1d167a65a..91e61ee6e0ab3 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxTcpStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxTcpStatistics.cs @@ -4,7 +4,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxTcpStatistics : TcpStatistics + internal sealed class LinuxTcpStatistics : TcpStatistics { private readonly TcpGlobalStatisticsTable _table; private readonly int _currentConnections; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxUdpStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxUdpStatistics.cs index 2f0fbef2a01e4..648e383005848 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxUdpStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/LinuxUdpStatistics.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { - internal class LinuxUdpStatistics : UdpStatistics + internal sealed class LinuxUdpStatistics : UdpStatistics { private readonly UdpGlobalStatisticsTable _table; private readonly int _udpListeners; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleGatewayIPAddressInformation.Unix.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleGatewayIPAddressInformation.Unix.cs index ae304a348120a..71bdb60b72336 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleGatewayIPAddressInformation.Unix.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleGatewayIPAddressInformation.Unix.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class SimpleGatewayIPAddressInformation : GatewayIPAddressInformation + internal sealed class SimpleGatewayIPAddressInformation : GatewayIPAddressInformation { private readonly IPAddress _address; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleTcpConnectionInformation.Unix.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleTcpConnectionInformation.Unix.cs index 90910da01882c..5ed3fe7837e7c 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleTcpConnectionInformation.Unix.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SimpleTcpConnectionInformation.Unix.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class SimpleTcpConnectionInformation : TcpConnectionInformation + internal sealed class SimpleTcpConnectionInformation : TcpConnectionInformation { private IPEndPoint _localEndPoint; private IPEndPoint _remoteEndPoint; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs index f48a231517ef2..d53d00eb5887e 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Connections.cs @@ -332,7 +332,7 @@ private static IPAddress ParseIPv6HexString(string hexAddress, bool isNetworkOrd { Debug.Assert(hexAddress.Length == 32); byte[] addressBytes = new byte[16]; - if (isNetworkOrder) + if (isNetworkOrder || !BitConverter.IsLittleEndian) { for (int i = 0; i < 16; i++) { diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemGatewayIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemGatewayIPAddressInformation.cs index 3bcde94263309..721ff1856f0b5 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemGatewayIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemGatewayIPAddressInformation.cs @@ -4,7 +4,7 @@ namespace System.Net.NetworkInformation { /// Provides information about a network interface address. - internal class SystemGatewayIPAddressInformation : GatewayIPAddressInformation + internal sealed class SystemGatewayIPAddressInformation : GatewayIPAddressInformation { private readonly IPAddress _address; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPAddressInformation.cs index 603e081e5bfb5..3fe65234d529e 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPAddressInformation.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { // This is the main addressinformation class that contains the ipaddress // and other properties. - internal class SystemIPAddressInformation : IPAddressInformation + internal sealed class SystemIPAddressInformation : IPAddressInformation { private readonly IPAddress _address; internal readonly bool Transient; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalStatistics.cs index 329d577503870..e8838b31d0c24 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPGlobalStatistics.cs @@ -6,7 +6,7 @@ namespace System.Net.NetworkInformation { /// IP statistics. - internal class SystemIPGlobalStatistics : IPGlobalStatistics + internal sealed class SystemIPGlobalStatistics : IPGlobalStatistics { private readonly Interop.IpHlpApi.MibIpStats _stats; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceProperties.cs index f3846aafc5e74..6d261115b1eec 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceProperties.cs @@ -9,7 +9,7 @@ namespace System.Net.NetworkInformation // Note: Provides information specific to a network interface. A network interface can have more // than one IPAddress associated with it. We call the native GetAdaptersAddresses API to // pre-populate all of the interface instances and most of their associated information. - internal class SystemIPInterfaceProperties : IPInterfaceProperties + internal sealed class SystemIPInterfaceProperties : IPInterfaceProperties { // These are valid for all interfaces. private readonly bool _dnsEnabled; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceStatistics.cs index 2b20bf7a945ae..a572c04c758e9 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPInterfaceStatistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class SystemIPInterfaceStatistics : IPInterfaceStatistics + internal sealed class SystemIPInterfaceStatistics : IPInterfaceStatistics { private readonly Interop.IpHlpApi.MibIfRow2 _ifRow; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceProperties.cs index 09386c2b1b58a..72accfe9f4514 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceProperties.cs @@ -7,7 +7,7 @@ namespace System.Net.NetworkInformation { - internal class SystemIPv4InterfaceProperties : IPv4InterfaceProperties + internal sealed class SystemIPv4InterfaceProperties : IPv4InterfaceProperties { // These are only valid for ipv4 interfaces. private readonly bool _haveWins; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceStatistics.cs index eddc2aeb34991..82466c914940f 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv4InterfaceStatistics.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class SystemIPv4InterfaceStatistics : IPv4InterfaceStatistics + internal sealed class SystemIPv4InterfaceStatistics : IPv4InterfaceStatistics { private readonly Interop.IpHlpApi.MibIfRow2 _ifRow; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv6InterfaceProperties.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv6InterfaceProperties.cs index 65e8569fd8fc9..eedf426fd1f4b 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv6InterfaceProperties.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIPv6InterfaceProperties.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class SystemIPv6InterfaceProperties : IPv6InterfaceProperties + internal sealed class SystemIPv6InterfaceProperties : IPv6InterfaceProperties { private readonly uint _index; private readonly uint _mtu; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV4Statistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV4Statistics.cs index 1ac442f1e777c..a2294d8ad5ef6 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV4Statistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV4Statistics.cs @@ -4,7 +4,7 @@ namespace System.Net.NetworkInformation { // ICMP statistics for IPv4. - internal class SystemIcmpV4Statistics : IcmpV4Statistics + internal sealed class SystemIcmpV4Statistics : IcmpV4Statistics { private readonly Interop.IpHlpApi.MibIcmpInfo _stats; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV6Statistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV6Statistics.cs index 4fc0a2f2e520c..778a925afc5a5 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV6Statistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemIcmpV6Statistics.cs @@ -24,7 +24,7 @@ internal enum IcmpV6StatType } // ICMP statistics for Ipv6. - internal class SystemIcmpV6Statistics : IcmpV6Statistics + internal sealed class SystemIcmpV6Statistics : IcmpV6Statistics { private readonly Interop.IpHlpApi.MibIcmpInfoEx _stats; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemMulticastIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemMulticastIPAddressInformation.cs index 034720807ee04..71ed8e9b66e9e 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemMulticastIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemMulticastIPAddressInformation.cs @@ -4,7 +4,7 @@ namespace System.Net.NetworkInformation { // Specifies the Multicast addresses for an interface. - internal class SystemMulticastIPAddressInformation : MulticastIPAddressInformation + internal sealed class SystemMulticastIPAddressInformation : MulticastIPAddressInformation { private readonly SystemIPAddressInformation _innerInfo; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs index 5941219ea6faa..f2196e3b79a4d 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemNetworkInterface.cs @@ -9,7 +9,7 @@ namespace System.Net.NetworkInformation { - internal class SystemNetworkInterface : NetworkInterface + internal sealed class SystemNetworkInterface : NetworkInterface { private readonly string _name; private readonly string _id; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs index 069e61f4fe83d..0237fcb3cb1c7 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpConnection.cs @@ -4,7 +4,7 @@ namespace System.Net.NetworkInformation { // Represents an active TCP connection. - internal class SystemTcpConnectionInformation : TcpConnectionInformation + internal sealed class SystemTcpConnectionInformation : TcpConnectionInformation { private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpStatistics.cs index 617e58f8b5b58..32453ce74c6a3 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemTcpStatistics.cs @@ -6,7 +6,7 @@ namespace System.Net.NetworkInformation { // TCP specific statistics. - internal class SystemTcpStatistics : TcpStatistics + internal sealed class SystemTcpStatistics : TcpStatistics { private readonly Interop.IpHlpApi.MibTcpStats _stats; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUdpStatistics.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUdpStatistics.cs index 7d3978f66473a..20869e1f8c684 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUdpStatistics.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUdpStatistics.cs @@ -6,7 +6,7 @@ namespace System.Net.NetworkInformation { // UDP statistics. - internal class SystemUdpStatistics : UdpStatistics + internal sealed class SystemUdpStatistics : UdpStatistics { private readonly Interop.IpHlpApi.MibUdpStats _stats; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUnicastIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUnicastIPAddressInformation.cs index f1274549c2d7e..e9ed56ff453fe 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUnicastIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/SystemUnicastIPAddressInformation.cs @@ -8,7 +8,7 @@ namespace System.Net.NetworkInformation { // Specifies the unicast addresses for an interface. - internal class SystemUnicastIPAddressInformation : UnicastIPAddressInformation + internal sealed class SystemUnicastIPAddressInformation : UnicastIPAddressInformation { private readonly long _dhcpLeaseLifetime; private readonly SystemIPAddressInformation _innerInfo; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/TeredoHelper.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/TeredoHelper.cs index 349c5e80e900d..d75c0a3a5b4a2 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/TeredoHelper.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/TeredoHelper.cs @@ -24,7 +24,7 @@ namespace System.Net.NetworkInformation // and that no new calls to the callback will be issued. // - internal class TeredoHelper + internal sealed class TeredoHelper { private readonly Action _callback; private readonly object _state; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixMulticastIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixMulticastIPAddressInformation.cs index 9caea94a81643..1a21e4e1efdac 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixMulticastIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixMulticastIPAddressInformation.cs @@ -3,7 +3,7 @@ namespace System.Net.NetworkInformation { - internal class UnixMulticastIPAddressInformation : MulticastIPAddressInformation + internal sealed class UnixMulticastIPAddressInformation : MulticastIPAddressInformation { private readonly IPAddress _address; diff --git a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixUnicastIPAddressInformation.cs b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixUnicastIPAddressInformation.cs index e45ce2cf771dc..36034cac75eba 100644 --- a/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixUnicastIPAddressInformation.cs +++ b/src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/UnixUnicastIPAddressInformation.cs @@ -5,7 +5,7 @@ namespace System.Net.NetworkInformation { /// Provides information about a network interface's unicast address - internal class UnixUnicastIPAddressInformation : UnicastIPAddressInformation + internal sealed class UnixUnicastIPAddressInformation : UnicastIPAddressInformation { private readonly IPAddress _address; private readonly int _prefixLength; diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AddressParsingTests.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AddressParsingTests.cs index 2aafbde026e0c..637cb202fe436 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AddressParsingTests.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/AddressParsingTests.cs @@ -12,22 +12,44 @@ public class AddressParsingTests : FileCleanupTestBase [Fact] public void HexIPAddressParsing() { - Assert.Equal(IPAddress.Parse("10.105.128.1"), StringParsingHelpers.ParseHexIPAddress("0180690A")); - Assert.Equal(IPAddress.Parse("103.69.35.1"), StringParsingHelpers.ParseHexIPAddress("01234567")); - Assert.Equal(IPAddress.Parse("152.186.220.254"), StringParsingHelpers.ParseHexIPAddress("FEDCBA98")); + if (BitConverter.IsLittleEndian) + { + Assert.Equal(IPAddress.Parse("10.105.128.1"), StringParsingHelpers.ParseHexIPAddress("0180690A")); + Assert.Equal(IPAddress.Parse("103.69.35.1"), StringParsingHelpers.ParseHexIPAddress("01234567")); + Assert.Equal(IPAddress.Parse("152.186.220.254"), StringParsingHelpers.ParseHexIPAddress("FEDCBA98")); - Assert.Equal(IPAddress.Parse("::"), StringParsingHelpers.ParseHexIPAddress("00000000000000000000000000000000")); - Assert.Equal(IPAddress.Parse("::1"), StringParsingHelpers.ParseHexIPAddress("00000000000000000000000001000000")); - Assert.Equal(IPAddress.Parse("fec0::1"), StringParsingHelpers.ParseHexIPAddress("0000C0FE000000000000000001000000")); - Assert.Equal(IPAddress.Parse("fe80::222:222"), StringParsingHelpers.ParseHexIPAddress("000080FE000000000000000022022202")); - Assert.Equal(IPAddress.Parse("fe80::215:5dff:fe00:402"), StringParsingHelpers.ParseHexIPAddress("000080FE00000000FF5D1502020400FE")); + Assert.Equal(IPAddress.Parse("::"), StringParsingHelpers.ParseHexIPAddress("00000000000000000000000000000000")); + Assert.Equal(IPAddress.Parse("::1"), StringParsingHelpers.ParseHexIPAddress("00000000000000000000000001000000")); + Assert.Equal(IPAddress.Parse("fec0::1"), StringParsingHelpers.ParseHexIPAddress("0000C0FE000000000000000001000000")); + Assert.Equal(IPAddress.Parse("fe80::222:222"), StringParsingHelpers.ParseHexIPAddress("000080FE000000000000000022022202")); + Assert.Equal(IPAddress.Parse("fe80::215:5dff:fe00:402"), StringParsingHelpers.ParseHexIPAddress("000080FE00000000FF5D1502020400FE")); + } + else + { + Assert.Equal(IPAddress.Parse("10.105.128.1"), StringParsingHelpers.ParseHexIPAddress("0A698001")); + Assert.Equal(IPAddress.Parse("103.69.35.1"), StringParsingHelpers.ParseHexIPAddress("67452301")); + Assert.Equal(IPAddress.Parse("152.186.220.254"), StringParsingHelpers.ParseHexIPAddress("98BADCFE")); + + Assert.Equal(IPAddress.Parse("::"), StringParsingHelpers.ParseHexIPAddress("00000000000000000000000000000000")); + Assert.Equal(IPAddress.Parse("::1"), StringParsingHelpers.ParseHexIPAddress("00000000000000000000000000000001")); + Assert.Equal(IPAddress.Parse("fec0::1"), StringParsingHelpers.ParseHexIPAddress("FEC00000000000000000000000000001")); + Assert.Equal(IPAddress.Parse("fe80::222:222"), StringParsingHelpers.ParseHexIPAddress("FE800000000000000000000002220222")); + Assert.Equal(IPAddress.Parse("fe80::215:5dff:fe00:402"), StringParsingHelpers.ParseHexIPAddress("FE8000000000000002155DFFFE000402")); + } } [Fact] public void IPv4GatewayAddressParsing() { string fileName = GetTestFilePath(); - FileUtil.NormalizeLineEndings("NetworkFiles/route", fileName); + if (BitConverter.IsLittleEndian) + { + FileUtil.NormalizeLineEndings("NetworkFiles/route", fileName); + } + else + { + FileUtil.NormalizeLineEndings("NetworkFiles/route-be", fileName); + } List gatewayAddresses = new List(); StringParsingHelpers.ParseIPv4GatewayAddressesFromRouteFile(gatewayAddresses, File.ReadAllLines(fileName), "wlan0"); Assert.Equal(3, gatewayAddresses.Count); diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/ConnectionsParsingTests.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/ConnectionsParsingTests.cs index 6a5764c98d91b..9f50bf682a9bc 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/ConnectionsParsingTests.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/ConnectionsParsingTests.cs @@ -34,16 +34,48 @@ public void ActiveTcpConnectionsParsing() { string tcpFile = GetTestFilePath(); string tcp6File = GetTestFilePath(); - FileUtil.NormalizeLineEndings("NetworkFiles/tcp", tcpFile); - FileUtil.NormalizeLineEndings("NetworkFiles/tcp6", tcp6File); + if (BitConverter.IsLittleEndian) + { + FileUtil.NormalizeLineEndings("NetworkFiles/tcp", tcpFile); + FileUtil.NormalizeLineEndings("NetworkFiles/tcp6", tcp6File); + } + else + { + FileUtil.NormalizeLineEndings("NetworkFiles/tcp-be", tcpFile); + FileUtil.NormalizeLineEndings("NetworkFiles/tcp6-be", tcp6File); + } TcpConnectionInformation[] infos = StringParsingHelpers.ParseActiveTcpConnectionsFromFiles(tcpFile, tcp6File); Assert.Equal(11, infos.Length); - ValidateInfo(infos[0], new IPEndPoint(0xFFFFFF01L, 0x01BD), new IPEndPoint(0L, 0), TcpState.Established); - ValidateInfo(infos[1], new IPEndPoint(0x12345678L, 0x008B), new IPEndPoint(0L, 0), TcpState.SynSent); - ValidateInfo(infos[2], new IPEndPoint(0x0101007FL, 0x0035), new IPEndPoint(0L, 0), TcpState.SynReceived); - ValidateInfo(infos[3], new IPEndPoint(0x0100007FL, 0x0277), new IPEndPoint(0L, 0), TcpState.FinWait1); - ValidateInfo(infos[4], new IPEndPoint(0x0100007FL, 0x0277), new IPEndPoint(0x00000001L, 0), TcpState.SynReceived); + + ValidateInfo( + infos[0], + new IPEndPoint(IPAddress.Parse("1.255.255.255"), 0x01BD), + new IPEndPoint(0L, 0), + TcpState.Established); + + ValidateInfo( + infos[1], + new IPEndPoint(IPAddress.Parse("120.86.52.18"), 0x008B), + new IPEndPoint(0L, 0), + TcpState.SynSent); + + ValidateInfo( + infos[2], + new IPEndPoint(IPAddress.Parse("127.0.1.1"), 0x0035), + new IPEndPoint(0L, 0), + TcpState.SynReceived); + + ValidateInfo( + infos[3], + new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0x0277), + new IPEndPoint(0L, 0), + TcpState.FinWait1); + + ValidateInfo(infos[4], + new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0x0277), + new IPEndPoint(IPAddress.Parse("1.0.0.0"), 0), + TcpState.SynReceived); ValidateInfo( infos[5], @@ -87,8 +119,16 @@ public void TcpListenersParsing() { string tcpFile = GetTestFilePath(); string tcp6File = GetTestFilePath(); - FileUtil.NormalizeLineEndings("NetworkFiles/tcp", tcpFile); - FileUtil.NormalizeLineEndings("NetworkFiles/tcp6", tcp6File); + if (BitConverter.IsLittleEndian) + { + FileUtil.NormalizeLineEndings("NetworkFiles/tcp", tcpFile); + FileUtil.NormalizeLineEndings("NetworkFiles/tcp6", tcp6File); + } + else + { + FileUtil.NormalizeLineEndings("NetworkFiles/tcp-be", tcpFile); + FileUtil.NormalizeLineEndings("NetworkFiles/tcp6-be", tcp6File); + } IPEndPoint[] listeners = StringParsingHelpers.ParseActiveTcpListenersFromFiles(tcpFile, tcp6File); // There is only one socket in Listening state @@ -102,24 +142,32 @@ public void UdpListenersParsing() { string udpFile = GetTestFilePath(); string udp6File = GetTestFilePath(); - FileUtil.NormalizeLineEndings("NetworkFiles/udp", udpFile); - FileUtil.NormalizeLineEndings("NetworkFiles/udp6", udp6File); + if (BitConverter.IsLittleEndian) + { + FileUtil.NormalizeLineEndings("NetworkFiles/udp", udpFile); + FileUtil.NormalizeLineEndings("NetworkFiles/udp6", udp6File); + } + else + { + FileUtil.NormalizeLineEndings("NetworkFiles/udp-be", udpFile); + FileUtil.NormalizeLineEndings("NetworkFiles/udp6-be", udp6File); + } IPEndPoint[] listeners = StringParsingHelpers.ParseActiveUdpListenersFromFiles(udpFile, udp6File); Assert.Equal(17, listeners.Length); - Assert.Equal(listeners[0], new IPEndPoint(0x00000000, 0x8E15)); - Assert.Equal(listeners[1], new IPEndPoint(0x00000000, 0x14E9)); - Assert.Equal(listeners[2], new IPEndPoint(0x00000000, 0xB50F)); - Assert.Equal(listeners[3], new IPEndPoint(0x0101007F, 0x0035)); - Assert.Equal(listeners[4], new IPEndPoint(0x00000000, 0x0044)); - Assert.Equal(listeners[5], new IPEndPoint(0xFF83690A, 0x0089)); - Assert.Equal(listeners[6], new IPEndPoint(0x3B80690A, 0x0089)); - Assert.Equal(listeners[7], new IPEndPoint(0x00000000, 0x0089)); - Assert.Equal(listeners[8], new IPEndPoint(0xFF83690A, 0x008A)); - Assert.Equal(listeners[9], new IPEndPoint(0x3B80690A, 0x008A)); - Assert.Equal(listeners[10], new IPEndPoint(0x00000000, 0x008A)); - Assert.Equal(listeners[11], new IPEndPoint(0x00000000, 0x0277)); + Assert.Equal(listeners[0], new IPEndPoint(0, 0x8E15)); + Assert.Equal(listeners[1], new IPEndPoint(0, 0x14E9)); + Assert.Equal(listeners[2], new IPEndPoint(0, 0xB50F)); + Assert.Equal(listeners[3], new IPEndPoint(IPAddress.Parse("127.0.1.1"), 0x0035)); + Assert.Equal(listeners[4], new IPEndPoint(0, 0x0044)); + Assert.Equal(listeners[5], new IPEndPoint(IPAddress.Parse("10.105.131.255"), 0x0089)); + Assert.Equal(listeners[6], new IPEndPoint(IPAddress.Parse("10.105.128.59"), 0x0089)); + Assert.Equal(listeners[7], new IPEndPoint(0, 0x0089)); + Assert.Equal(listeners[8], new IPEndPoint(IPAddress.Parse("10.105.131.255"), 0x008A)); + Assert.Equal(listeners[9], new IPEndPoint(IPAddress.Parse("10.105.128.59"), 0x008A)); + Assert.Equal(listeners[10], new IPEndPoint(0, 0x008A)); + Assert.Equal(listeners[11], new IPEndPoint(0, 0x0277)); Assert.Equal(listeners[12], new IPEndPoint(IPAddress.Parse("::"), 0x14E9)); Assert.Equal(listeners[13], new IPEndPoint(IPAddress.Parse("::"), 0x96D3)); diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/route-be b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/route-be new file mode 100644 index 0000000000000..aa912f68ad91d --- /dev/null +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/route-be @@ -0,0 +1,5 @@ +Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT +wlan0 00000000 0A698001 0003 0 0 400 00000000 0 0 0 +wlan0 0A698000 67452301 0001 0 0 400 FFFFFC00 0 0 0 +wlan0 A9FE0000 98BADCFE 0001 0 0 1000 FFFF0000 0 0 0 +eth0 00000000 11111111 0001 0 0 1000 FFFF0000 0 0 0 diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/tcp-be b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/tcp-be new file mode 100644 index 0000000000000..e7bbf100e4773 --- /dev/null +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/tcp-be @@ -0,0 +1,6 @@ + sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 01FFFFFF:01BD 00000000:0000 01 00000000:00000000 00:00000000 00000000 0 0 23053 1 0000000000000000 100 0 0 10 0 + 1: 78563412:008B 00000000:0000 02 00000000:00000000 00:00000000 00000000 0 0 23054 1 0000000000000000 100 0 0 10 0 + 2: 7F000101:0035 00000000:0000 03 00000000:00000000 00:00000000 00000000 0 0 17216 1 0000000000000000 100 0 0 10 0 + 3: 7F000001:0277 00000000:0000 04 00000000:00000000 00:00000000 00000000 0 0 16089 1 0000000000000000 100 0 0 10 0 + 4: 7F000001:0277 01000000:0000 0C 00000000:00000000 00:00000000 00000000 0 0 16089 1 0000000000000000 100 0 0 10 0 diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/tcp6-be b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/tcp6-be new file mode 100644 index 0000000000000..6fdda567697f9 --- /dev/null +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/tcp6-be @@ -0,0 +1,9 @@ + sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 00000000000000000000000000000000:01BD 00000000000000000000000000000000:0000 05 00000000:00000000 00:00000000 00000000 0 0 23051 1 0000000000000000 100 0 0 10 0 + 1: 00000000000000000000000000000000:008B 00000000000000000000000000000000:0000 06 00000000:00000000 00:00000000 00000000 0 0 23052 1 0000000000000000 100 0 0 10 0 + 2: 00000000000000000000000000000001:0277 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 16088 1 0000000000000000 100 0 0 10 0 + 3: 00000000000000000000000000000001:A696 00000000000000000000000000000001:0277 08 00000000:00000001 00:00000000 00000000 0 0 17035 1 0000000000000000 20 4 24 10 -1 + 4: 00000000000000000000000000000001:A69B 00000000000000000000000000000001:0277 09 00000000:00000001 00:00000000 00000000 1000 0 23248 1 0000000000000000 20 4 16 10 -1 + 5: 00000000000000000000000000000001:A697 00000000000000000000000000000001:0277 0A 00000000:00000001 00:00000000 00000000 0 0 12176 1 0000000000000000 20 4 24 10 -1 + 6: FEC0000000000000AA64000000000001:007B 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 109 0 59682 2 0000000000000000 100 0 0 10 0 + 7: 00000000000000000000000000000001:007C FE8000000000000002155DFFFE000402:007B 01 00000000:00000000 00:00000000 00000000 0 0 24746 2 0000000000000000 100 0 0 10 0 diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/udp-be b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/udp-be new file mode 100644 index 0000000000000..6a981a45a2766 --- /dev/null +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/udp-be @@ -0,0 +1,13 @@ + sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops + 572: 00000000:8E15 00000000:0000 07 00000000:00000000 00:00000000 00000000 107 0 11207 2 0000000000000000 0 + 2320: 00000000:14E9 00000000:0000 07 00000000:00000000 00:00000000 00000000 107 0 11205 2 0000000000000000 0 + 2358: 00000000:B50F 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 21943 2 0000000000000000 0 + 5212: 7F000101:0035 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 17215 2 0000000000000000 0 + 5227: 00000000:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 17211 2 0000000000000000 0 + 5296: 0A6983FF:0089 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 20035 2 0000000000000000 0 + 5296: 0A69803B:0089 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 20034 2 0000000000000000 0 + 5296: 00000000:0089 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 20031 2 0000000000000000 0 + 5297: 0A6983FF:008A 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 20037 2 0000000000000000 0 + 5297: 0A69803B:008A 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 20036 2 0000000000000000 0 + 5297: 00000000:008A 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 20032 2 0000000000000000 0 + 5790: 00000000:0277 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 16165 2 0000000000000000 0 diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/udp6-be b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/udp6-be new file mode 100644 index 0000000000000..046ed3bc3a192 --- /dev/null +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkFiles/udp6-be @@ -0,0 +1,6 @@ + sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops + 2320: 00000000000000000000000000000000:14E9 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 107 0 11206 2 0000000000000000 0 + 2810: 00000000000000000000000000000000:96D3 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 21944 2 0000000000000000 0 + 8063: 00000000000000000000000000000000:8B58 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 107 0 11208 2 0000000000000000 0 + 9960: FEC0000000000000AA64000000000001:007B 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 109 0 59682 2 0000000000000000 0 + 9961: FE8000000000000002155DFFFE000402:007B 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 24746 2 0000000000000000 0 diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs index 2c2092f176557..f4452e06d7c79 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/NetworkInterfaceBasicTest.cs @@ -108,7 +108,7 @@ public void BasicTest_AccessInstanceProperties_NoExceptions_Bsd() string id = nic.Id; Assert.False(string.IsNullOrEmpty(id), "NetworkInterface.Id should not be null or empty."); _log.WriteLine("ID: " + id); - Assert.Throws(() => nic.IsReceiveOnly); + Assert.False(nic.IsReceiveOnly); _log.WriteLine("Type: " + nic.NetworkInterfaceType); _log.WriteLine("Status: " + nic.OperationalStatus); _log.WriteLine("Speed: " + nic.Speed); diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs index 4f87263a9b248..74abb2a4adf25 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs @@ -20,8 +20,6 @@ public partial class Ping private const int MaxIpHeaderLengthInBytes = 60; private static bool SendIpHeader => OperatingSystem.IsMacOS(); private static bool NeedsConnect => OperatingSystem.IsLinux(); - [ThreadStatic] - private static Random? t_idGenerator; private PingReply SendPingCore(IPAddress address, byte[] buffer, int timeout, PingOptions? options) { @@ -51,8 +49,7 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in { // Use a random value as the identifier. This doesn't need to be perfectly random // or very unpredictable, rather just good enough to avoid unexpected conflicts. - Random rand = t_idGenerator ??= new Random(); - ushort id = (ushort)rand.Next(ushort.MaxValue + 1); + ushort id = (ushort)Random.Shared.Next(ushort.MaxValue + 1); IpHeader iph = default; bool ipv4 = address.AddressFamily == AddressFamily.InterNetwork; @@ -419,7 +416,7 @@ internal struct IcmpHeader // Since this is private should be safe to trust that the calling code // will behave. To get a little performance boost raw fields are exposed // and no validation is performed. - private class SocketConfig + private sealed class SocketConfig { public SocketConfig(EndPoint endPoint, int timeout, PingOptions? options, bool isIPv4, ProtocolType protocolType, ushort id, byte[] sendBuffer) { diff --git a/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs b/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs index 66be8c8195bfd..d48c98e0e1632 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs @@ -17,12 +17,12 @@ namespace System.Net /// public class IPAddress { - public static readonly IPAddress Any = new ReadOnlyIPAddress(0x0000000000000000); - public static readonly IPAddress Loopback = new ReadOnlyIPAddress(0x000000000100007F); - public static readonly IPAddress Broadcast = new ReadOnlyIPAddress(0x00000000FFFFFFFF); + public static readonly IPAddress Any = new ReadOnlyIPAddress(new byte[] { 0, 0, 0, 0 }); + public static readonly IPAddress Loopback = new ReadOnlyIPAddress(new byte[] { 127, 0, 0, 1 }); + public static readonly IPAddress Broadcast = new ReadOnlyIPAddress(new byte[] { 255, 255, 255, 255 }); public static readonly IPAddress None = Broadcast; - internal const long LoopbackMask = 0x00000000000000FF; + internal const uint LoopbackMaskHostOrder = 0xFF000000; public static readonly IPAddress IPv6Any = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0); public static readonly IPAddress IPv6Loopback = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 0); @@ -184,7 +184,7 @@ public IPAddress(ReadOnlySpan address) { if (address.Length == IPAddressParserStatics.IPv4AddressBytes) { - PrivateAddress = BinaryPrimitives.ReadUInt32LittleEndian(address); + PrivateAddress = MemoryMarshal.Read(address); } else if (address.Length == IPAddressParserStatics.IPv6AddressBytes) { @@ -290,10 +290,7 @@ private void WriteIPv6Bytes(Span destination) private void WriteIPv4Bytes(Span destination) { uint address = PrivateAddress; - destination[0] = (byte)(address); - destination[1] = (byte)(address >> 8); - destination[2] = (byte)(address >> 16); - destination[3] = (byte)(address >> 24); + MemoryMarshal.Write(destination, ref address); } /// @@ -431,6 +428,7 @@ public static bool IsLoopback(IPAddress address) } else { + long LoopbackMask = (uint)HostToNetworkOrder(unchecked((int)LoopbackMaskHostOrder)); return ((address.PrivateAddress & LoopbackMask) == (Loopback.PrivateAddress & LoopbackMask)); } } @@ -619,11 +617,11 @@ public IPAddress MapToIPv6() return this; } - uint address = PrivateAddress; + uint address = (uint)NetworkToHostOrder(unchecked((int)PrivateAddress)); ushort[] labels = new ushort[NumberOfLabels]; labels[5] = 0xFFFF; - labels[6] = (ushort)(((address & 0x0000FF00) >> 8) | ((address & 0x000000FF) << 8)); - labels[7] = (ushort)(((address & 0xFF000000) >> 24) | ((address & 0x00FF0000) >> 8)); + labels[6] = (ushort)(address >> 16); + labels[7] = (ushort)address; return new IPAddress(labels, 0); } @@ -637,13 +635,8 @@ public IPAddress MapToIPv4() return this; } - // Cast the ushort values to a uint and mask with unsigned literal before bit shifting. - // Otherwise, we can end up getting a negative value for any IPv4 address that ends with - // a byte higher than 127 due to sign extension of the most significant 1 bit. - long address = ((((uint)_numbers![6] & 0x0000FF00u) >> 8) | (((uint)_numbers[6] & 0x000000FFu) << 8)) | - (((((uint)_numbers[7] & 0x0000FF00u) >> 8) | (((uint)_numbers[7] & 0x000000FFu) << 8)) << 16); - - return new IPAddress(address); + uint address = (uint)_numbers![6] << 16 | (uint)_numbers[7]; + return new IPAddress((uint)HostToNetworkOrder(unchecked((int)address))); } [DoesNotReturn] @@ -651,7 +644,7 @@ public IPAddress MapToIPv4() private sealed class ReadOnlyIPAddress : IPAddress { - public ReadOnlyIPAddress(long newAddress) : base(newAddress) + public ReadOnlyIPAddress(byte[] newAddress) : base(newAddress) { } } } diff --git a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs index f1c23cac89647..408e17e9716f8 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/IPAddressParser.cs @@ -13,7 +13,7 @@ namespace System.Net { - internal class IPAddressParser + internal static class IPAddressParser { private const int MaxIPv4StringLength = 15; // 4 numbers separated by 3 periods, with up to 3 digits per number @@ -76,14 +76,15 @@ internal static unsafe bool IPv4AddressToString(uint address, Span formatt private static unsafe int IPv4AddressToStringHelper(uint address, char* addressString) { int offset = 0; + address = (uint)IPAddress.NetworkToHostOrder(unchecked((int)address)); - FormatIPv4AddressNumber((int)(address & 0xFF), addressString, ref offset); - addressString[offset++] = '.'; - FormatIPv4AddressNumber((int)((address >> 8) & 0xFF), addressString, ref offset); + FormatIPv4AddressNumber((int)((address >> 24) & 0xFF), addressString, ref offset); addressString[offset++] = '.'; FormatIPv4AddressNumber((int)((address >> 16) & 0xFF), addressString, ref offset); addressString[offset++] = '.'; - FormatIPv4AddressNumber((int)((address >> 24) & 0xFF), addressString, ref offset); + FormatIPv4AddressNumber((int)((address >> 8) & 0xFF), addressString, ref offset); + addressString[offset++] = '.'; + FormatIPv4AddressNumber((int)(address & 0xFF), addressString, ref offset); return offset; } @@ -179,9 +180,9 @@ public static unsafe bool Ipv4StringToAddress(ReadOnlySpan ipSpan, out lon if (tmpAddr != IPv4AddressHelper.Invalid && end == ipSpan.Length) { - // IPv4AddressHelper.ParseNonCanonical returns the bytes in the inverse order. - // Reverse them and return success. - address = BinaryPrimitives.ReverseEndianness((uint)tmpAddr); + // IPv4AddressHelper.ParseNonCanonical returns the bytes in host order. + // Convert to network order and return success. + address = (uint)IPAddress.HostToNetworkOrder(unchecked((int)tmpAddr)); return true; } else @@ -291,9 +292,10 @@ private static void AppendHex(ushort value, StringBuilder buffer) } /// Extracts the IPv4 address from the end of the IPv6 address byte array. - private static uint ExtractIPv4Address(ushort[] address) => (uint)(Reverse(address[7]) << 16) | Reverse(address[6]); - - /// Reverses the two bytes in the ushort. - private static ushort Reverse(ushort number) => (ushort)(((number >> 8) & 0xFF) | ((number << 8) & 0xFF00)); + private static uint ExtractIPv4Address(ushort[] address) + { + uint ipv4address = (uint)address[6] << 16 | (uint)address[7]; + return (uint)IPAddress.HostToNetworkOrder(unchecked((int)ipv4address)); + } } } diff --git a/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPAddressTest.cs b/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPAddressTest.cs index a72fed2d81867..584a219962138 100644 --- a/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPAddressTest.cs +++ b/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPAddressTest.cs @@ -48,13 +48,13 @@ private static IPAddress IPV6Address2() [Theory] [InlineData(MinAddress, new byte[] { 0, 0, 0, 0 })] [InlineData(MaxAddress, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF })] - [InlineData(0x2414188f, new byte[] { 0x8f, 0x18, 0x14, 0x24 })] - [InlineData(0xFF, new byte[] { 0xFF, 0, 0, 0 })] - [InlineData(0xFF00FF, new byte[] { 0xFF, 0, 0xFF, 0 })] - [InlineData(0xFF00FF00, new byte[] { 0, 0xFF, 0, 0xFF })] + [InlineData(0x8f181424, new byte[] { 0x8f, 0x18, 0x14, 0x24 })] + [InlineData(0xFF000000, new byte[] { 0xFF, 0, 0, 0 })] + [InlineData(0xFF00FF00, new byte[] { 0xFF, 0, 0xFF, 0 })] + [InlineData(0x00FF00FF, new byte[] { 0, 0xFF, 0, 0xFF })] public static void Ctor_Long_Success(long address, byte[] expectedBytes) { - IPAddress ip = new IPAddress(address); + IPAddress ip = new IPAddress((uint)IPAddress.HostToNetworkOrder(unchecked((int)address))); Assert.Equal(expectedBytes, ip.GetAddressBytes()); Assert.Equal(AddressFamily.InterNetwork, ip.AddressFamily); } @@ -163,17 +163,34 @@ public static void HostToNetworkOrder_Compare_Equal() short s1 = (short)0x1350; short s2 = (short)0x5013; - Assert.Equal(l2, IPAddress.HostToNetworkOrder(l1)); - Assert.Equal(l4, IPAddress.HostToNetworkOrder(l3)); - Assert.Equal(i2, IPAddress.HostToNetworkOrder(i1)); - Assert.Equal(i4, IPAddress.HostToNetworkOrder(i3)); - Assert.Equal(s2, IPAddress.HostToNetworkOrder(s1)); - - Assert.Equal(l1, IPAddress.NetworkToHostOrder(l2)); - Assert.Equal(l3, IPAddress.NetworkToHostOrder(l4)); - Assert.Equal(i1, IPAddress.NetworkToHostOrder(i2)); - Assert.Equal(i3, IPAddress.NetworkToHostOrder(i4)); - Assert.Equal(s1, IPAddress.NetworkToHostOrder(s2)); + if (BitConverter.IsLittleEndian) + { + Assert.Equal(l2, IPAddress.HostToNetworkOrder(l1)); + Assert.Equal(l4, IPAddress.HostToNetworkOrder(l3)); + Assert.Equal(i2, IPAddress.HostToNetworkOrder(i1)); + Assert.Equal(i4, IPAddress.HostToNetworkOrder(i3)); + Assert.Equal(s2, IPAddress.HostToNetworkOrder(s1)); + + Assert.Equal(l1, IPAddress.NetworkToHostOrder(l2)); + Assert.Equal(l3, IPAddress.NetworkToHostOrder(l4)); + Assert.Equal(i1, IPAddress.NetworkToHostOrder(i2)); + Assert.Equal(i3, IPAddress.NetworkToHostOrder(i4)); + Assert.Equal(s1, IPAddress.NetworkToHostOrder(s2)); + } + else + { + Assert.Equal(l1, IPAddress.HostToNetworkOrder(l1)); + Assert.Equal(l3, IPAddress.HostToNetworkOrder(l3)); + Assert.Equal(i1, IPAddress.HostToNetworkOrder(i1)); + Assert.Equal(i3, IPAddress.HostToNetworkOrder(i3)); + Assert.Equal(s1, IPAddress.HostToNetworkOrder(s1)); + + Assert.Equal(l2, IPAddress.NetworkToHostOrder(l2)); + Assert.Equal(l4, IPAddress.NetworkToHostOrder(l4)); + Assert.Equal(i2, IPAddress.NetworkToHostOrder(i2)); + Assert.Equal(i4, IPAddress.NetworkToHostOrder(i4)); + Assert.Equal(s2, IPAddress.NetworkToHostOrder(s2)); + } } [Fact] diff --git a/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPEndPointTest.cs b/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPEndPointTest.cs index 8e00d00236ec6..bb9b95d438e99 100644 --- a/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPEndPointTest.cs +++ b/src/libraries/System.Net.Primitives/tests/FunctionalTests/IPEndPointTest.cs @@ -131,7 +131,7 @@ public static void Address_SetNull_ThrowsArgumentNullException() public static IEnumerable ToString_TestData() { - yield return new object[] { new IPEndPoint(2, 500), "2.0.0.0:500" }; + yield return new object[] { new IPEndPoint(IPAddress.HostToNetworkOrder(0x02000000), 500), "2.0.0.0:500" }; yield return new object[] { new IPEndPoint(IPAddress.Parse("192.169.0.9"), 500), "192.169.0.9:500" }; yield return new object[] { new IPEndPoint(IPAddress.Parse("0:0:0:0:0:0:0:1"), 500), "[::1]:500" }; } diff --git a/src/libraries/System.Net.Quic/readme.md b/src/libraries/System.Net.Quic/readme.md new file mode 100644 index 0000000000000..16ac0d31a0490 --- /dev/null +++ b/src/libraries/System.Net.Quic/readme.md @@ -0,0 +1,45 @@ +# MsQuic + +`System.Net.Quic` depends on [MsQuic](https://github.com/microsoft/msquic), Microsoft, cross-platform, native implementation of the [QUIC](https://datatracker.ietf.org/wg/quic/about/) protocol. +Currently, `System.Net.Quic` depends on [**msquic@cc104e836a5d4a5e0d324bc08b42136d2acac997**](https://github.com/microsoft/msquic/commit/cc104e836a5d4a5e0d324bc08b42136d2acac997) revision. + +## Usage + +### Build MsQuic + +[MsQuic build docs](https://github.com/microsoft/msquic/blob/main/docs/BUILD.md) + +> **Note**: At the moment, we're using stub_tls option to bypass OpenSSL/SChannel, since work with certificates is not fully figured out. + +#### Linux +Prerequisites: +- build-essential +- cmake +- lttng-ust +- lttng-tools + +`dotnet tool install --global`: +- microsoft.logging.clog +- microsoft.logging.clog2text.lttng + + +Run inside the msquic directory (for **Debug** build with logging on): +```bash +# build msquic in debug with logging and stub tls +rm -rf build +mkdir build +cmake -B build -DCMAKE_BUILD_TYPE=Debug -DQUIC_ENABLE_LOGGING=on -DQUIC_TLS=stub +cd build +cmake --build . --config Debug + +# copy msquic into runtime +yes | cp -rf bin/Debug/libmsquic.* /src/libraries/System.Net.Quic/src/ +``` + +#### Windows +Prerequisites: +- Latest [Windows Insider Builds](https://insider.windows.com/en-us/), Insiders Fast build. This is required for SChannel support for QUIC. + - To confirm you have a new enough build, run winver on command line and confirm you version is greater than Version 2004 (OS Build 20145.1000). + +TODO + diff --git a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs index 12f167a2a6623..847773e8d501d 100644 --- a/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs +++ b/src/libraries/System.Net.Quic/ref/System.Net.Quic.cs @@ -6,121 +6,123 @@ namespace System.Net.Quic { - public static class QuicImplementationProviders + public partial class QuicClientConnectionOptions : System.Net.Quic.QuicOptions { - public static Implementations.QuicImplementationProvider Mock => throw null; - public static Implementations.QuicImplementationProvider MsQuic => throw null; - public static Implementations.QuicImplementationProvider Default => throw null; + public QuicClientConnectionOptions() { } + public System.Net.Security.SslClientAuthenticationOptions? ClientAuthenticationOptions { get { throw null; } set { } } + public System.Net.IPEndPoint? LocalEndPoint { get { throw null; } set { } } + public System.Net.EndPoint? RemoteEndPoint { get { throw null; } set { } } } - public sealed class QuicListener : IDisposable + public sealed partial class QuicConnection : System.IDisposable { - public QuicListener(IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { throw null; } - public QuicListener(QuicListenerOptions options) { throw null; } - public QuicListener(Implementations.QuicImplementationProvider implementationProvider, IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { throw null; } - public QuicListener(Implementations.QuicImplementationProvider implementationProvider, QuicListenerOptions options) { throw null; } - public IPEndPoint ListenEndPoint => throw null; - public System.Threading.Tasks.ValueTask AcceptConnectionAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - public void Start() => throw null; - public void Close() => throw null; - public void Dispose() => throw null; + public QuicConnection(System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { } + public QuicConnection(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { } + public QuicConnection(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.Quic.QuicClientConnectionOptions options) { } + public QuicConnection(System.Net.Quic.QuicClientConnectionOptions options) { } + public bool Connected { get { throw null; } } + public System.Net.IPEndPoint? LocalEndPoint { get { throw null; } } + public System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol { get { throw null; } } + public System.Net.EndPoint RemoteEndPoint { get { throw null; } } + public System.Threading.Tasks.ValueTask AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask CloseAsync(long errorCode, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask ConnectAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public void Dispose() { } + public long GetRemoteAvailableBidirectionalStreamCount() { throw null; } + public long GetRemoteAvailableUnidirectionalStreamCount() { throw null; } + public System.Net.Quic.QuicStream OpenBidirectionalStream() { throw null; } + public System.Net.Quic.QuicStream OpenUnidirectionalStream() { throw null; } } - public class QuicListenerOptions + public partial class QuicConnectionAbortedException : System.Net.Quic.QuicException { - public System.Net.Security.SslServerAuthenticationOptions? ServerAuthenticationOptions { get => throw null; set => throw null; } - public string? CertificateFilePath { get => throw null; set => throw null; } - public string? PrivateKeyFilePath { get => throw null; set => throw null; } - public IPEndPoint? ListenEndPoint { get => throw null; set => throw null; } - public int ListenBacklog { get => throw null; set => throw null; } - public long MaxBidirectionalStreams { get => throw null; set => throw null; } - public long MaxUnidirectionalStreams { get => throw null; set => throw null; } - public TimeSpan IdleTimeout { get => throw null; set => throw null; } + public QuicConnectionAbortedException(string message, long errorCode) : base (default(string)) { } + public long ErrorCode { get { throw null; } } } - public sealed class QuicConnection : IDisposable + public partial class QuicException : System.Exception { - public QuicConnection(System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { throw null; } - public QuicConnection(QuicClientConnectionOptions options) { throw null; } - public QuicConnection(Implementations.QuicImplementationProvider implementationProvider, System.Net.EndPoint remoteEndPoint, System.Net.Security.SslClientAuthenticationOptions? sslClientAuthenticationOptions, System.Net.IPEndPoint? localEndPoint = null) { throw null; } - public QuicConnection(Implementations.QuicImplementationProvider implementationProvider, QuicClientConnectionOptions options) { throw null; } - public bool Connected => throw null; - public System.Net.IPEndPoint LocalEndPoint => throw null; - public System.Net.EndPoint RemoteEndPoint => throw null; - public System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol => throw null; - public System.Threading.Tasks.ValueTask ConnectAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - public QuicStream OpenUnidirectionalStream() => throw null; - public QuicStream OpenBidirectionalStream() => throw null; - public System.Threading.Tasks.ValueTask AcceptStreamAsync(System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask CloseAsync(long errorCode, System.Threading.CancellationToken cancellationToken = default) => throw null; - public void Dispose() => throw null; - public long GetRemoteAvailableUnidirectionalStreamCount() => throw null; - public long GetRemoteAvailableBidirectionalStreamCount() => throw null; + public QuicException(string? message) { } + public QuicException(string? message, System.Exception? innerException) { } } - public class QuicClientConnectionOptions + public static partial class QuicImplementationProviders { - public System.Net.Security.SslClientAuthenticationOptions? ClientAuthenticationOptions { get => throw null; set => throw null; } - public IPEndPoint? LocalEndPoint { get => throw null; set => throw null; } - public EndPoint? RemoteEndPoint { get => throw null; set => throw null; } - public long MaxBidirectionalStreams { get => throw null; set => throw null; } - public long MaxUnidirectionalStreams { get => throw null; set => throw null; } - public TimeSpan IdleTimeout { get => throw null; set => throw null; } + public static System.Net.Quic.Implementations.QuicImplementationProvider Default { get { throw null; } } + public static System.Net.Quic.Implementations.QuicImplementationProvider Mock { get { throw null; } } + public static System.Net.Quic.Implementations.QuicImplementationProvider MsQuic { get { throw null; } } } - public sealed class QuicStream : System.IO.Stream + public sealed partial class QuicListener : System.IDisposable { - internal QuicStream() { throw null; } - public override bool CanSeek => throw null; - public override long Length => throw null; - public override long Seek(long offset, System.IO.SeekOrigin origin) => throw null; - public override void SetLength(long value) => throw null; - public override long Position { get => throw null; set => throw null; } - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => throw null; - public override int EndRead(IAsyncResult asyncResult) => throw null; - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) => throw null; - public override void EndWrite(IAsyncResult asyncResult) => throw null; - public override int Read(byte[] buffer, int offset, int count) => throw null; - public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null; - public override void Write(byte[] buffer, int offset, int count) => throw null; - public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) => throw null; - public long StreamId => throw null; - public override bool CanRead => throw null; - public override int Read(Span buffer) => throw null; - public override System.Threading.Tasks.ValueTask ReadAsync(Memory buffer, System.Threading.CancellationToken cancellationToken = default) => throw null; - public override bool CanWrite => throw null; - public override void Write(ReadOnlySpan buffer) => throw null; - public override System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default) => throw null; - public override void Flush() => throw null; - public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) => throw null; - public void AbortRead(long errorCode) => throw null; - public void AbortWrite(long errorCode) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory buffer, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory> buffers, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask WriteAsync(ReadOnlyMemory> buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default) => throw null; - public System.Threading.Tasks.ValueTask ShutdownWriteCompleted(System.Threading.CancellationToken cancellationToken = default) => throw null; - public void Shutdown() => throw null; + public QuicListener(System.Net.IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { } + public QuicListener(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.IPEndPoint listenEndPoint, System.Net.Security.SslServerAuthenticationOptions sslServerAuthenticationOptions) { } + public QuicListener(System.Net.Quic.Implementations.QuicImplementationProvider implementationProvider, System.Net.Quic.QuicListenerOptions options) { } + public QuicListener(System.Net.Quic.QuicListenerOptions options) { } + public System.Net.IPEndPoint ListenEndPoint { get { throw null; } } + public System.Threading.Tasks.ValueTask AcceptConnectionAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public void Close() { } + public void Dispose() { } + public void Start() { } } - public class QuicException : Exception + public partial class QuicListenerOptions : System.Net.Quic.QuicOptions { - public QuicException(string? message) { throw null; } - public QuicException(string? message, Exception? innerException) { throw null; } + public QuicListenerOptions() { } + public int ListenBacklog { get { throw null; } set { } } + public System.Net.IPEndPoint? ListenEndPoint { get { throw null; } set { } } + public System.Net.Security.SslServerAuthenticationOptions? ServerAuthenticationOptions { get { throw null; } set { } } } - public class QuicConnectionAbortedException : QuicException + public partial class QuicOperationAbortedException : System.Net.Quic.QuicException { - public QuicConnectionAbortedException(string message, long errorCode) : base(default) { throw null; } - public long ErrorCode { get { throw null; } } + public QuicOperationAbortedException(string message) : base (default(string)) { } + } + public partial class QuicOptions + { + public QuicOptions() { } + public System.TimeSpan IdleTimeout { get { throw null; } set { } } + public long MaxBidirectionalStreams { get { throw null; } set { } } + public long MaxUnidirectionalStreams { get { throw null; } set { } } } - public class QuicOperationAbortedException : QuicException + public sealed partial class QuicStream : System.IO.Stream { - public QuicOperationAbortedException(string message) : base(default) { throw null; } + internal QuicStream() { } + public override bool CanRead { get { throw null; } } + public override bool CanSeek { get { throw null; } } + public override bool CanWrite { get { throw null; } } + public override long Length { get { throw null; } } + public override long Position { get { throw null; } set { } } + public long StreamId { get { throw null; } } + public void AbortRead(long errorCode) { } + public void AbortWrite(long errorCode) { } + public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } + public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } + protected override void Dispose(bool disposing) { } + public override int EndRead(System.IAsyncResult asyncResult) { throw null; } + public override void EndWrite(System.IAsyncResult asyncResult) { } + public override void Flush() { } + public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { throw null; } + public override int Read(byte[] buffer, int offset, int count) { throw null; } + public override int Read(System.Span buffer) { throw null; } + public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } + public override System.Threading.Tasks.ValueTask ReadAsync(System.Memory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override long Seek(long offset, System.IO.SeekOrigin origin) { throw null; } + public override void SetLength(long value) { } + public void Shutdown() { } + public System.Threading.Tasks.ValueTask ShutdownWriteCompleted(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override void Write(byte[] buffer, int offset, int count) { } + public override void Write(System.ReadOnlySpan buffer) { } + public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.Buffers.ReadOnlySequence buffers, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public override System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory> buffers, bool endStream, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.ValueTask WriteAsync(System.ReadOnlyMemory> buffers, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } - public class QuicStreamAbortedException : QuicException + public partial class QuicStreamAbortedException : System.Net.Quic.QuicException { - public QuicStreamAbortedException(string message, long errorCode) : base(default) { throw null; } + public QuicStreamAbortedException(string message, long errorCode) : base (default(string)) { } public long ErrorCode { get { throw null; } } } } namespace System.Net.Quic.Implementations { - public abstract class QuicImplementationProvider + public abstract partial class QuicImplementationProvider { internal QuicImplementationProvider() { } public abstract bool IsSupported { get; } diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index f516d28789e30..6ad41a39fdf03 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -15,7 +15,7 @@ - + @@ -62,11 +62,15 @@ PreserveNewest PreserveNewest + + PreserveNewest + PreserveNewest + PreserveNewest PreserveNewest - + PreserveNewest PreserveNewest diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs index 12f39ab1c0060..6e5d2719452de 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs @@ -91,7 +91,9 @@ internal override bool Connected } // TODO: Should clone the endpoint since it is mutable - internal override IPEndPoint LocalEndPoint => _localEndPoint; + // TODO: could this be made back to non-nullable? + // For inbound we have it immediately, for outbound after connect. + internal override IPEndPoint? LocalEndPoint => _localEndPoint; // TODO: Should clone the endpoint since it is mutable internal override EndPoint RemoteEndPoint => _remoteEndPoint!; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs index d483c76a0ac36..5914cf200c65b 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs @@ -1,137 +1,118 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Net.Security; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; namespace System.Net.Quic.Implementations.MsQuic.Internal { - internal class MsQuicApi : IDisposable + internal unsafe sealed class MsQuicApi { - private bool _disposed; - - private readonly IntPtr _registrationContext; - - private unsafe MsQuicApi() + public SafeMsQuicRegistrationHandle Registration { get; } + + // This is workaround for a bug in ILTrimmer. + // Without these DynamicDependency attributes, .ctor() will be removed from the safe handles. + // Remove once fixed: https://github.com/mono/linker/issues/1660 + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicRegistrationHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConfigurationHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicListenerHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicConnectionHandle))] + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(SafeMsQuicStreamHandle))] + private MsQuicApi(NativeApi* vtable) { - MsQuicNativeMethods.NativeApi* registration; + uint status; - try - { - uint status = Interop.MsQuic.MsQuicOpen(out registration); - if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) - { - throw new NotSupportedException(SR.net_quic_notsupported); - } - } - catch (DllNotFoundException) - { - throw new NotSupportedException(SR.net_quic_notsupported); - } + SetParamDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->SetParam); - MsQuicNativeMethods.NativeApi nativeRegistration = *registration; + GetParamDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->GetParam); + + SetCallbackHandlerDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->SetCallbackHandler); RegistrationOpenDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.RegistrationOpen); + Marshal.GetDelegateForFunctionPointer( + vtable->RegistrationOpen); RegistrationCloseDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.RegistrationClose); - - SecConfigCreateDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SecConfigCreate); - SecConfigDeleteDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SecConfigDelete); - SessionOpenDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SessionOpen); - SessionCloseDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SessionClose); - SessionShutdownDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SessionShutdown); + Marshal.GetDelegateForFunctionPointer( + vtable->RegistrationClose); + + ConfigurationOpenDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConfigurationOpen); + ConfigurationCloseDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConfigurationClose); + ConfigurationLoadCredentialDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConfigurationLoadCredential); ListenerOpenDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerOpen); + Marshal.GetDelegateForFunctionPointer( + vtable->ListenerOpen); ListenerCloseDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerClose); + Marshal.GetDelegateForFunctionPointer( + vtable->ListenerClose); ListenerStartDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerStart); + Marshal.GetDelegateForFunctionPointer( + vtable->ListenerStart); ListenerStopDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ListenerStop); + Marshal.GetDelegateForFunctionPointer( + vtable->ListenerStop); ConnectionOpenDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionOpen); + Marshal.GetDelegateForFunctionPointer( + vtable->ConnectionOpen); ConnectionCloseDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionClose); + Marshal.GetDelegateForFunctionPointer( + vtable->ConnectionClose); + ConnectionSetConfigurationDelegate = + Marshal.GetDelegateForFunctionPointer( + vtable->ConnectionSetConfiguration); ConnectionShutdownDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionShutdown); + Marshal.GetDelegateForFunctionPointer( + vtable->ConnectionShutdown); ConnectionStartDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.ConnectionStart); + Marshal.GetDelegateForFunctionPointer( + vtable->ConnectionStart); StreamOpenDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamOpen); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamOpen); StreamCloseDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamClose); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamClose); StreamStartDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamStart); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamStart); StreamShutdownDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamShutdown); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamShutdown); StreamSendDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamSend); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamSend); StreamReceiveCompleteDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamReceiveComplete); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamReceiveComplete); StreamReceiveSetEnabledDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.StreamReceiveSetEnabled); - SetContextDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SetContext); - GetContextDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.GetContext); - SetCallbackHandlerDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SetCallbackHandler); + Marshal.GetDelegateForFunctionPointer( + vtable->StreamReceiveSetEnabled); - SetParamDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.SetParam); - GetParamDelegate = - Marshal.GetDelegateForFunctionPointer( - nativeRegistration.GetParam); - - var registrationConfig = new MsQuicNativeMethods.RegistrationConfig + var cfg = new RegistrationConfig { - AppName = "SystemNetQuic", + AppName = ".NET", ExecutionProfile = QUIC_EXECUTION_PROFILE.QUIC_EXECUTION_PROFILE_LOW_LATENCY }; - RegistrationOpenDelegate(ref registrationConfig, out IntPtr ctx); - _registrationContext = ctx; + status = RegistrationOpenDelegate(ref cfg, out SafeMsQuicRegistrationHandle handle); + QuicExceptionHelpers.ThrowIfFailed(status, "RegistrationOpen failed."); + + Registration = handle; } internal static MsQuicApi Api { get; } = null!; @@ -140,278 +121,63 @@ private unsafe MsQuicApi() static MsQuicApi() { - // MsQuicOpen will succeed even if the platform will not support it. It will then fail with unspecified - // platform-specific errors in subsequent callbacks. For now, check for the minimum build we've tested it on. - - // TODO: - // - Hopefully, MsQuicOpen will perform this check for us and give us a consistent error code. - // - Otherwise, dial this in to reflect actual minimum requirements and add some sort of platform - // error code mapping when creating exceptions. - - // TODO: try to initialize TLS 1.3 in SslStream. - - try - { - Api = new MsQuicApi(); - IsQuicSupported = true; - } - catch (NotSupportedException) - { - IsQuicSupported = false; - } - } - - internal MsQuicNativeMethods.RegistrationOpenDelegate RegistrationOpenDelegate { get; } - internal MsQuicNativeMethods.RegistrationCloseDelegate RegistrationCloseDelegate { get; } - - internal MsQuicNativeMethods.SecConfigCreateDelegate SecConfigCreateDelegate { get; } - internal MsQuicNativeMethods.SecConfigDeleteDelegate SecConfigDeleteDelegate { get; } - - internal MsQuicNativeMethods.SessionOpenDelegate SessionOpenDelegate { get; } - internal MsQuicNativeMethods.SessionCloseDelegate SessionCloseDelegate { get; } - internal MsQuicNativeMethods.SessionShutdownDelegate SessionShutdownDelegate { get; } - - internal MsQuicNativeMethods.ListenerOpenDelegate ListenerOpenDelegate { get; } - internal MsQuicNativeMethods.ListenerCloseDelegate ListenerCloseDelegate { get; } - internal MsQuicNativeMethods.ListenerStartDelegate ListenerStartDelegate { get; } - internal MsQuicNativeMethods.ListenerStopDelegate ListenerStopDelegate { get; } - - internal MsQuicNativeMethods.ConnectionOpenDelegate ConnectionOpenDelegate { get; } - internal MsQuicNativeMethods.ConnectionCloseDelegate ConnectionCloseDelegate { get; } - internal MsQuicNativeMethods.ConnectionShutdownDelegate ConnectionShutdownDelegate { get; } - internal MsQuicNativeMethods.ConnectionStartDelegate ConnectionStartDelegate { get; } - - internal MsQuicNativeMethods.StreamOpenDelegate StreamOpenDelegate { get; } - internal MsQuicNativeMethods.StreamCloseDelegate StreamCloseDelegate { get; } - internal MsQuicNativeMethods.StreamStartDelegate StreamStartDelegate { get; } - internal MsQuicNativeMethods.StreamShutdownDelegate StreamShutdownDelegate { get; } - internal MsQuicNativeMethods.StreamSendDelegate StreamSendDelegate { get; } - internal MsQuicNativeMethods.StreamReceiveCompleteDelegate StreamReceiveCompleteDelegate { get; } - internal MsQuicNativeMethods.StreamReceiveSetEnabledDelegate StreamReceiveSetEnabledDelegate { get; } - - internal MsQuicNativeMethods.SetContextDelegate SetContextDelegate { get; } - internal MsQuicNativeMethods.GetContextDelegate GetContextDelegate { get; } - internal MsQuicNativeMethods.SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; } - - internal MsQuicNativeMethods.SetParamDelegate SetParamDelegate { get; } - internal MsQuicNativeMethods.GetParamDelegate GetParamDelegate { get; } - - internal unsafe uint UnsafeSetParam( - IntPtr Handle, - uint Level, - uint Param, - MsQuicNativeMethods.QuicBuffer Buffer) - { - return SetParamDelegate( - Handle, - Level, - Param, - Buffer.Length, - Buffer.Buffer); - } - - internal unsafe uint UnsafeGetParam( - IntPtr Handle, - uint Level, - uint Param, - ref MsQuicNativeMethods.QuicBuffer Buffer) - { - uint bufferLength = Buffer.Length; - byte* buf = Buffer.Buffer; - return GetParamDelegate( - Handle, - Level, - Param, - &bufferLength, - buf); - } - - public async ValueTask CreateSecurityConfig(X509Certificate certificate, string? certFilePath, string? privateKeyFilePath) - { - MsQuicSecurityConfig? secConfig = null; - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - uint secConfigCreateStatus = MsQuicStatusCodes.InternalError; - uint createConfigStatus; - IntPtr unmanagedAddr = IntPtr.Zero; - MsQuicNativeMethods.CertFileParams fileParams = default; - - try + // TODO: Consider updating all of these delegates to instead use function pointers. + if (NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle)) { - if (certFilePath != null && privateKeyFilePath != null) + try { - fileParams = new MsQuicNativeMethods.CertFileParams + if (NativeLibrary.TryGetExport(msQuicHandle, "MsQuicOpen", out IntPtr msQuicOpenAddress)) { - PrivateKeyFilePath = Marshal.StringToCoTaskMemUTF8(privateKeyFilePath), - CertificateFilePath = Marshal.StringToCoTaskMemUTF8(certFilePath) - }; - - unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(fileParams)); - Marshal.StructureToPtr(fileParams, unmanagedAddr, fDeleteOld: false); - - createConfigStatus = SecConfigCreateDelegate( - _registrationContext, - (uint)QUIC_SEC_CONFIG_FLAG.CERT_FILE, - unmanagedAddr, - null, - IntPtr.Zero, - SecCfgCreateCallbackHandler); - } - else if (certificate != null) - { - createConfigStatus = SecConfigCreateDelegate( - _registrationContext, - (uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT, - certificate.Handle, - null, - IntPtr.Zero, - SecCfgCreateCallbackHandler); - } - else - { - // If no certificate is provided, provide a null one. - createConfigStatus = SecConfigCreateDelegate( - _registrationContext, - (uint)QUIC_SEC_CONFIG_FLAG.CERT_NULL, - IntPtr.Zero, - null, - IntPtr.Zero, - SecCfgCreateCallbackHandler); + MsQuicOpenDelegate msQuicOpen = + Marshal.GetDelegateForFunctionPointer(msQuicOpenAddress); + uint status = msQuicOpen(out NativeApi* vtable); + if (MsQuicStatusHelper.SuccessfulStatusCode(status)) + { + IsQuicSupported = true; + Api = new MsQuicApi(vtable); + } + } } - - QuicExceptionHelpers.ThrowIfFailed( - createConfigStatus, - "Could not create security configuration."); - - void SecCfgCreateCallbackHandler( - IntPtr context, - uint status, - IntPtr securityConfig) + finally { - secConfig = new MsQuicSecurityConfig(this, securityConfig); - secConfigCreateStatus = status; - tcs.SetResult(); - } - - await tcs.Task.ConfigureAwait(false); - - QuicExceptionHelpers.ThrowIfFailed( - secConfigCreateStatus, - "Could not create security configuration."); - } - finally - { - if (fileParams.CertificateFilePath != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(fileParams.CertificateFilePath); - } - - if (fileParams.PrivateKeyFilePath != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(fileParams.PrivateKeyFilePath); - } - - if (unmanagedAddr != IntPtr.Zero) - { - Marshal.FreeHGlobal(unmanagedAddr); - } - } - - return secConfig; - } - - public unsafe IntPtr SessionOpen(List alpnProtocols) - { - if (alpnProtocols.Count == 1) - { - return SessionOpen(alpnProtocols[0]); - } - - var memoryHandles = ArrayPool.Shared.Rent(alpnProtocols.Count); - var quicBuffers = ArrayPool.Shared.Rent(alpnProtocols.Count); - - try - { - for (int i = 0; i < alpnProtocols.Count; ++i) - { - ReadOnlyMemory alpnProtocol = alpnProtocols[i].Protocol; - MemoryHandle h = alpnProtocol.Pin(); - - memoryHandles[i] = h; - quicBuffers[i].Buffer = (byte*)h.Pointer; - quicBuffers[i].Length = (uint)alpnProtocol.Length; - } - - IntPtr session; - - fixed (MsQuicNativeMethods.QuicBuffer* pQuicBuffers = quicBuffers) - { - session = SessionOpen(pQuicBuffers, (uint)alpnProtocols.Count); - } - - ArrayPool.Shared.Return(quicBuffers); - ArrayPool.Shared.Return(memoryHandles); - - return session; - } - finally - { - foreach (MemoryHandle handle in memoryHandles) - { - handle.Dispose(); + if (!IsQuicSupported) + { + NativeLibrary.Free(msQuicHandle); + } } } } - private unsafe IntPtr SessionOpen(SslApplicationProtocol alpnProtocol) - { - ReadOnlyMemory memory = alpnProtocol.Protocol; - using MemoryHandle h = memory.Pin(); - - var quicBuffer = new MsQuicNativeMethods.QuicBuffer() - { - Buffer = (byte*)h.Pointer, - Length = (uint)memory.Length - }; - - return SessionOpen(&quicBuffer, 1); - } - - private unsafe IntPtr SessionOpen(MsQuicNativeMethods.QuicBuffer *alpnBuffers, uint bufferCount) - { - IntPtr sessionPtr = IntPtr.Zero; - uint status = SessionOpenDelegate( - _registrationContext, - alpnBuffers, - bufferCount, - IntPtr.Zero, - ref sessionPtr); - - QuicExceptionHelpers.ThrowIfFailed(status, "Could not open session."); - - return sessionPtr; - } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - ~MsQuicApi() - { - Dispose(disposing: false); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - RegistrationCloseDelegate?.Invoke(_registrationContext); - - _disposed = true; - } + internal RegistrationOpenDelegate RegistrationOpenDelegate { get; } + internal RegistrationCloseDelegate RegistrationCloseDelegate { get; } + + internal ConfigurationOpenDelegate ConfigurationOpenDelegate { get; } + internal ConfigurationCloseDelegate ConfigurationCloseDelegate { get; } + internal ConfigurationLoadCredentialDelegate ConfigurationLoadCredentialDelegate { get; } + + internal ListenerOpenDelegate ListenerOpenDelegate { get; } + internal ListenerCloseDelegate ListenerCloseDelegate { get; } + internal ListenerStartDelegate ListenerStartDelegate { get; } + internal ListenerStopDelegate ListenerStopDelegate { get; } + + // TODO: missing SendResumptionTicket + internal ConnectionOpenDelegate ConnectionOpenDelegate { get; } + internal ConnectionCloseDelegate ConnectionCloseDelegate { get; } + internal ConnectionShutdownDelegate ConnectionShutdownDelegate { get; } + internal ConnectionStartDelegate ConnectionStartDelegate { get; } + internal ConnectionSetConfigurationDelegate ConnectionSetConfigurationDelegate { get; } + + internal StreamOpenDelegate StreamOpenDelegate { get; } + internal StreamCloseDelegate StreamCloseDelegate { get; } + internal StreamStartDelegate StreamStartDelegate { get; } + internal StreamShutdownDelegate StreamShutdownDelegate { get; } + internal StreamSendDelegate StreamSendDelegate { get; } + internal StreamReceiveCompleteDelegate StreamReceiveCompleteDelegate { get; } + internal StreamReceiveSetEnabledDelegate StreamReceiveSetEnabledDelegate { get; } + + internal SetCallbackHandlerDelegate SetCallbackHandlerDelegate { get; } + + internal SetParamDelegate SetParamDelegate { get; } + internal GetParamDelegate GetParamDelegate { get; } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs index afeb151a0436b..63672d2a6e175 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicParameterHelpers.cs @@ -1,97 +1,62 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Runtime.InteropServices; using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; namespace System.Net.Quic.Implementations.MsQuic.Internal { internal static class MsQuicParameterHelpers { - internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + internal static unsafe SOCKADDR_INET GetINetParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { - byte* ptr = stackalloc byte[sizeof(SOCKADDR_INET)]; - QuicBuffer buffer = new QuicBuffer - { - Length = (uint)sizeof(SOCKADDR_INET), - Buffer = ptr - }; + SOCKADDR_INET value; + uint valueLen = (uint)sizeof(SOCKADDR_INET); - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), - "Could not get SOCKADDR_INET."); + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + QuicExceptionHelpers.ThrowIfFailed(status, "GetINETParam failed."); + Debug.Assert(valueLen == sizeof(SOCKADDR_INET)); - return *(SOCKADDR_INET*)ptr; + return value; } - internal static unsafe ushort GetUShortParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + internal static unsafe ushort GetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { - byte* ptr = stackalloc byte[sizeof(ushort)]; - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ushort), - Buffer = ptr - }; + ushort value; + uint valueLen = (uint)sizeof(ushort); - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), - "Could not get ushort."); + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + QuicExceptionHelpers.ThrowIfFailed(status, "GetUShortParam failed."); + Debug.Assert(valueLen == sizeof(ushort)); - return *(ushort*)ptr; + return value; } - internal static unsafe void SetUshortParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param, ushort value) + internal static unsafe void SetUShortParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, ushort value) { - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ushort), - Buffer = (byte*)&value - }; - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeSetParam(nativeObject, level, param, buffer), + api.SetParamDelegate(nativeObject, level, param, sizeof(ushort), (byte*)&value), "Could not set ushort."); } - internal static unsafe ulong GetULongParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param) + internal static unsafe ulong GetULongParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param) { - byte* ptr = stackalloc byte[sizeof(ulong)]; - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ulong), - Buffer = ptr - }; + ulong value; + uint valueLen = (uint)sizeof(ulong); - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), - "Could not get ulong."); + uint status = api.GetParamDelegate(nativeObject, level, param, ref valueLen, (byte*)&value); + QuicExceptionHelpers.ThrowIfFailed(status, "GetULongParam failed."); + Debug.Assert(valueLen == sizeof(ulong)); - return *(ulong*)ptr; + return value; } - internal static unsafe void SetULongParam(MsQuicApi api, IntPtr nativeObject, uint level, uint param, ulong value) + internal static unsafe void SetULongParam(MsQuicApi api, SafeHandle nativeObject, QUIC_PARAM_LEVEL level, uint param, ulong value) { - QuicBuffer buffer = new QuicBuffer() - { - Length = sizeof(ulong), - Buffer = (byte*)&value - }; - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeGetParam(nativeObject, level, param, ref buffer), + api.SetParamDelegate(nativeObject, level, param, sizeof(ulong), (byte*)&value), "Could not set ulong."); } - - internal static unsafe void SetSecurityConfig(MsQuicApi api, IntPtr nativeObject, uint level, uint param, IntPtr value) - { - QuicBuffer buffer = new QuicBuffer() - { - Length = (uint)sizeof(void*), - Buffer = (byte*)&value - }; - - QuicExceptionHelpers.ThrowIfFailed( - api.UnsafeSetParam(nativeObject, level, param, buffer), - "Could not set security configuration."); - } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs deleted file mode 100644 index 68b1432028b66..0000000000000 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSecurityConfig.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Quic.Implementations.MsQuic.Internal -{ - // TODO this will eventually be abstracted to support both Client and Server - // certificates - internal class MsQuicSecurityConfig : IDisposable - { - private bool _disposed; - private MsQuicApi _registration; - - public MsQuicSecurityConfig(MsQuicApi registration, IntPtr nativeObjPtr) - { - _registration = registration; - NativeObjPtr = nativeObjPtr; - } - - public IntPtr NativeObjPtr { get; private set; } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - _registration.SecConfigDeleteDelegate?.Invoke(NativeObjPtr); - NativeObjPtr = IntPtr.Zero; - _disposed = true; - } - - ~MsQuicSecurityConfig() - { - Dispose(disposing: false); - } - } -} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs deleted file mode 100644 index 8b0e28e290ef7..0000000000000 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicSession.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Net.Security; - -namespace System.Net.Quic.Implementations.MsQuic.Internal -{ - internal sealed class MsQuicSession : IDisposable - { - private bool _disposed; - private IntPtr _nativeObjPtr; - private bool _opened; - - internal MsQuicSession() - { - if (!MsQuicApi.IsQuicSupported) - { - throw new NotSupportedException(SR.net_quic_notsupported); - } - } - - public IntPtr ConnectionOpen(QuicClientConnectionOptions options) - { - if (!_opened) - { - OpenSession(options.ClientAuthenticationOptions!.ApplicationProtocols!, - (ushort)options.MaxBidirectionalStreams, - (ushort)options.MaxUnidirectionalStreams); - } - - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ConnectionOpenDelegate( - _nativeObjPtr, - MsQuicConnection.s_connectionDelegate, - IntPtr.Zero, - out IntPtr connectionPtr), - "Could not open the connection."); - - return connectionPtr; - } - - private void OpenSession(List alpn, ushort bidirectionalStreamCount, ushort undirectionalStreamCount) - { - _opened = true; - _nativeObjPtr = MsQuicApi.Api.SessionOpen(alpn); - SetPeerBiDirectionalStreamCount(bidirectionalStreamCount); - SetPeerUnidirectionalStreamCount(undirectionalStreamCount); - } - - // TODO allow for a callback to select the certificate (SNI). - public IntPtr ListenerOpen(QuicListenerOptions options) - { - if (!_opened) - { - OpenSession(options.ServerAuthenticationOptions!.ApplicationProtocols!, - (ushort)options.MaxBidirectionalStreams, - (ushort)options.MaxUnidirectionalStreams); - } - - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerOpenDelegate( - _nativeObjPtr, - MsQuicListener.s_listenerDelegate, - IntPtr.Zero, - out IntPtr listenerPointer), - "Could not open listener."); - - return listenerPointer; - } - - // TODO call this for graceful shutdown? - public void ShutDown( - QUIC_CONNECTION_SHUTDOWN_FLAG Flags, - ushort ErrorCode) - { - MsQuicApi.Api.SessionShutdownDelegate( - _nativeObjPtr, - (uint)Flags, - ErrorCode); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void SetPeerBiDirectionalStreamCount(ushort count) - { - SetUshortParameter(QUIC_PARAM_SESSION.PEER_BIDI_STREAM_COUNT, count); - } - - public void SetPeerUnidirectionalStreamCount(ushort count) - { - SetUshortParameter(QUIC_PARAM_SESSION.PEER_UNIDI_STREAM_COUNT, count); - } - - private unsafe void SetUshortParameter(QUIC_PARAM_SESSION param, ushort count) - { - var buffer = new MsQuicNativeMethods.QuicBuffer() - { - Length = sizeof(ushort), - Buffer = (byte*)&count - }; - - SetParam(param, buffer); - } - - public void SetDisconnectTimeout(TimeSpan timeout) - { - SetULongParamter(QUIC_PARAM_SESSION.DISCONNECT_TIMEOUT, (ulong)timeout.TotalMilliseconds); - } - - public void SetIdleTimeout(TimeSpan timeout) - { - SetULongParamter(QUIC_PARAM_SESSION.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds); - - } - private unsafe void SetULongParamter(QUIC_PARAM_SESSION param, ulong count) - { - var buffer = new MsQuicNativeMethods.QuicBuffer() - { - Length = sizeof(ulong), - Buffer = (byte*)&count - }; - SetParam(param, buffer); - } - - private void SetParam( - QUIC_PARAM_SESSION param, - MsQuicNativeMethods.QuicBuffer buf) - { - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.UnsafeSetParam( - _nativeObjPtr, - (uint)QUIC_PARAM_LEVEL.SESSION, - (uint)param, - buf), - "Could not set parameter on session."); - } - - ~MsQuicSession() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - MsQuicApi.Api.SessionCloseDelegate?.Invoke(_nativeObjPtr); - _nativeObjPtr = IntPtr.Zero; - - _disposed = true; - } - } -} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs index b1ac4c17552bd..fb94687962ee1 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/ResettableCompletionSource.cs @@ -10,9 +10,9 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal /// A resettable completion source which can be completed multiple times. /// Used to make methods async between completed events and their associated async method. /// - internal class ResettableCompletionSource : IValueTaskSource, IValueTaskSource + internal sealed class ResettableCompletionSource : IValueTaskSource, IValueTaskSource { - protected ManualResetValueTaskSourceCore _valueTaskSource; + private ManualResetValueTaskSourceCore _valueTaskSource; public ResettableCompletionSource() { diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs new file mode 100644 index 0000000000000..916cfc4c550cd --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicAlpnHelper.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Security; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal static class MsQuicAlpnHelper + { + public static unsafe void Prepare(List alpnProtocols, [NotNull] out MemoryHandle[]? handles, [NotNull] out QuicBuffer[]? buffers) + { + handles = ArrayPool.Shared.Rent(alpnProtocols.Count); + buffers = ArrayPool.Shared.Rent(alpnProtocols.Count); + + try + { + for (int i = 0; i < alpnProtocols.Count; ++i) + { + ReadOnlyMemory alpnProtocol = alpnProtocols[i].Protocol; + MemoryHandle h = alpnProtocol.Pin(); + + handles[i] = h; + buffers[i].Buffer = (byte*)h.Pointer; + buffers[i].Length = (uint)alpnProtocol.Length; + } + } + catch + { + Return(ref handles, ref buffers); + throw; + } + } + + public static void Return(ref MemoryHandle[]? handles, ref QuicBuffer[]? buffers) + { + if (handles is MemoryHandle[] notNullHandles) + { + foreach (MemoryHandle h in notNullHandles) + { + h.Dispose(); + } + + handles = null; + ArrayPool.Shared.Return(notNullHandles); + } + + if (buffers is QuicBuffer[] notNullBuffers) + { + buffers = null; + ArrayPool.Shared.Return(notNullBuffers); + } + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs new file mode 100644 index 0000000000000..34dd31bf70ce8 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicEnums.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal enum QUIC_EXECUTION_PROFILE : uint + { + QUIC_EXECUTION_PROFILE_LOW_LATENCY, // Default + QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT, + QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER, + QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME, + } + + internal enum QUIC_CREDENTIAL_TYPE : uint + { + NONE, + HASH, + HASH_STORE, + CONTEXT, + FILE, + FILE_PROTECTED, + STUB_NULL = 0xF0000000, // Pass as server cert to stubtls implementation. + } + + [Flags] + internal enum QUIC_CREDENTIAL_FLAGS : uint + { + NONE = 0x00000000, + CLIENT = 0x00000001, // Lack of client flag indicates server. + LOAD_ASYNCHRONOUS = 0x00000002, + NO_CERTIFICATE_VALIDATION = 0x00000004, + ENABLE_OCSP = 0x00000008, // Schannel only currently. + INDICATE_CERTIFICATE_RECEIVED = 0x00000010, + DEFER_CERTIFICATE_VALIDATION = 0x00000020, // Schannel only currently. + REQUIRE_CLIENT_AUTHENTICATION = 0x00000040, // Schannel only currently. + USE_TLS_BUILTIN_CERTIFICATE_VALIDATION = 0x00000080, + } + + internal enum QUIC_CERTIFICATE_HASH_STORE_FLAGS + { + QUIC_CERTIFICATE_HASH_STORE_FLAG_NONE = 0x0000, + QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE = 0x0001, + } + + [Flags] + internal enum QUIC_CONNECTION_SHUTDOWN_FLAGS : uint + { + NONE = 0x0000, + SILENT = 0x0001, // Don't send the close frame over the network. + } + + internal enum QUIC_SERVER_RESUMPTION_LEVEL : uint + { + NO_RESUME, + RESUME_ONLY, + RESUME_AND_ZERORTT, + } + + [Flags] + internal enum QUIC_STREAM_OPEN_FLAGS : uint + { + NONE = 0x0000, + UNIDIRECTIONAL = 0x0001, // Indicates the stream is unidirectional. + ZERO_RTT = 0x0002, // The stream was opened via a 0-RTT packet. + } + + [Flags] + internal enum QUIC_STREAM_START_FLAGS : uint + { + NONE = 0x0000, + FAIL_BLOCKED = 0x0001, // Only opens the stream if flow control allows. + IMMEDIATE = 0x0002, // Immediately informs peer that stream is open. + ASYNC = 0x0004, // Don't block the API call to wait for completion. + SHUTDOWN_ON_FAIL = 0x0008, // Shutdown the stream immediately after start failure. + } + + [Flags] + internal enum QUIC_STREAM_SHUTDOWN_FLAGS : uint + { + NONE = 0x0000, + GRACEFUL = 0x0001, // Cleanly closes the send path. + ABORT_SEND = 0x0002, // Abruptly closes the send path. + ABORT_RECEIVE = 0x0004, // Abruptly closes the receive path. + ABORT = 0x0006, // Abruptly closes both send and receive paths. + IMMEDIATE = 0x0008, // Immediately sends completion events to app. + } + + [Flags] + internal enum QUIC_RECEIVE_FLAGS : uint + { + NONE = 0x0000, + ZERO_RTT = 0x0001, // Data was encrypted with 0-RTT key. + FIN = 0x0002, // FIN was included with this data. + } + + [Flags] + internal enum QUIC_SEND_FLAGS : uint + { + NONE = 0x0000, + ALLOW_0_RTT = 0x0001, // Allows the use of encrypting with 0-RTT key. + START = 0x0002, // Asynchronously starts the stream with the sent data. + FIN = 0x0004, // Indicates the request is the one last sent on the stream. + DGRAM_PRIORITY = 0x0008, // Indicates the datagram is higher priority than others. + DELAY_SEND = 0x0010, // Indicates the send should be delayed because more will be queued soon. + } + + internal enum QUIC_PARAM_LEVEL : uint + { + GLOBAL, + REGISTRATION, + CONFIGURATION, + LISTENER, + CONNECTION, + TLS, + STREAM, + } + + internal enum QUIC_PARAM_GLOBAL : uint + { + RETRY_MEMORY_PERCENT = 0, // uint16_t + SUPPORTED_VERSIONS = 1, // uint32_t[] - network byte order + LOAD_BALANCING_MODE = 2, // uint16_t - QUIC_LOAD_BALANCING_MODE + PERF_COUNTERS = 3, // uint64_t[] - Array size is QUIC_PERF_COUNTER_MAX + SETTINGS = 4, // QUIC_SETTINGS + } + + internal enum QUIC_PARAM_REGISTRATION : uint + { + CID_PREFIX = 0, // uint8_t[] + } + + internal enum QUIC_PARAM_LISTENER : uint + { + LOCAL_ADDRESS = 0, // QUIC_ADDR + STATS = 1, // QUIC_LISTENER_STATISTICS + } + + internal enum QUIC_PARAM_CONN : uint + { + QUIC_VERSION = 0, // uint32_t + LOCAL_ADDRESS = 1, // QUIC_ADDR + REMOTE_ADDRESS = 2, // QUIC_ADDR + IDEAL_PROCESSOR = 3, // uint16_t + SETTINGS = 4, // QUIC_SETTINGS + STATISTICS = 5, // QUIC_STATISTICS + STATISTICS_PLAT = 6, // QUIC_STATISTICS + SHARE_UDP_BINDING = 7, // uint8_t (BOOLEAN) + LOCAL_BIDI_STREAM_COUNT = 8, // uint16_t + LOCAL_UNIDI_STREAM_COUNT = 9, // uint16_t + MAX_STREAM_IDS = 10, // uint64_t[4] + CLOSE_REASON_PHRASE = 11, // char[] + STREAM_SCHEDULING_SCHEME = 12, // QUIC_STREAM_SCHEDULING_SCHEME + DATAGRAM_RECEIVE_ENABLED = 13, // uint8_t (BOOLEAN) + DATAGRAM_SEND_ENABLED = 14, // uint8_t (BOOLEAN) + DISABLE_1RTT_ENCRYPTION = 15, // uint8_t (BOOLEAN) + RESUMPTION_TICKET = 16, // uint8_t[] + PEER_CERTIFICATE_VALID = 17, // uint8_t (BOOLEAN) + } + + internal enum QUIC_PARAM_STREAM : uint + { + ID = 0, // QUIC_UINT62 + ZERRTT_LENGTH = 1, // uint64_t + IDEAL_SEND_BUFFER_SIZE = 2, // uint64_t - bytes + } + + internal enum QUIC_LISTENER_EVENT : uint + { + NEW_CONNECTION = 0, + } + + internal enum QUIC_CONNECTION_EVENT_TYPE : uint + { + CONNECTED = 0, + SHUTDOWN_INITIATED_BY_TRANSPORT = 1, // The transport started the shutdown process. + SHUTDOWN_INITIATED_BY_PEER = 2, // The peer application started the shutdown process. + SHUTDOWN_COMPLETE = 3, // Ready for the handle to be closed. + LOCAL_ADDRESS_CHANGED = 4, + PEER_ADDRESS_CHANGED = 5, + PEER_STREAM_STARTED = 6, + STREAMS_AVAILABLE = 7, + PEER_NEEDS_STREAMS = 8, + IDEAL_PROCESSOR_CHANGED = 9, + DATAGRAM_STATE_CHANGED = 10, + DATAGRAM_RECEIVED = 11, + DATAGRAM_SEND_STATE_CHANGED = 12, + RESUMED = 13, // Server-only; provides resumption data, if any. + RESUMPTION_TICKET_RECEIVED = 14, // Client-only; provides ticket to persist, if any. + PEER_CERTIFICATE_RECEIVED = 15, // Only with QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED set. + } + + internal enum QUIC_STREAM_EVENT_TYPE : uint + { + START_COMPLETE = 0, + RECEIVE = 1, + SEND_COMPLETE = 2, + PEER_SEND_SHUTDOWN = 3, + PEER_SEND_ABORTED = 4, + PEER_RECEIVE_ABORTED = 5, + SEND_SHUTDOWN_COMPLETE = 6, + SHUTDOWN_COMPLETE = 7, + IDEAL_SEND_BUFFER_SIZE = 8, + } + + internal enum QUIC_ADDRESS_FAMILY : ushort + { + UNSPEC = 0, + INET = 2, + INET6 = 23, + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicNativeMethods.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs similarity index 53% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicNativeMethods.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs index c8ff6d6a8422c..131227746c3ad 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicNativeMethods.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicNativeMethods.cs @@ -1,9 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Net.Sockets; using System.Runtime.InteropServices; -using System.Text; namespace System.Net.Quic.Implementations.MsQuic.Internal { @@ -24,13 +22,11 @@ internal struct NativeApi internal IntPtr RegistrationOpen; internal IntPtr RegistrationClose; + internal IntPtr RegistrationShutdown; - internal IntPtr SecConfigCreate; - internal IntPtr SecConfigDelete; - - internal IntPtr SessionOpen; - internal IntPtr SessionClose; - internal IntPtr SessionShutdown; + internal IntPtr ConfigurationOpen; + internal IntPtr ConfigurationClose; + internal IntPtr ConfigurationLoadCredential; internal IntPtr ListenerOpen; internal IntPtr ListenerClose; @@ -41,6 +37,7 @@ internal struct NativeApi internal IntPtr ConnectionClose; internal IntPtr ConnectionShutdown; internal IntPtr ConnectionStart; + internal IntPtr ConnectionSetConfiguration; internal IntPtr ConnectionSendResumptionTicket; internal IntPtr StreamOpen; @@ -54,42 +51,49 @@ internal struct NativeApi internal IntPtr DatagramSend; } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate uint MsQuicOpenDelegate( + out NativeApi* registration); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint SetContextDelegate( - IntPtr handle, + SafeHandle handle, IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr GetContextDelegate( - IntPtr handle); + SafeHandle handle); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void SetCallbackHandlerDelegate( - IntPtr handle, + SafeHandle handle, Delegate del, IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint SetParamDelegate( - IntPtr handle, - uint level, + SafeHandle handle, + QUIC_PARAM_LEVEL level, uint param, uint bufferLength, byte* buffer); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint GetParamDelegate( - IntPtr handle, - uint level, + SafeHandle handle, + QUIC_PARAM_LEVEL level, uint param, - uint* bufferLength, + ref uint bufferLength, byte* buffer); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint RegistrationOpenDelegate(ref RegistrationConfig config, out IntPtr registrationContext); + internal delegate uint RegistrationOpenDelegate( + ref RegistrationConfig config, + out SafeMsQuicRegistrationHandle registrationContext); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void RegistrationCloseDelegate(IntPtr registrationContext); + internal delegate void RegistrationCloseDelegate( + IntPtr registrationContext); [StructLayout(LayoutKind.Sequential)] internal struct RegistrationConfig @@ -100,38 +104,173 @@ internal struct RegistrationConfig } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SecConfigCreateCompleteDelegate(IntPtr context, uint status, IntPtr securityConfig); - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint SecConfigCreateDelegate( - IntPtr registrationContext, - uint flags, - IntPtr certificate, - [MarshalAs(UnmanagedType.LPUTF8Str)]string? principal, + internal delegate uint ConfigurationOpenDelegate( + SafeMsQuicRegistrationHandle registrationContext, + QuicBuffer* alpnBuffers, + uint alpnBufferCount, + ref QuicSettings settings, + uint settingsSize, IntPtr context, - SecConfigCreateCompleteDelegate completionHandler); + out SafeMsQuicConfigurationHandle configuration); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SecConfigDeleteDelegate( - IntPtr securityConfig); + internal delegate void ConfigurationCloseDelegate( + IntPtr configuration); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint SessionOpenDelegate( - IntPtr registrationContext, - QuicBuffer *alpnBuffers, - uint alpnBufferCount, - IntPtr context, - ref IntPtr session); + internal delegate uint ConfigurationLoadCredentialDelegate( + SafeMsQuicConfigurationHandle configuration, + ref CredentialConfig credConfig); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SessionCloseDelegate( - IntPtr session); + [StructLayout(LayoutKind.Sequential)] + internal struct QuicSettings + { + internal QuicSettingsIsSetFlags IsSetFlags; + internal ulong MaxBytesPerKey; + internal ulong HandshakeIdleTimeoutMs; + internal ulong IdleTimeoutMs; + internal uint TlsClientMaxSendBuffer; + internal uint TlsServerMaxSendBuffer; + internal uint StreamRecvWindowDefault; + internal uint StreamRecvBufferDefault; + internal uint ConnFlowControlWindow; + internal uint MaxWorkerQueueDelayUs; + internal uint MaxStatelessOperations; + internal uint InitialWindowPackets; + internal uint SendIdleTimeoutMs; + internal uint InitialRttMs; + internal uint MaxAckDelayMs; + internal uint DisconnectTimeoutMs; + internal uint KeepAliveIntervalMs; + internal ushort PeerBidiStreamCount; + internal ushort PeerUnidiStreamCount; + internal ushort RetryMemoryLimit; // Global only + internal ushort LoadBalancingMode; // Global only + internal byte MaxOperationsPerDrain; + internal QuicSettingsEnabledFlagsFlags EnabledFlags; + internal uint* DesiredVersionsList; + internal uint DesiredVersionsListLength; + } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void SessionShutdownDelegate( - IntPtr session, - uint flags, - ulong errorCode); + [Flags] + internal enum QuicSettingsIsSetFlags : ulong + { + MaxBytesPerKey = 1 << 0, + HandshakeIdleTimeoutMs = 1 << 1, + IdleTimeoutMs = 1 << 2, + TlsClientMaxSendBuffer = 1 << 3, + TlsServerMaxSendBuffer = 1 << 4, + StreamRecvWindowDefault = 1 << 5, + StreamRecvBufferDefault = 1 << 6, + ConnFlowControlWindow = 1 << 7, + MaxWorkerQueueDelayUs = 1 << 8, + MaxStatelessOperations = 1 << 9, + InitialWindowPackets = 1 << 10, + SendIdleTimeoutMs = 1 << 11, + InitialRttMs = 1 << 12, + MaxAckDelayMs = 1 << 13, + DisconnectTimeoutMs = 1 << 14, + KeepAliveIntervalMs = 1 << 15, + PeerBidiStreamCount = 1 << 16, + PeerUnidiStreamCount = 1 << 17, + RetryMemoryLimit = 1 << 18, + LoadBalancingMode = 1 << 19, + MaxOperationsPerDrain = 1 << 20, + SendBufferingEnabled = 1 << 21, + PacingEnabled = 1 << 22, + MigrationEnabled = 1 << 23, + DatagramReceiveEnabled = 1 << 24, + ServerResumptionLevel = 1 << 25, + DesiredVersionsList = 1 << 26, + VersionNegotiationExtEnabled = 1 << 27, + } + + [Flags] + internal enum QuicSettingsEnabledFlagsFlags : byte + { + SendBufferingEnabled = 1 << 0, + PacingEnabled = 1 << 1, + MigrationEnabled = 1 << 2, + DatagramReceiveEnabled = 1 << 3, + // Contains values of QUIC_SERVER_RESUMPTION_LEVEL + ServerResumptionLevel = 1 << 4 | 1 << 5, + VersionNegotiationExtEnabled = 1 << 6, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfig + { + internal QUIC_CREDENTIAL_TYPE Type; + internal QUIC_CREDENTIAL_FLAGS Flags; + // CredentialConfigCertificateUnion* + internal IntPtr Certificate; + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string Principal; + internal IntPtr Reserved; // Currently unused + // TODO: define delegate for AsyncHandler and make proper use of it. + internal IntPtr AsyncHandler; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct CredentialConfigCertificateUnion + { + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateHash CertificateHash; + + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateHashStore CertificateHashStore; + + [FieldOffset(0)] + internal IntPtr CertificateContext; + + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateFile CertificateFile; + + [FieldOffset(0)] + internal CredentialConfigCertificateCertificateFileProtected CertificateFileProtected; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificateCertificateHash + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + internal byte[] ShaHash; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct CredentialConfigCertificateCertificateHashStore + { + internal QUIC_CERTIFICATE_HASH_STORE_FLAGS Flags; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + internal byte[] ShaHash; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + internal char[] StoreName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificateCertificateFile + { + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string PrivateKeyFile; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string CertificateFile; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct CredentialConfigCertificateCertificateFileProtected + { + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string PrivateKeyFile; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string CertificateFile; + + [MarshalAs(UnmanagedType.LPUTF8Str)] + internal string PrivateKeyPassword; + } [StructLayout(LayoutKind.Sequential)] internal struct ListenerEvent @@ -150,24 +289,29 @@ internal struct ListenerEventDataUnion [StructLayout(LayoutKind.Sequential)] internal struct ListenerEventDataNewConnection { - internal IntPtr Info; + internal NewConnectionInfo* Info; internal IntPtr Connection; - internal IntPtr SecurityConfig; } [StructLayout(LayoutKind.Sequential)] internal struct NewConnectionInfo { internal uint QuicVersion; + // QUIC_ADDR internal IntPtr LocalAddress; + // QUIC_ADDR internal IntPtr RemoteAddress; internal uint CryptoBufferLength; - internal ushort AlpnListLength; + internal ushort ClientAlpnListLength; internal ushort ServerNameLength; internal byte NegotiatedAlpnLength; + // byte[] internal IntPtr CryptoBuffer; + // byte[] internal IntPtr ClientAlpnList; + // byte[] internal IntPtr NegotiatedAlpn; + // string internal IntPtr ServerName; } @@ -179,29 +323,32 @@ internal delegate uint ListenerCallbackDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ListenerOpenDelegate( - IntPtr session, + SafeMsQuicRegistrationHandle registration, ListenerCallbackDelegate handler, IntPtr context, - out IntPtr listener); + out SafeMsQuicListenerHandle listener); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ListenerCloseDelegate( + internal delegate void ListenerCloseDelegate( IntPtr listener); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ListenerStartDelegate( - IntPtr listener, + SafeMsQuicListenerHandle listener, + QuicBuffer* alpnBuffers, + uint alpnBufferCount, ref SOCKADDR_INET localAddress); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ListenerStopDelegate( - IntPtr listener); + internal delegate void ListenerStopDelegate( + SafeMsQuicListenerHandle listener); [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataConnected { - internal bool SessionResumed; + internal byte SessionResumed; internal byte NegotiatedAlpnLength; + // byte[] internal IntPtr NegotiatedAlpn; } @@ -220,26 +367,37 @@ internal struct ConnectionEventDataShutdownInitiatedByPeer [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEventDataShutdownComplete { - internal bool TimedOut; + // The flags have fixed sized exactly 3 bits + internal ConnectionEventDataShutdownCompleteFlags Flags; + } + + [Flags] + internal enum ConnectionEventDataShutdownCompleteFlags : byte + { + HandshakeCompleted = 1 << 0, + PeerAcknowledgedShutdown = 1 << 1, + AppCloseInProgress = 1 << 2, } [StructLayout(LayoutKind.Sequential)] - internal struct ConnectionEventDataLocalAddrChanged + internal struct ConnectionEventDataLocalAddressChanged { + // QUIC_ADDR internal IntPtr Address; } [StructLayout(LayoutKind.Sequential)] - internal struct ConnectionEventDataPeerAddrChanged + internal struct ConnectionEventDataPeerAddressChanged { + // QUIC_ADDR internal IntPtr Address; } [StructLayout(LayoutKind.Sequential)] - internal struct ConnectionEventDataStreamStarted + internal struct ConnectionEventDataPeerStreamStarted { internal IntPtr Stream; - internal QUIC_STREAM_OPEN_FLAG Flags; + internal QUIC_STREAM_OPEN_FLAGS Flags; } [StructLayout(LayoutKind.Sequential)] @@ -265,32 +423,25 @@ internal struct ConnectionEventDataUnion internal ConnectionEventDataShutdownComplete ShutdownComplete; [FieldOffset(0)] - internal ConnectionEventDataLocalAddrChanged LocalAddrChanged; + internal ConnectionEventDataLocalAddressChanged LocalAddressChanged; [FieldOffset(0)] - internal ConnectionEventDataPeerAddrChanged PeerAddrChanged; + internal ConnectionEventDataPeerAddressChanged PeerAddressChanged; [FieldOffset(0)] - internal ConnectionEventDataStreamStarted StreamStarted; + internal ConnectionEventDataPeerStreamStarted PeerStreamStarted; [FieldOffset(0)] internal ConnectionEventDataStreamsAvailable StreamsAvailable; + + // TODO: missing IDEAL_PROCESSOR_CHANGED, ..., PEER_CERTIFICATE_RECEIVED (7 total) } [StructLayout(LayoutKind.Sequential)] internal struct ConnectionEvent { - internal QUIC_CONNECTION_EVENT Type; + internal QUIC_CONNECTION_EVENT_TYPE Type; internal ConnectionEventDataUnion Data; - - internal bool EarlyDataAccepted => Data.Connected.SessionResumed; - //internal ulong NumBytes => Data.IdealSendBuffer.NumBytes; - internal uint ShutdownBeginStatus => Data.ShutdownInitiatedByTransport.Status; - internal long ShutdownBeginPeerStatus => Data.ShutdownInitiatedByPeer.ErrorCode; - internal bool ShutdownTimedOut => Data.ShutdownComplete.TimedOut; - internal ushort BiDirectionalCount => Data.StreamsAvailable.BiDirectionalCount; - internal ushort UniDirectionalCount => Data.StreamsAvailable.UniDirectionalCount; - internal QUIC_STREAM_OPEN_FLAG StreamFlags => Data.StreamStarted.Flags; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -299,56 +450,65 @@ internal delegate uint ConnectionCallbackDelegate( IntPtr context, ref ConnectionEvent connectionEvent); + // TODO: order is Open, Close, Shutdown, Start, SetConfiguration, SendResumptionTicket [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConnectionOpenDelegate( - IntPtr session, + SafeMsQuicRegistrationHandle registration, ConnectionCallbackDelegate handler, IntPtr context, - out IntPtr connection); + out SafeMsQuicConnectionHandle connection); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ConnectionCloseDelegate( + internal delegate void ConnectionCloseDelegate( IntPtr connection); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate uint ConnectionSetConfigurationDelegate( + SafeMsQuicConnectionHandle connection, + SafeMsQuicConfigurationHandle configuration); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint ConnectionStartDelegate( - IntPtr connection, - ushort family, + SafeMsQuicConnectionHandle connection, + SafeMsQuicConfigurationHandle configuration, + QUIC_ADDRESS_FAMILY family, [MarshalAs(UnmanagedType.LPUTF8Str)] string serverName, ushort serverPort); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint ConnectionShutdownDelegate( - IntPtr connection, - uint flags, + internal delegate void ConnectionShutdownDelegate( + SafeMsQuicConnectionHandle connection, + QUIC_CONNECTION_SHUTDOWN_FLAGS flags, long errorCode); + // TODO: missing SendResumptionTicket + [StructLayout(LayoutKind.Sequential)] - internal struct StreamEventDataRecv + internal struct StreamEventDataReceive { internal ulong AbsoluteOffset; internal ulong TotalBufferLength; internal QuicBuffer* Buffers; internal uint BufferCount; - internal QUIC_RECEIVE_FLAG Flags; + internal QUIC_RECEIVE_FLAGS Flags; } [StructLayout(LayoutKind.Sequential)] internal struct StreamEventDataSendComplete { - internal bool Canceled; + internal byte Canceled; internal IntPtr ClientContext; } [StructLayout(LayoutKind.Sequential)] - internal struct StreamEventDataPeerSendAbort + internal struct StreamEventDataPeerSendAborted { internal long ErrorCode; } [StructLayout(LayoutKind.Sequential)] - internal struct StreamEventDataPeerRecvAbort + internal struct StreamEventDataPeerReceiveAborted { internal long ErrorCode; } @@ -362,29 +522,33 @@ internal struct StreamEventDataSendShutdownComplete [StructLayout(LayoutKind.Explicit)] internal struct StreamEventDataUnion { + // TODO: missing START_COMPLETE [FieldOffset(0)] - internal StreamEventDataRecv Recv; + internal StreamEventDataReceive Receive; [FieldOffset(0)] internal StreamEventDataSendComplete SendComplete; [FieldOffset(0)] - internal StreamEventDataPeerSendAbort PeerSendAbort; + internal StreamEventDataPeerSendAborted PeerSendAborted; [FieldOffset(0)] - internal StreamEventDataPeerRecvAbort PeerRecvAbort; + internal StreamEventDataPeerReceiveAborted PeerReceiveAborted; [FieldOffset(0)] internal StreamEventDataSendShutdownComplete SendShutdownComplete; + + // TODO: missing IDEAL_SEND_BUFFER_SIZE } [StructLayout(LayoutKind.Sequential)] internal struct StreamEvent { - internal QUIC_STREAM_EVENT Type; + internal QUIC_STREAM_EVENT_TYPE Type; internal StreamEventDataUnion Data; } + // TODO: rename to C#-like [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN { @@ -404,6 +568,7 @@ internal byte[] Address } } + // TODO: rename to C#-like [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR_IN6 { @@ -441,7 +606,8 @@ internal byte[] Address } } - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)] + // TODO: rename to C#-like + [StructLayout(LayoutKind.Explicit)] internal struct SOCKADDR_INET { [FieldOffset(0)] @@ -460,58 +626,53 @@ internal delegate uint StreamCallbackDelegate( [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamOpenDelegate( - IntPtr connection, - uint flags, + SafeMsQuicConnectionHandle connection, + QUIC_STREAM_OPEN_FLAGS flags, StreamCallbackDelegate handler, IntPtr context, - out IntPtr stream); + out SafeMsQuicStreamHandle stream); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamStartDelegate( - IntPtr stream, - uint flags); + SafeMsQuicStreamHandle stream, + QUIC_STREAM_START_FLAGS flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate uint StreamCloseDelegate( + internal delegate void StreamCloseDelegate( IntPtr stream); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamShutdownDelegate( - IntPtr stream, - uint flags, + SafeMsQuicStreamHandle stream, + QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamSendDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, QuicBuffer* buffers, uint bufferCount, - uint flags, + QUIC_SEND_FLAGS flags, IntPtr clientSendContext); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamReceiveCompleteDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, ulong bufferLength); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate uint StreamReceiveSetEnabledDelegate( - IntPtr stream, + SafeMsQuicStreamHandle stream, [MarshalAs(UnmanagedType.U1)] bool enabled); [StructLayout(LayoutKind.Sequential)] - internal unsafe struct QuicBuffer + internal struct QuicBuffer { internal uint Length; internal byte* Buffer; } - [StructLayout(LayoutKind.Sequential)] - internal struct CertFileParams - { - internal IntPtr PrivateKeyFilePath; - internal IntPtr CertificateFilePath; - } + // TODO: DatagramSend missing } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusCodes.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs similarity index 100% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusCodes.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusCodes.cs diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusHelper.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs similarity index 100% rename from src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicStatusHelper.cs rename to src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/MsQuicStatusHelper.cs diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs new file mode 100644 index 0000000000000..24a5b2fa5f921 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConfigurationHandle.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using static System.Net.Quic.Implementations.MsQuic.Internal.MsQuicNativeMethods; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicConfigurationHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicConfigurationHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.ConfigurationCloseDelegate(handle); + return true; + } + + // TODO: consider moving the static code from here to keep all the handle classes small and simple. + public static unsafe SafeMsQuicConfigurationHandle Create(QuicClientConnectionOptions options) + { + // TODO: lots of ClientAuthenticationOptions are not yet supported by MsQuic. + return Create(options, QUIC_CREDENTIAL_FLAGS.CLIENT, certificate: null, options.ClientAuthenticationOptions?.ApplicationProtocols); + } + + public static unsafe SafeMsQuicConfigurationHandle Create(QuicListenerOptions options) + { + // TODO: lots of ServerAuthenticationOptions are not yet supported by MsQuic. + return Create(options, QUIC_CREDENTIAL_FLAGS.NONE, options.ServerAuthenticationOptions?.ServerCertificate, options.ServerAuthenticationOptions?.ApplicationProtocols); + } + + // TODO: this is called from MsQuicListener and when it fails it wreaks havoc in MsQuicListener finalizer. + // Consider moving bigger logic like this outside of constructor call chains. + private static unsafe SafeMsQuicConfigurationHandle Create(QuicOptions options, QUIC_CREDENTIAL_FLAGS flags, X509Certificate? certificate, List? alpnProtocols) + { + // TODO: some of these checks should be done by the QuicOptions type. + if (alpnProtocols == null || alpnProtocols.Count == 0) + { + throw new Exception("At least one SslApplicationProtocol value must be present in SslClientAuthenticationOptions or SslServerAuthenticationOptions."); + } + + if (options.MaxBidirectionalStreams > ushort.MaxValue) + { + throw new Exception("MaxBidirectionalStreams overflow."); + } + + if (options.MaxBidirectionalStreams > ushort.MaxValue) + { + throw new Exception("MaxBidirectionalStreams overflow."); + } + + Debug.Assert(!MsQuicApi.Api.Registration.IsInvalid); + + var settings = new QuicSettings + { + IsSetFlags = QuicSettingsIsSetFlags.PeerBidiStreamCount | + QuicSettingsIsSetFlags.PeerUnidiStreamCount, + PeerBidiStreamCount = (ushort)options.MaxBidirectionalStreams, + PeerUnidiStreamCount = (ushort)options.MaxUnidirectionalStreams + }; + + if (options.IdleTimeout != Timeout.InfiniteTimeSpan) + { + if (options.IdleTimeout <= TimeSpan.Zero) throw new Exception("IdleTimeout must not be negative."); + + ulong ms = (ulong)options.IdleTimeout.Ticks / TimeSpan.TicksPerMillisecond; + if (ms > (1ul << 62) - 1) throw new Exception("IdleTimeout is too large (max 2^62-1 milliseconds)"); + + settings.IsSetFlags |= QuicSettingsIsSetFlags.IdleTimeoutMs; + settings.IdleTimeoutMs = (ulong)options.IdleTimeout.TotalMilliseconds; + } + + uint status; + SafeMsQuicConfigurationHandle? configurationHandle; + + MemoryHandle[]? handles = null; + QuicBuffer[]? buffers = null; + try + { + MsQuicAlpnHelper.Prepare(alpnProtocols, out handles, out buffers); + status = MsQuicApi.Api.ConfigurationOpenDelegate(MsQuicApi.Api.Registration, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)alpnProtocols.Count, ref settings, (uint)sizeof(QuicSettings), context: IntPtr.Zero, out configurationHandle); + } + finally + { + MsQuicAlpnHelper.Return(ref handles, ref buffers); + } + + QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationOpen failed."); + + try + { + // TODO: find out what to do for OpenSSL here -- passing handle won't work, because + // MsQuic has a private copy of OpenSSL so the SSL_CTX will be incompatible. + + CredentialConfig config = default; + + config.Flags = flags; // TODO: consider using LOAD_ASYNCHRONOUS with a callback. + + if (certificate != null) + { +#if true + // If using stub TLS. + config.Type = QUIC_CREDENTIAL_TYPE.STUB_NULL; +#else + // TODO: doesn't work on non-Windows + config.Type = QUIC_CREDENTIAL_TYPE.CONTEXT; + config.Certificate = certificate.Handle; +#endif + } + else + { + // TODO: not allowed for OpenSSL and server + config.Type = QUIC_CREDENTIAL_TYPE.NONE; + } + + status = MsQuicApi.Api.ConfigurationLoadCredentialDelegate(configurationHandle, ref config); + QuicExceptionHelpers.ThrowIfFailed(status, "ConfigurationLoadCredential failed."); + } + catch + { + configurationHandle.Dispose(); + throw; + } + + return configurationHandle; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs new file mode 100644 index 0000000000000..38d908d3adfa9 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicConnectionHandle.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicConnectionHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicConnectionHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } + + public SafeMsQuicConnectionHandle(IntPtr connectionHandle) + : this() + { + SetHandle(connectionHandle); + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.ConnectionCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs new file mode 100644 index 0000000000000..8a22ab8487542 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicListenerHandle.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicListenerHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicListenerHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.ListenerCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs new file mode 100644 index 0000000000000..47a0599c43f0a --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicRegistrationHandle.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicRegistrationHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicRegistrationHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.RegistrationCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs new file mode 100644 index 0000000000000..c9e775b885e8e --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Interop/SafeMsQuicStreamHandle.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Net.Quic.Implementations.MsQuic.Internal +{ + internal sealed class SafeMsQuicStreamHandle : SafeHandle + { + public override bool IsInvalid => handle == IntPtr.Zero; + + private SafeMsQuicStreamHandle() + : base(IntPtr.Zero, ownsHandle: true) + { } + + public SafeMsQuicStreamHandle(IntPtr streamHandle) + : this() + { + SetHandle(streamHandle); + } + + protected override bool ReleaseHandle() + { + MsQuicApi.Api.StreamCloseDelegate(handle); + return true; + } + } +} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs index 9a2063f89f087..87897c1951159 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.IO; using System.Net.Quic.Implementations.MsQuic.Internal; using System.Net.Security; using System.Net.Sockets; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; @@ -18,175 +16,167 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicConnection : QuicConnectionProvider { - private readonly MsQuicSession? _session; - - // Pointer to the underlying connection - // TODO replace all IntPtr with SafeHandles - private IntPtr _ptr; + // Delegate that wraps the static function that will be called when receiving an event. + private static readonly ConnectionCallbackDelegate s_connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); - // Handle to this object for native callbacks. - private GCHandle _handle; + // TODO: remove this. + // This is only used for client-initiated connections, and isn't needed even then once Connect() has been called. + private readonly SafeMsQuicConfigurationHandle? _configuration; - // Delegate that wraps the static function that will be called when receiving an event. - internal static readonly ConnectionCallbackDelegate s_connectionDelegate = new ConnectionCallbackDelegate(NativeCallbackHandler); + private readonly State _state = new State(); + private GCHandle _stateHandle; + private bool _disposed; - // Endpoint to either connect to or the endpoint already accepted. private IPEndPoint? _localEndPoint; private readonly EndPoint _remoteEndPoint; - private SslApplicationProtocol _negotiatedAlpnProtocol; - // TODO: only allocate these when there is an outstanding connect/shutdown. - private readonly TaskCompletionSource _connectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - private readonly TaskCompletionSource _shutdownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private sealed class State + { + public SafeMsQuicConnectionHandle Handle = null!; // set inside of MsQuicConnection ctor. - private bool _disposed; - private bool _connected; - private MsQuicSecurityConfig? _securityConfig; - private long _abortErrorCode = -1; + // These exists to prevent GC of the MsQuicConnection in the middle of an async op (Connect or Shutdown). + public MsQuicConnection? Connection; - // Queue for accepted streams - private readonly Channel _acceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions() - { - SingleReader = true, - SingleWriter = true, - }); + // TODO: only allocate these when there is an outstanding connect/shutdown. + public readonly TaskCompletionSource ConnectTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + public readonly TaskCompletionSource ShutdownTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + public bool Connected; + public long AbortErrorCode = -1; + + // Queue for accepted streams. + // Backlog limit is managed by MsQuic so it can be unbounded here. + public readonly Channel AcceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions() + { + SingleReader = true, + SingleWriter = true, + }); + } // constructor for inbound connections - public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, IntPtr nativeObjPtr, TimeSpan idleTimeout) + public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle) { + _state.Handle = handle; + _state.Connected = true; _localEndPoint = localEndPoint; _remoteEndPoint = remoteEndPoint; - _ptr = nativeObjPtr; - _connected = true; - SetCallbackHandler(); + _stateHandle = GCHandle.Alloc(_state); - SetIdleTimeout(idleTimeout); + try + { + MsQuicApi.Api.SetCallbackHandlerDelegate( + _state.Handle, + s_connectionDelegate, + GCHandle.ToIntPtr(_stateHandle)); + } + catch + { + _stateHandle.Free(); + throw; + } } // constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { - // TODO need to figure out if/how we want to expose sessions - // Creating a session per connection isn't ideal. - _session = new MsQuicSession(); - _ptr = _session.ConnectionOpen(options); _remoteEndPoint = options.RemoteEndPoint!; + _configuration = SafeMsQuicConfigurationHandle.Create(options); - SetCallbackHandler(); - SetIdleTimeout(options.IdleTimeout); - } + _stateHandle = GCHandle.Alloc(_state); + try + { + // this handle is ref counted by MsQuic, so safe to dispose here. + using SafeMsQuicConfigurationHandle config = SafeMsQuicConfigurationHandle.Create(options); - internal override IPEndPoint LocalEndPoint - { - get + uint status = MsQuicApi.Api.ConnectionOpenDelegate( + MsQuicApi.Api.Registration, + s_connectionDelegate, + GCHandle.ToIntPtr(_stateHandle), + out _state.Handle); + + QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); + } + catch { - return new IPEndPoint(_localEndPoint!.Address, _localEndPoint.Port); + _stateHandle.Free(); + throw; } } - internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert, string? certFilePath, string? privateKeyFilePath) - { - _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert, certFilePath, privateKeyFilePath).ConfigureAwait(false); - // TODO this isn't being set correctly - MsQuicParameterHelpers.SetSecurityConfig(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.SEC_CONFIG, _securityConfig!.NativeObjPtr); - } + internal override IPEndPoint? LocalEndPoint => _localEndPoint; internal override EndPoint RemoteEndPoint => _remoteEndPoint; internal override SslApplicationProtocol NegotiatedApplicationProtocol => _negotiatedAlpnProtocol; - internal override bool Connected => _connected; + internal override bool Connected => _state.Connected; - internal uint HandleEvent(ref ConnectionEvent connectionEvent) + private static uint HandleEventConnected(State state, ref ConnectionEvent connectionEvent) { - try + if (!state.Connected) { - switch (connectionEvent.Type) - { - case QUIC_CONNECTION_EVENT.CONNECTED: - return HandleEventConnected(ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_TRANSPORT: - return HandleEventShutdownInitiatedByTransport(ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_INITIATED_BY_PEER: - return HandleEventShutdownInitiatedByPeer(ref connectionEvent); - case QUIC_CONNECTION_EVENT.SHUTDOWN_COMPLETE: - return HandleEventShutdownComplete(ref connectionEvent); - case QUIC_CONNECTION_EVENT.PEER_STREAM_STARTED: - return HandleEventNewStream(ref connectionEvent); - case QUIC_CONNECTION_EVENT.STREAMS_AVAILABLE: - return HandleEventStreamsAvailable(ref connectionEvent); - default: - return MsQuicStatusCodes.Success; - } - } - catch (Exception ex) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Error(this, $"Exception occurred during connection callback: {ex.Message}"); - } + // Connected will already be true for connections accepted from a listener. - // TODO: trigger an exception on any outstanding async calls. + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); - return MsQuicStatusCodes.InternalError; - } - } - - private uint HandleEventConnected(ref ConnectionEvent connectionEvent) - { - if (!_connected) - { - // _connected will already be true for connections accepted from a listener. - - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS); - _localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + Debug.Assert(state.Connection != null); + state.Connection._localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); + state.Connection = null; - SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength); - - _connected = true; - _connectTcs.SetResult(MsQuicStatusCodes.Success); + state.Connected = true; + state.ConnectTcs.SetResult(MsQuicStatusCodes.Success); } return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownInitiatedByTransport(ref ConnectionEvent connectionEvent) + private static uint HandleEventShutdownInitiatedByTransport(State state, ref ConnectionEvent connectionEvent) { - if (!_connected) + if (!state.Connected) { + Debug.Assert(state.Connection != null); + state.Connection = null; + uint hresult = connectionEvent.Data.ShutdownInitiatedByTransport.Status; Exception ex = QuicExceptionHelpers.CreateExceptionForHResult(hresult, "Connection has been shutdown by transport."); - _connectTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(ex)); + state.ConnectTcs.SetException(ExceptionDispatchInfo.SetCurrentStackTrace(ex)); } + state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownInitiatedByPeer(ref ConnectionEvent connectionEvent) + private static uint HandleEventShutdownInitiatedByPeer(State state, ref ConnectionEvent connectionEvent) { - _abortErrorCode = connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; + state.AbortErrorCode = (long)connectionEvent.Data.ShutdownInitiatedByPeer.ErrorCode; + state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownComplete(ref ConnectionEvent connectionEvent) + private static uint HandleEventShutdownComplete(State state, ref ConnectionEvent connectionEvent) { - _shutdownTcs.SetResult(MsQuicStatusCodes.Success); + state.Connection = null; + + state.ShutdownTcs.SetResult(MsQuicStatusCodes.Success); // Stop accepting new streams. - _acceptQueue?.Writer.Complete(); + state.AcceptQueue.Writer.Complete(); return MsQuicStatusCodes.Success; } - private uint HandleEventNewStream(ref ConnectionEvent connectionEvent) + private static uint HandleEventNewStream(State state, ref ConnectionEvent connectionEvent) { - MsQuicStream msQuicStream = new MsQuicStream(this, connectionEvent.StreamFlags, connectionEvent.Data.StreamStarted.Stream, inbound: true); - _acceptQueue.Writer.TryWrite(msQuicStream); + var streamHandle = new SafeMsQuicStreamHandle(connectionEvent.Data.PeerStreamStarted.Stream); + var stream = new MsQuicStream(streamHandle, connectionEvent.Data.PeerStreamStarted.Flags); + + state.AcceptQueue.Writer.TryWrite(stream); return MsQuicStatusCodes.Success; } - private uint HandleEventStreamsAvailable(ref ConnectionEvent connectionEvent) + private static uint HandleEventStreamsAvailable(State state, ref ConnectionEvent connectionEvent) { return MsQuicStatusCodes.Success; } @@ -199,11 +189,11 @@ internal override async ValueTask AcceptStreamAsync(Cancella try { - stream = await _acceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + stream = await _state.AcceptQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); } catch (ChannelClosedException) { - throw _abortErrorCode switch + throw _state.AbortErrorCode switch { -1 => new QuicOperationAbortedException(), // Shutdown initiated by us. long err => new QuicConnectionAbortedException(err) // Shutdown initiated by peer. @@ -216,36 +206,34 @@ internal override async ValueTask AcceptStreamAsync(Cancella internal override QuicStreamProvider OpenUnidirectionalStream() { ThrowIfDisposed(); - - return StreamOpen(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); } internal override QuicStreamProvider OpenBidirectionalStream() { ThrowIfDisposed(); - - return StreamOpen(QUIC_STREAM_OPEN_FLAG.NONE); + return new MsQuicStream(_state.Handle, QUIC_STREAM_OPEN_FLAGS.NONE); } internal override long GetRemoteAvailableUnidirectionalStreamCount() { - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_UNIDI_STREAM_COUNT); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT); } internal override long GetRemoteAvailableBidirectionalStreamCount() { - return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_BIDI_STREAM_COUNT); - } - - private void SetIdleTimeout(TimeSpan timeout) - { - MsQuicParameterHelpers.SetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds); + return MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT); } internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); + if (_configuration is null) + { + throw new Exception($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); + } + (string address, int port) = _remoteEndPoint switch { DnsEndPoint dnsEp => (dnsEp.Host, dnsEp.Port), @@ -253,64 +241,59 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.") }; - // values taken from https://github.com/microsoft/msquic/blob/main/docs/api/ConnectionStart.md - int af = _remoteEndPoint.AddressFamily switch + QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { - AddressFamily.Unspecified => 0, - AddressFamily.InterNetwork => 2, - AddressFamily.InterNetworkV6 => 23, + AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, + AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, + AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; - QuicExceptionHelpers.ThrowIfFailed( - MsQuicApi.Api.ConnectionStartDelegate( - _ptr, - (ushort)af, - address, - (ushort)port), - "Failed to connect to peer."); - - return new ValueTask(_connectTcs.Task); - } - - private MsQuicStream StreamOpen( - QUIC_STREAM_OPEN_FLAG flags) - { - IntPtr streamPtr = IntPtr.Zero; - QuicExceptionHelpers.ThrowIfFailed( - MsQuicApi.Api.StreamOpenDelegate( - _ptr, - (uint)flags, - MsQuicStream.s_streamDelegate, - IntPtr.Zero, - out streamPtr), - "Failed to open stream to peer."); - - return new MsQuicStream(this, flags, streamPtr, inbound: false); - } - - private void SetCallbackHandler() - { - Debug.Assert(!_handle.IsAllocated, "callback handler allocated already"); - _handle = GCHandle.Alloc(this); + _state.Connection = this; + try + { + uint status = MsQuicApi.Api.ConnectionStartDelegate( + _state.Handle, + _configuration, + af, + address, + (ushort)port); + + QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); + } + catch + { + _state.Connection = null; + throw; + } - MsQuicApi.Api.SetCallbackHandlerDelegate( - _ptr, - s_connectionDelegate, - GCHandle.ToIntPtr(_handle)); + return new ValueTask(_state.ConnectTcs.Task); } private ValueTask ShutdownAsync( - QUIC_CONNECTION_SHUTDOWN_FLAG Flags, + QUIC_CONNECTION_SHUTDOWN_FLAGS Flags, long ErrorCode) { - uint status = MsQuicApi.Api.ConnectionShutdownDelegate( - _ptr, - (uint)Flags, - ErrorCode); - QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection."); + Debug.Assert(!_state.ShutdownTcs.Task.IsCompleted); + + // Store the connection into the GCHandle'd state to prevent GC if user calls ShutdownAsync and gets rid of all references to the MsQuicConnection. + Debug.Assert(_state.Connection == null); + _state.Connection = this; + + try + { + MsQuicApi.Api.ConnectionShutdownDelegate( + _state.Handle, + Flags, + ErrorCode); + } + catch + { + _state.Connection = null; + throw; + } - return new ValueTask(_shutdownTcs.Task); + return new ValueTask(_state.ShutdownTcs.Task); } internal void SetNegotiatedAlpn(IntPtr alpn, int alpnLength) @@ -326,11 +309,41 @@ internal void SetNegotiatedAlpn(IntPtr alpn, int alpnLength) private static uint NativeCallbackHandler( IntPtr connection, IntPtr context, - ref ConnectionEvent connectionEventStruct) + ref ConnectionEvent connectionEvent) { - GCHandle handle = GCHandle.FromIntPtr(context); - MsQuicConnection quicConnection = (MsQuicConnection)handle.Target!; - return quicConnection.HandleEvent(ref connectionEventStruct); + var state = (State)GCHandle.FromIntPtr(context).Target!; + + try + { + switch ((QUIC_CONNECTION_EVENT_TYPE)connectionEvent.Type) + { + case QUIC_CONNECTION_EVENT_TYPE.CONNECTED: + return HandleEventConnected(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_TRANSPORT: + return HandleEventShutdownInitiatedByTransport(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_PEER: + return HandleEventShutdownInitiatedByPeer(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_COMPLETE: + return HandleEventShutdownComplete(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT_TYPE.PEER_STREAM_STARTED: + return HandleEventNewStream(state, ref connectionEvent); + case QUIC_CONNECTION_EVENT_TYPE.STREAMS_AVAILABLE: + return HandleEventStreamsAvailable(state, ref connectionEvent); + default: + return MsQuicStatusCodes.Success; + } + } + catch (Exception ex) + { + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Error(state, $"Exception occurred during connection callback: {ex.Message}"); + } + + // TODO: trigger an exception on any outstanding async calls. + + return MsQuicStatusCodes.InternalError; + } } public override void Dispose() @@ -351,20 +364,8 @@ private void Dispose(bool disposing) return; } - if (_ptr != IntPtr.Zero) - { - MsQuicApi.Api.ConnectionCloseDelegate?.Invoke(_ptr); - } - - _ptr = IntPtr.Zero; - - if (disposing) - { - _handle.Free(); - _session?.Dispose(); - _securityConfig?.Dispose(); - } - + _state?.Handle?.Dispose(); + if (_stateHandle.IsAllocated) _stateHandle.Free(); _disposed = true; } @@ -374,7 +375,7 @@ internal override ValueTask CloseAsync(long errorCode, CancellationToken cancell { ThrowIfDisposed(); - return ShutdownAsync(QUIC_CONNECTION_SHUTDOWN_FLAG.NONE, errorCode); + return ShutdownAsync(QUIC_CONNECTION_SHUTDOWN_FLAGS.NONE, errorCode); } private void ThrowIfDisposed() diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs index 169ce5435507a..0b55939205d1c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs @@ -1,7 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; +using System.Buffers; +using System.Collections.Generic; using System.Net.Quic.Implementations.MsQuic.Internal; using System.Net.Security; using System.Runtime.InteropServices; @@ -14,42 +15,58 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicListener : QuicListenerProvider, IDisposable { - // Security configuration for MsQuic - private readonly MsQuicSession _session; + private static readonly ListenerCallbackDelegate s_listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); - // Pointer to the underlying listener - // TODO replace all IntPtr with SafeHandles - private IntPtr _ptr; + private readonly State _state; + private GCHandle _stateHandle; + private volatile bool _disposed; - // Handle to this object for native callbacks. - private GCHandle _handle; + private IPEndPoint _listenEndPoint; + private readonly List _applicationProtocols; - // Delegate that wraps the static function that will be called when receiving an event. - internal static readonly ListenerCallbackDelegate s_listenerDelegate = new ListenerCallbackDelegate(NativeCallbackHandler); + private sealed class State + { + // set immediately in ctor, but we need a GCHandle to State in order to create the handle. + public SafeMsQuicListenerHandle Handle = null!; - // Ssl listening options (ALPN, cert, etc) - private readonly SslServerAuthenticationOptions _sslOptions; + public readonly SafeMsQuicConfigurationHandle ConnectionConfiguration; + public readonly Channel AcceptConnectionQueue; - private QuicListenerOptions _options; - private volatile bool _disposed; - private IPEndPoint _listenEndPoint; - private bool _started; - private readonly Channel _acceptConnectionQueue; + public State(QuicListenerOptions options) + { + ConnectionConfiguration = SafeMsQuicConfigurationHandle.Create(options); + + AcceptConnectionQueue = Channel.CreateBounded(new BoundedChannelOptions(options.ListenBacklog) + { + SingleReader = true, + SingleWriter = true + }); + } + } internal MsQuicListener(QuicListenerOptions options) { - _session = new MsQuicSession(); - _acceptConnectionQueue = Channel.CreateBounded(new BoundedChannelOptions(options.ListenBacklog) - { - SingleReader = true, - SingleWriter = true - }); - - _options = options; - _sslOptions = options.ServerAuthenticationOptions!; + _applicationProtocols = options.ServerAuthenticationOptions!.ApplicationProtocols!; _listenEndPoint = options.ListenEndPoint!; - _ptr = _session.ListenerOpen(options); + _state = new State(options); + _stateHandle = GCHandle.Alloc(_state); + try + { + uint status = MsQuicApi.Api.ListenerOpenDelegate( + MsQuicApi.Api.Registration, + s_listenerDelegate, + GCHandle.ToIntPtr(_stateHandle), + out _state.Handle); + + QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); + } + catch + { + _state.Handle?.Dispose(); + _stateHandle.Free(); + throw; + } } internal override IPEndPoint ListenEndPoint @@ -64,22 +81,14 @@ internal override async ValueTask AcceptConnectionAsync( { ThrowIfDisposed(); - MsQuicConnection connection; - try { - connection = await _acceptConnectionQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); + return await _state.AcceptConnectionQueue.Reader.ReadAsync(cancellationToken).ConfigureAwait(false); } catch (ChannelClosedException) { throw new QuicOperationAbortedException(); } - - await connection.SetSecurityConfigForConnection(_sslOptions.ServerCertificate!, - _options.CertificateFilePath, - _options.PrivateKeyFilePath).ConfigureAwait(false); - - return connection; } public override void Dispose() @@ -101,120 +110,98 @@ private void Dispose(bool disposing) } StopAcceptingConnections(); - - if (_ptr != IntPtr.Zero) - { - MsQuicApi.Api.ListenerStopDelegate(_ptr); - MsQuicApi.Api.ListenerCloseDelegate(_ptr); - } - - _ptr = IntPtr.Zero; - - // TODO this call to session dispose hangs. - //_session.Dispose(); + _state?.Handle?.Dispose(); + if (_stateHandle.IsAllocated) _stateHandle.Free(); + _state?.ConnectionConfiguration?.Dispose(); _disposed = true; } - internal override void Start() + internal override unsafe void Start() { ThrowIfDisposed(); - // protect against double starts. - if (_started) - { - throw new QuicException("Cannot start Listener multiple times"); - } + SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); - _started = true; - SetCallbackHandler(); + uint status; - SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint); + MemoryHandle[]? handles = null; + QuicBuffer[]? buffers = null; + try + { + MsQuicAlpnHelper.Prepare(_applicationProtocols, out handles, out buffers); + status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)_applicationProtocols.Count, ref address); + } + finally + { + MsQuicAlpnHelper.Return(ref handles, ref buffers); + } - QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate( - _ptr, - ref address), - "Failed to start listener."); + QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); - SetListenPort(); + SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); + _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); } internal override void Close() { ThrowIfDisposed(); - - MsQuicApi.Api.ListenerStopDelegate(_ptr); + MsQuicApi.Api.ListenerStopDelegate(_state.Handle); } - private void SetListenPort() + private void StopAcceptingConnections() { - SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS); - - _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress); + // TODO finalizers are called even if the object construction fails. + if (_state != null) + { + _state.AcceptConnectionQueue.Writer.TryComplete(); + } } - internal unsafe uint ListenerCallbackHandler(ref ListenerEvent evt) + private static unsafe uint NativeCallbackHandler( + IntPtr listener, + IntPtr context, + ref ListenerEvent evt) { - try + if (evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) { - switch (evt.Type) - { - case QUIC_LISTENER_EVENT.NEW_CONNECTION: - { - ref NewConnectionInfo connectionInfo = ref *(NewConnectionInfo*)evt.Data.NewConnection.Info; - - IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.LocalAddress); - IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.RemoteAddress); - - MsQuicConnection msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, evt.Data.NewConnection.Connection, _options.IdleTimeout); - msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength); - - _acceptConnectionQueue.Writer.TryWrite(msQuicConnection); - } - // Always pend the new connection to wait for the security config to be resolved - // TODO this doesn't need to be async always - return MsQuicStatusCodes.Pending; - default: - return MsQuicStatusCodes.InternalError; - } + return MsQuicStatusCodes.InternalError; } - catch (Exception ex) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Error(this, $"Exception occurred during connection callback: {ex.Message}"); - } - // TODO: trigger an exception on any outstanding async calls. + State state = (State)GCHandle.FromIntPtr(context).Target!; + SafeMsQuicConnectionHandle? connectionHandle = null; - return MsQuicStatusCodes.InternalError; - } - } + try + { + ref NewConnectionInfo connectionInfo = ref *evt.Data.NewConnection.Info; - private void StopAcceptingConnections() - { - _acceptConnectionQueue.Writer.TryComplete(); - } + IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.LocalAddress); + IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET*)connectionInfo.RemoteAddress); - private static uint NativeCallbackHandler( - IntPtr listener, - IntPtr context, - ref ListenerEvent connectionEventStruct) - { - GCHandle handle = GCHandle.FromIntPtr(context); - MsQuicListener quicListener = (MsQuicListener)handle.Target!; + connectionHandle = new SafeMsQuicConnectionHandle(evt.Data.NewConnection.Connection); - return quicListener.ListenerCallbackHandler(ref connectionEventStruct); - } + uint status = MsQuicApi.Api.ConnectionSetConfigurationDelegate(connectionHandle, state.ConnectionConfiguration); + QuicExceptionHelpers.ThrowIfFailed(status, "ConnectionSetConfiguration failed."); - internal void SetCallbackHandler() - { - Debug.Assert(!_handle.IsAllocated, "listener allocated"); - _handle = GCHandle.Alloc(this); + var msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, connectionHandle); + msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength); - MsQuicApi.Api.SetCallbackHandlerDelegate( - _ptr, - s_listenerDelegate, - GCHandle.ToIntPtr(_handle)); + if (!state.AcceptConnectionQueue.Writer.TryWrite(msQuicConnection)) + { + // This handle will be cleaned up by MsQuic. + connectionHandle.SetHandleAsInvalid(); + msQuicConnection.Dispose(); + return MsQuicStatusCodes.InternalError; + } + + return MsQuicStatusCodes.Success; + } + catch (Exception ex) + { + // This handle will be cleaned up by MsQuic by returning InternalError. + connectionHandle?.SetHandleAsInvalid(); + state.AcceptConnectionQueue.Writer.TryComplete(ex); + return MsQuicStatusCodes.InternalError; + } } private void ThrowIfDisposed() diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs index d98110d15be4e..45476d1a08de4 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicStream.cs @@ -14,46 +14,18 @@ namespace System.Net.Quic.Implementations.MsQuic { internal sealed class MsQuicStream : QuicStreamProvider { - // Pointer to the underlying stream - // TODO replace all IntPtr with SafeHandles - private readonly IntPtr _ptr; - - // Handle to this object for native callbacks. - private GCHandle _handle; - // Delegate that wraps the static function that will be called when receiving an event. internal static readonly StreamCallbackDelegate s_streamDelegate = new StreamCallbackDelegate(NativeCallbackHandler); + private readonly State _state = new State(); + private GCHandle _stateHandle; + // Backing for StreamId private long _streamId = -1; - // Resettable completions to be used for multiple calls to send, start, and shutdown. - private readonly ResettableCompletionSource _sendResettableCompletionSource; - - // Resettable completions to be used for multiple calls to receive. - private readonly ResettableCompletionSource _receiveResettableCompletionSource; - - // Set once writes have been shutdown. - private readonly TaskCompletionSource _shutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - // Buffers to hold during a call to send. - private MemoryHandle[] _bufferArrays = new MemoryHandle[1]; - private QuicBuffer[] _sendQuicBuffers = new QuicBuffer[1]; - - // Handle to hold when sending. - private GCHandle _sendHandle; - // Used to check if StartAsync has been called. private bool _started; - private ReadState _readState; - private long _readErrorCode = -1; - - private ShutdownWriteState _shutdownWriteState; - - private SendState _sendState; - private long _sendErrorCode = -1; - // Used by the class to indicate that the stream is m_Readable. private readonly bool _canRead; @@ -62,35 +34,87 @@ internal sealed class MsQuicStream : QuicStreamProvider private volatile bool _disposed; - private readonly List _receiveQuicBuffers = new List(); + private sealed class State + { + public SafeMsQuicStreamHandle Handle = null!; // set in ctor. + + public ReadState ReadState; + public long ReadErrorCode = -1; + public readonly List ReceiveQuicBuffers = new List(); + + // Resettable completions to be used for multiple calls to receive. + public readonly ResettableCompletionSource ReceiveResettableCompletionSource = new ResettableCompletionSource(); + + public SendState SendState; + public long SendErrorCode = -1; - // TODO consider using Interlocked.Exchange instead of a sync if we can avoid it. - private readonly object _sync = new object(); + // Buffers to hold during a call to send. + public MemoryHandle[] BufferArrays = new MemoryHandle[1]; + public QuicBuffer[] SendQuicBuffers = new QuicBuffer[1]; - // Creates a new MsQuicStream - internal MsQuicStream(MsQuicConnection connection, QUIC_STREAM_OPEN_FLAG flags, IntPtr nativeObjPtr, bool inbound) + // Handle to pinned SendQuicBuffers. + public GCHandle SendHandle; + + // Resettable completions to be used for multiple calls to send, start, and shutdown. + public readonly ResettableCompletionSource SendResettableCompletionSource = new ResettableCompletionSource(); + + public ShutdownWriteState ShutdownWriteState; + + // Set once writes have been shutdown. + public readonly TaskCompletionSource ShutdownWriteCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + } + + // inbound. + internal MsQuicStream(SafeMsQuicStreamHandle streamHandle, QUIC_STREAM_OPEN_FLAGS flags) { - Debug.Assert(connection != null, "Connection null"); + _state.Handle = streamHandle; + _canRead = true; + _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); + _started = true; - _ptr = nativeObjPtr; + _stateHandle = GCHandle.Alloc(_state); + try + { + MsQuicApi.Api.SetCallbackHandlerDelegate( + _state.Handle, + s_streamDelegate, + GCHandle.ToIntPtr(_stateHandle)); + } + catch + { + _stateHandle.Free(); + throw; + } + } - _sendResettableCompletionSource = new ResettableCompletionSource(); - _receiveResettableCompletionSource = new ResettableCompletionSource(); - SetCallbackHandler(); + // outbound. + internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FLAGS flags) + { + Debug.Assert(connection != null); - bool isBidirectional = !flags.HasFlag(QUIC_STREAM_OPEN_FLAG.UNIDIRECTIONAL); + _canRead = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL); + _canWrite = true; - if (inbound) + _stateHandle = GCHandle.Alloc(_state); + try { - _canRead = true; - _canWrite = isBidirectional; - _started = true; + uint status = MsQuicApi.Api.StreamOpenDelegate( + connection, + flags, + s_streamDelegate, + GCHandle.ToIntPtr(_stateHandle), + out _state.Handle); + + QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer."); + + status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.ASYNC); + QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); } - else + catch { - _canRead = isBidirectional; - _canWrite = true; - StartLocalStream(); + _state.Handle?.Dispose(); + _stateHandle.Free(); + throw; } } @@ -129,7 +153,7 @@ internal override async ValueTask WriteAsync(ReadOnlySequence buffers, boo using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - await SendReadOnlySequenceAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); + await SendReadOnlySequenceAsync(buffers, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } @@ -144,8 +168,7 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory ThrowIfDisposed(); using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - - await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); + await SendReadOnlyMemoryListAsync(buffers, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } @@ -156,7 +179,7 @@ internal override async ValueTask WriteAsync(ReadOnlyMemory buffer, bool e using CancellationTokenRegistration registration = await HandleWriteStartState(cancellationToken).ConfigureAwait(false); - await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAG.FIN : QUIC_SEND_FLAG.NONE).ConfigureAwait(false); + await SendReadOnlyMemoryAsync(buffer, endStream ? QUIC_SEND_FLAGS.FIN : QUIC_SEND_FLAGS.NONE).ConfigureAwait(false); HandleWriteCompletedState(); } @@ -168,9 +191,9 @@ private async ValueTask HandleWriteStartState(Can throw new InvalidOperationException(SR.net_quic_writing_notallowed); } - lock (_sync) + lock (_state) { - if (_sendState == SendState.Aborted) + if (_state.SendState == SendState.Aborted) { throw new OperationCanceledException(SR.net_quic_sending_aborted); } @@ -179,25 +202,25 @@ private async ValueTask HandleWriteStartState(Can CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_sendState == SendState.None) + if (_state.SendState == SendState.None) { - _sendState = SendState.Aborted; + _state.SendState = SendState.Aborted; shouldComplete = true; } } if (shouldComplete) { - _sendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled", cancellationToken)); + _state.SendResettableCompletionSource.CompleteException(new OperationCanceledException("Write was canceled", cancellationToken)); } }); // Make sure start has completed if (!_started) { - await _sendResettableCompletionSource.GetTypelessValueTask().ConfigureAwait(false); + await _state.SendResettableCompletionSource.GetTypelessValueTask().ConfigureAwait(false); _started = true; } @@ -206,11 +229,11 @@ private async ValueTask HandleWriteStartState(Can private void HandleWriteCompletedState() { - lock (_sync) + lock (_state) { - if (_sendState == SendState.Finished) + if (_state.SendState == SendState.Finished) { - _sendState = SendState.None; + _state.SendState = SendState.None; } } } @@ -229,15 +252,15 @@ internal override async ValueTask ReadAsync(Memory destination, Cance NetEventSource.Info(this, $"[{GetHashCode()}] reading into Memory of '{destination.Length}' bytes."); } - lock (_sync) + lock (_state) { - if (_readState == ReadState.ReadsCompleted) + if (_state.ReadState == ReadState.ReadsCompleted) { return 0; } - else if (_readState == ReadState.Aborted) + else if (_state.ReadState == ReadState.Aborted) { - throw _readErrorCode switch + throw _state.ReadErrorCode switch { -1 => new QuicOperationAbortedException(), long err => new QuicStreamAbortedException(err) @@ -248,26 +271,26 @@ internal override async ValueTask ReadAsync(Memory destination, Cance using CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_readState == ReadState.None) + if (_state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.Aborted; + _state.ReadState = ReadState.Aborted; } if (shouldComplete) { - _receiveResettableCompletionSource.CompleteException(new OperationCanceledException("Read was canceled", cancellationToken)); + _state.ReceiveResettableCompletionSource.CompleteException(new OperationCanceledException("Read was canceled", cancellationToken)); } }); // TODO there could potentially be a perf gain by storing the buffer from the inital read // This reduces the amount of async calls, however it makes it so MsQuic holds onto the buffers // longer than it needs to. We will need to benchmark this. - int length = (int)await _receiveResettableCompletionSource.GetValueTask().ConfigureAwait(false); + int length = (int)await _state.ReceiveResettableCompletionSource.GetValueTask().ConfigureAwait(false); int actual = Math.Min(length, destination.Length); @@ -288,16 +311,16 @@ static unsafe void CopyToBuffer(Span destinationBuffer, List s } } - CopyToBuffer(destination.Span, _receiveQuicBuffers); + CopyToBuffer(destination.Span, _state.ReceiveQuicBuffers); - lock (_sync) + lock (_state) { - if (_readState == ReadState.IndividualReadComplete) + if (_state.ReadState == ReadState.IndividualReadComplete) { - _receiveQuicBuffers.Clear(); + _state.ReceiveQuicBuffers.Clear(); ReceiveComplete(actual); EnableReceive(); - _readState = ReadState.None; + _state.ReadState = ReadState.None; } } @@ -310,13 +333,12 @@ internal override void AbortRead(long errorCode) { ThrowIfDisposed(); - lock (_sync) + lock (_state) { - _readState = ReadState.Aborted; + _state.ReadState = ReadState.Aborted; } - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_RECV, errorCode); - + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE, errorCode); } internal override void AbortWrite(long errorCode) @@ -325,24 +347,30 @@ internal override void AbortWrite(long errorCode) bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_shutdownWriteState == ShutdownWriteState.None) + if (_state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Canceled; + _state.ShutdownWriteState = ShutdownWriteState.Canceled; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteCompletionSource.SetException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); + _state.ShutdownWriteCompletionSource.SetException(new QuicStreamAbortedException("Shutdown was aborted.", errorCode)); } - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT_SEND, errorCode); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_SEND, errorCode); } - internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) + private void StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode) + { + uint status = MsQuicApi.Api.StreamShutdownDelegate(_state.Handle, flags, errorCode); + QuicExceptionHelpers.ThrowIfFailed(status, "StreamShutdown failed."); + } + + internal override async ValueTask ShutdownWriteCompleted(CancellationToken cancellationToken = default) { ThrowIfDisposed(); @@ -350,29 +378,28 @@ internal override ValueTask ShutdownWriteCompleted(CancellationToken cancellatio using CancellationTokenRegistration registration = cancellationToken.Register(() => { bool shouldComplete = false; - lock (_sync) + lock (_state) { - if (_shutdownWriteState == ShutdownWriteState.None) + if (_state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Canceled; + _state.ShutdownWriteState = ShutdownWriteState.Canceled; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteCompletionSource.SetException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); + _state.ShutdownWriteCompletionSource.SetException(new OperationCanceledException("Shutdown was canceled", cancellationToken)); } }); - return new ValueTask(_shutdownWriteCompletionSource.Task); + await _state.ShutdownWriteCompletionSource.Task.ConfigureAwait(false); } internal override void Shutdown() { ThrowIfDisposed(); - - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } // TODO consider removing sync-over-async with blocking calls. @@ -407,24 +434,9 @@ internal override Task FlushAsync(CancellationToken cancellationToken = default) public override ValueTask DisposeAsync() { - if (_disposed) - { - return default; - } - - if (_ptr != IntPtr.Zero) - { - // TODO resolve graceful vs abortive dispose here. Will file a separate issue. - //MsQuicApi.Api._streamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 1); - MsQuicApi.Api.StreamCloseDelegate?.Invoke(_ptr); - } - - _handle.Free(); - - CleanupSendState(); - - _disposed = true; + // TODO: perform a graceful shutdown and wait for completion? + Dispose(true); return default; } @@ -446,23 +458,16 @@ private void Dispose(bool disposing) return; } - if (_ptr != IntPtr.Zero) - { - // TODO resolve graceful vs abortive dispose here. Will file a separate issue. - //MsQuicApi.Api._streamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.ABORT, 1); - MsQuicApi.Api.StreamCloseDelegate?.Invoke(_ptr); - } - - _handle.Free(); - - CleanupSendState(); + _state.Handle.Dispose(); + if (_stateHandle.IsAllocated) _stateHandle.Free(); + CleanupSendState(_state); _disposed = true; } private void EnableReceive() { - MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_ptr, enabled: true); + MsQuicApi.Api.StreamReceiveSetEnabledDelegate(_state.Handle, enabled: true); } private static uint NativeCallbackHandler( @@ -470,141 +475,108 @@ private static uint NativeCallbackHandler( IntPtr context, ref StreamEvent streamEvent) { - var handle = GCHandle.FromIntPtr(context); - var quicStream = (MsQuicStream)handle.Target!; - - return quicStream.HandleEvent(ref streamEvent); + var state = (State)GCHandle.FromIntPtr(context).Target!; + return HandleEvent(state, ref streamEvent); } - private uint HandleEvent(ref StreamEvent evt) + private static uint HandleEvent(State state, ref StreamEvent evt) { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, $"[{GetHashCode()}] handling event '{evt.Type}'."); - } - - uint status = MsQuicStatusCodes.Success; - try { - switch (evt.Type) + switch ((QUIC_STREAM_EVENT_TYPE)evt.Type) { // Stream has started. // Will only be done for outbound streams (inbound streams have already started) - case QUIC_STREAM_EVENT.START_COMPLETE: - status = HandleStartComplete(); - break; + case QUIC_STREAM_EVENT_TYPE.START_COMPLETE: + return HandleStartComplete(state); // Received data on the stream - case QUIC_STREAM_EVENT.RECEIVE: - { - status = HandleEventRecv(ref evt); - } - break; + case QUIC_STREAM_EVENT_TYPE.RECEIVE: + return HandleEventRecv(state, ref evt); // Send has completed. // Contains a canceled bool to indicate if the send was canceled. - case QUIC_STREAM_EVENT.SEND_COMPLETE: - { - status = HandleEventSendComplete(ref evt); - } - break; + case QUIC_STREAM_EVENT_TYPE.SEND_COMPLETE: + return HandleEventSendComplete(state, ref evt); // Peer has told us to shutdown the reading side of the stream. - case QUIC_STREAM_EVENT.PEER_SEND_SHUTDOWN: - { - status = HandleEventPeerSendShutdown(); - } - break; + case QUIC_STREAM_EVENT_TYPE.PEER_SEND_SHUTDOWN: + return HandleEventPeerSendShutdown(state); // Peer has told us to abort the reading side of the stream. - case QUIC_STREAM_EVENT.PEER_SEND_ABORTED: - { - status = HandleEventPeerSendAborted(ref evt); - } - break; + case QUIC_STREAM_EVENT_TYPE.PEER_SEND_ABORTED: + return HandleEventPeerSendAborted(state, ref evt); // Peer has stopped receiving data, don't send anymore. - case QUIC_STREAM_EVENT.PEER_RECEIVE_ABORTED: - { - status = HandleEventPeerRecvAborted(ref evt); - } - break; + case QUIC_STREAM_EVENT_TYPE.PEER_RECEIVE_ABORTED: + return HandleEventPeerRecvAborted(state, ref evt); // Occurs when shutdown is completed for the send side. // This only happens for shutdown on sending, not receiving // Receive shutdown can only be abortive. - case QUIC_STREAM_EVENT.SEND_SHUTDOWN_COMPLETE: - { - status = HandleEventSendShutdownComplete(ref evt); - } - break; + case QUIC_STREAM_EVENT_TYPE.SEND_SHUTDOWN_COMPLETE: + return HandleEventSendShutdownComplete(state, ref evt); // Shutdown for both sending and receiving is completed. - case QUIC_STREAM_EVENT.SHUTDOWN_COMPLETE: - { - status = HandleEventShutdownComplete(); - } - break; + case QUIC_STREAM_EVENT_TYPE.SHUTDOWN_COMPLETE: + return HandleEventShutdownComplete(state); default: - break; + return MsQuicStatusCodes.Success; } } catch (Exception) { return MsQuicStatusCodes.InternalError; } - - return status; } - private unsafe uint HandleEventRecv(ref MsQuicNativeMethods.StreamEvent evt) + private static unsafe uint HandleEventRecv(State state, ref StreamEvent evt) { - StreamEventDataRecv receieveEvent = evt.Data.Recv; - for (int i = 0; i < receieveEvent.BufferCount; i++) + StreamEventDataReceive receiveEvent = evt.Data.Receive; + for (int i = 0; i < receiveEvent.BufferCount; i++) { - _receiveQuicBuffers.Add(receieveEvent.Buffers[i]); + state.ReceiveQuicBuffers.Add(receiveEvent.Buffers[i]); } bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.IndividualReadComplete; + state.ReadState = ReadState.IndividualReadComplete; } if (shouldComplete) { - _receiveResettableCompletionSource.Complete((uint)receieveEvent.TotalBufferLength); + state.ReceiveResettableCompletionSource.Complete((uint)receiveEvent.TotalBufferLength); } return MsQuicStatusCodes.Pending; } - private uint HandleEventPeerRecvAborted(ref StreamEvent evt) + private static uint HandleEventPeerRecvAborted(State state, ref StreamEvent evt) { bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_sendState == SendState.None) + if (state.SendState == SendState.None) { shouldComplete = true; } - _sendState = SendState.Aborted; - _sendErrorCode = evt.Data.PeerSendAbort.ErrorCode; + state.SendState = SendState.Aborted; + state.SendErrorCode = (long)evt.Data.PeerSendAborted.ErrorCode; } if (shouldComplete) { - _sendResettableCompletionSource.CompleteException(new QuicStreamAbortedException(_sendErrorCode)); + state.SendResettableCompletionSource.CompleteException(new QuicStreamAbortedException(state.SendErrorCode)); } return MsQuicStatusCodes.Success; } - private uint HandleStartComplete() + private static uint HandleStartComplete(State state) { bool shouldComplete = false; - lock (_sync) + lock (state) { // Check send state before completing as send cancellation is shared between start and send. - if (_sendState == SendState.None) + if (state.SendState == SendState.None) { shouldComplete = true; } @@ -612,217 +584,207 @@ private uint HandleStartComplete() if (shouldComplete) { - _sendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + state.SendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); } return MsQuicStatusCodes.Success; } - private uint HandleEventSendShutdownComplete(ref MsQuicNativeMethods.StreamEvent evt) + private static uint HandleEventSendShutdownComplete(State state, ref StreamEvent evt) { bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_shutdownWriteState == ShutdownWriteState.None) + if (state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Finished; + state.ShutdownWriteState = ShutdownWriteState.Finished; shouldComplete = true; } } if (shouldComplete) { - _shutdownWriteCompletionSource.TrySetResult(); + state.ShutdownWriteCompletionSource.TrySetResult(); } return MsQuicStatusCodes.Success; } - private uint HandleEventShutdownComplete() + private static uint HandleEventShutdownComplete(State state) { bool shouldReadComplete = false; bool shouldShutdownWriteComplete = false; - lock (_sync) + lock (state) { // This event won't occur within the middle of a receive. if (NetEventSource.Log.IsEnabled()) NetEventSource.Info("Completing resettable event source."); - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldReadComplete = true; } - _readState = ReadState.ReadsCompleted; + state.ReadState = ReadState.ReadsCompleted; - if (_shutdownWriteState == ShutdownWriteState.None) + if (state.ShutdownWriteState == ShutdownWriteState.None) { - _shutdownWriteState = ShutdownWriteState.Finished; + state.ShutdownWriteState = ShutdownWriteState.Finished; shouldShutdownWriteComplete = true; } } if (shouldReadComplete) { - _receiveResettableCompletionSource.Complete(0); + state.ReceiveResettableCompletionSource.Complete(0); } if (shouldShutdownWriteComplete) { - _shutdownWriteCompletionSource.TrySetResult(); + state.ShutdownWriteCompletionSource.TrySetResult(); } return MsQuicStatusCodes.Success; } - private uint HandleEventPeerSendAborted(ref StreamEvent evt) + private static uint HandleEventPeerSendAborted(State state, ref StreamEvent evt) { bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.Aborted; - _readErrorCode = evt.Data.PeerSendAbort.ErrorCode; + state.ReadState = ReadState.Aborted; + state.ReadErrorCode = (long)evt.Data.PeerSendAborted.ErrorCode; } if (shouldComplete) { - _receiveResettableCompletionSource.CompleteException(new QuicStreamAbortedException(_readErrorCode)); + state.ReceiveResettableCompletionSource.CompleteException(new QuicStreamAbortedException(state.ReadErrorCode)); } return MsQuicStatusCodes.Success; } - private uint HandleEventPeerSendShutdown() + private static uint HandleEventPeerSendShutdown(State state) { bool shouldComplete = false; - lock (_sync) + lock (state) { // This event won't occur within the middle of a receive. if (NetEventSource.Log.IsEnabled()) NetEventSource.Info("Completing resettable event source."); - if (_readState == ReadState.None) + if (state.ReadState == ReadState.None) { shouldComplete = true; } - _readState = ReadState.ReadsCompleted; + state.ReadState = ReadState.ReadsCompleted; } if (shouldComplete) { - _receiveResettableCompletionSource.Complete(0); + state.ReceiveResettableCompletionSource.Complete(0); } return MsQuicStatusCodes.Success; } - private uint HandleEventSendComplete(ref StreamEvent evt) + private static uint HandleEventSendComplete(State state, ref StreamEvent evt) { - CleanupSendState(); + bool complete = false; - // TODO throw if a write was canceled. - - bool shouldComplete = false; - lock (_sync) + lock (state) { - if (_sendState == SendState.None) + if (state.SendState == SendState.None) { - _sendState = SendState.Finished; - shouldComplete = true; + state.SendState = SendState.Finished; + complete = true; } } - if (shouldComplete) + if (complete) { - _sendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); + CleanupSendState(state); + + // TODO throw if a write was canceled. + state.SendResettableCompletionSource.Complete(MsQuicStatusCodes.Success); } return MsQuicStatusCodes.Success; } - private void CleanupSendState() + private static void CleanupSendState(State state) { - if (_sendHandle.IsAllocated) + if (state.SendHandle.IsAllocated) { - _sendHandle.Free(); + state.SendHandle.Free(); } // Callings dispose twice on a memory handle should be okay - foreach (MemoryHandle buffer in _bufferArrays) + foreach (MemoryHandle buffer in state.BufferArrays) { buffer.Dispose(); } } - private void SetCallbackHandler() - { - _handle = GCHandle.Alloc(this); - - MsQuicApi.Api.SetCallbackHandlerDelegate( - _ptr, - s_streamDelegate, - GCHandle.ToIntPtr(_handle)); - } - // TODO prevent overlapping sends or consider supporting it. private unsafe ValueTask SendReadOnlyMemoryAsync( ReadOnlyMemory buffer, - QUIC_SEND_FLAG flags) + QUIC_SEND_FLAGS flags) { if (buffer.IsEmpty) { - if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + if ((flags & QUIC_SEND_FLAGS.FIN) == QUIC_SEND_FLAGS.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } return default; } MemoryHandle handle = buffer.Pin(); - _sendQuicBuffers[0].Length = (uint)buffer.Length; - _sendQuicBuffers[0].Buffer = (byte*)handle.Pointer; + _state.SendQuicBuffers[0].Length = (uint)buffer.Length; + _state.SendQuicBuffers[0].Buffer = (byte*)handle.Pointer; - _bufferArrays[0] = handle; + _state.BufferArrays[0] = handle; - _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + _state.SendHandle = GCHandle.Alloc(_state.SendQuicBuffers, GCHandleType.Pinned); - var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_state.SendQuicBuffers, 0); uint status = MsQuicApi.Api.StreamSendDelegate( - _ptr, + _state.Handle, quicBufferPointer, bufferCount: 1, - (uint)flags, - _ptr); + flags, + IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { - CleanupSendState(); + CleanupSendState(_state); // TODO this may need to be an aborted exception. QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } - return _sendResettableCompletionSource.GetTypelessValueTask(); + return _state.SendResettableCompletionSource.GetTypelessValueTask(); } private unsafe ValueTask SendReadOnlySequenceAsync( ReadOnlySequence buffers, - QUIC_SEND_FLAG flags) + QUIC_SEND_FLAGS flags) { if (buffers.IsEmpty) { - if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + if ((flags & QUIC_SEND_FLAGS.FIN) == QUIC_SEND_FLAGS.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } return default; } @@ -834,10 +796,10 @@ private unsafe ValueTask SendReadOnlySequenceAsync( ++count; } - if (_sendQuicBuffers.Length < count) + if (_state.SendQuicBuffers.Length < count) { - _sendQuicBuffers = new QuicBuffer[count]; - _bufferArrays = new MemoryHandle[count]; + _state.SendQuicBuffers = new QuicBuffer[count]; + _state.BufferArrays = new MemoryHandle[count]; } count = 0; @@ -845,45 +807,45 @@ private unsafe ValueTask SendReadOnlySequenceAsync( foreach (ReadOnlyMemory buffer in buffers) { MemoryHandle handle = buffer.Pin(); - _sendQuicBuffers[count].Length = (uint)buffer.Length; - _sendQuicBuffers[count].Buffer = (byte*)handle.Pointer; - _bufferArrays[count] = handle; + _state.SendQuicBuffers[count].Length = (uint)buffer.Length; + _state.SendQuicBuffers[count].Buffer = (byte*)handle.Pointer; + _state.BufferArrays[count] = handle; ++count; } - _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + _state.SendHandle = GCHandle.Alloc(_state.SendQuicBuffers, GCHandleType.Pinned); - var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_state.SendQuicBuffers, 0); uint status = MsQuicApi.Api.StreamSendDelegate( - _ptr, + _state.Handle, quicBufferPointer, count, - (uint)flags, - _ptr); + flags, + IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { - CleanupSendState(); + CleanupSendState(_state); // TODO this may need to be an aborted exception. QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } - return _sendResettableCompletionSource.GetTypelessValueTask(); + return _state.SendResettableCompletionSource.GetTypelessValueTask(); } private unsafe ValueTask SendReadOnlyMemoryListAsync( ReadOnlyMemory> buffers, - QUIC_SEND_FLAG flags) + QUIC_SEND_FLAGS flags) { if (buffers.IsEmpty) { - if ((flags & QUIC_SEND_FLAG.FIN) == QUIC_SEND_FLAG.FIN) + if ((flags & QUIC_SEND_FLAGS.FIN) == QUIC_SEND_FLAGS.FIN) { // Start graceful shutdown sequence if passed in the fin flag and there is an empty buffer. - MsQuicApi.Api.StreamShutdownDelegate(_ptr, (uint)QUIC_STREAM_SHUTDOWN_FLAG.GRACEFUL, errorCode: 0); + StartShutdown(QUIC_STREAM_SHUTDOWN_FLAGS.GRACEFUL, errorCode: 0); } return default; } @@ -892,67 +854,54 @@ private unsafe ValueTask SendReadOnlyMemoryListAsync( uint length = (uint)array.Length; - if (_sendQuicBuffers.Length < length) + if (_state.SendQuicBuffers.Length < length) { - _sendQuicBuffers = new QuicBuffer[length]; - _bufferArrays = new MemoryHandle[length]; + _state.SendQuicBuffers = new QuicBuffer[length]; + _state.BufferArrays = new MemoryHandle[length]; } for (int i = 0; i < length; i++) { ReadOnlyMemory buffer = array[i]; MemoryHandle handle = buffer.Pin(); - _sendQuicBuffers[i].Length = (uint)buffer.Length; - _sendQuicBuffers[i].Buffer = (byte*)handle.Pointer; - _bufferArrays[i] = handle; + _state.SendQuicBuffers[i].Length = (uint)buffer.Length; + _state.SendQuicBuffers[i].Buffer = (byte*)handle.Pointer; + _state.BufferArrays[i] = handle; } - _sendHandle = GCHandle.Alloc(_sendQuicBuffers, GCHandleType.Pinned); + _state.SendHandle = GCHandle.Alloc(_state.SendQuicBuffers, GCHandleType.Pinned); - var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_sendQuicBuffers, 0); + var quicBufferPointer = (QuicBuffer*)Marshal.UnsafeAddrOfPinnedArrayElement(_state.SendQuicBuffers, 0); uint status = MsQuicApi.Api.StreamSendDelegate( - _ptr, + _state.Handle, quicBufferPointer, length, - (uint)flags, - _ptr); + flags, + IntPtr.Zero); if (!MsQuicStatusHelper.SuccessfulStatusCode(status)) { - CleanupSendState(); + CleanupSendState(_state); // TODO this may need to be an aborted exception. QuicExceptionHelpers.ThrowIfFailed(status, "Could not send data to peer."); } - return _sendResettableCompletionSource.GetTypelessValueTask(); - } - - /// - /// Assigns a stream ID and begins process the stream. - /// - private void StartLocalStream() - { - Debug.Assert(!_started, "start local stream"); - uint status = MsQuicApi.Api.StreamStartDelegate( - _ptr, - (uint)QUIC_STREAM_START_FLAG.ASYNC); - - QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream."); + return _state.SendResettableCompletionSource.GetTypelessValueTask(); } private void ReceiveComplete(int bufferLength) { - uint status = MsQuicApi.Api.StreamReceiveCompleteDelegate(_ptr, (ulong)bufferLength); + uint status = MsQuicApi.Api.StreamReceiveCompleteDelegate(_state.Handle, (ulong)bufferLength); QuicExceptionHelpers.ThrowIfFailed(status, "Could not complete receive call."); } // This can fail if the stream isn't started. private long GetStreamId() { - return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); + return (long)MsQuicParameterHelpers.GetULongParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.STREAM, (uint)QUIC_PARAM_STREAM.ID); } private void ThrowIfDisposed() @@ -971,7 +920,7 @@ private enum ReadState None, /// - /// Data is available in . + /// Data is available in . /// IndividualReadComplete, diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs index 7febd47be1ee1..9425833413589 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs @@ -10,7 +10,7 @@ internal abstract class QuicConnectionProvider : IDisposable { internal abstract bool Connected { get; } - internal abstract IPEndPoint LocalEndPoint { get; } + internal abstract IPEndPoint? LocalEndPoint { get; } internal abstract EndPoint RemoteEndPoint { get; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/Interop.MsQuic.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/Interop.MsQuic.cs deleted file mode 100644 index b873b789f1d22..0000000000000 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/Interop.MsQuic.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Net.Quic.Implementations.MsQuic.Internal; -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static class MsQuic - { - [DllImport(Libraries.MsQuic, CallingConvention = CallingConvention.Cdecl)] - internal static unsafe extern uint MsQuicOpen(out MsQuicNativeMethods.NativeApi* registration); - } -} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicEnums.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicEnums.cs deleted file mode 100644 index 5fc281c8670d4..0000000000000 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Interop/MsQuicEnums.cs +++ /dev/null @@ -1,193 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Quic.Implementations.MsQuic.Internal -{ - internal enum QUIC_EXECUTION_PROFILE : uint - { - QUIC_EXECUTION_PROFILE_LOW_LATENCY, // Default - QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT, - QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER, - QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME - } - - /// - /// Flags to pass when creating a security config. - /// - [Flags] - internal enum QUIC_SEC_CONFIG_FLAG : uint - { - CERT_HASH = 0x00000001, - CERT_HASH_STORE = 0x00000002, - CERT_CONTEXT = 0x00000004, - CERT_FILE = 0x00000008, - ENABL_OCSP = 0x00000010, - CERT_NULL = 0xF0000000 // TODO: only valid for stub TLS. - } - - [Flags] - internal enum QUIC_CONNECTION_SHUTDOWN_FLAG : uint - { - NONE = 0x0, - SILENT = 0x1 - } - - [Flags] - internal enum QUIC_STREAM_OPEN_FLAG : uint - { - NONE = 0, - UNIDIRECTIONAL = 0x1, - ZERO_RTT = 0x2, - } - - [Flags] - internal enum QUIC_STREAM_START_FLAG : uint - { - NONE = 0, - FAIL_BLOCKED = 0x1, - IMMEDIATE = 0x2, - ASYNC = 0x4, - } - - [Flags] - internal enum QUIC_STREAM_SHUTDOWN_FLAG : uint - { - NONE = 0, - GRACEFUL = 0x1, - ABORT_SEND = 0x2, - ABORT_RECV = 0x4, - ABORT = ABORT_SEND | ABORT_RECV, - IMMEDIATE = 0x8 - } - - [Flags] - internal enum QUIC_RECEIVE_FLAG : uint - { - NONE = 0, - ZERO_RTT = 0x1, - FIN = 0x02 - } - - [Flags] - internal enum QUIC_SEND_FLAG : uint - { - NONE = 0, - ALLOW_0_RTT = 0x00000001, - FIN = 0x00000002, - DGRAM_PRIORITY = 0x00000004 - } - - internal enum QUIC_PARAM_LEVEL : uint - { - GLOBAL, - REGISTRATION, - SESSION, - LISTENER, - CONNECTION, - TLS, - STREAM - } - - internal enum QUIC_PARAM_GLOBAL : uint - { - RETRY_MEMORY_PERCENT = 0, - SUPPORTED_VERSIONS = 1, - LOAD_BALANCING_MODE = 2, - } - - internal enum QUIC_PARAM_REGISTRATION : uint - { - CID_PREFIX = 0 - } - - internal enum QUIC_PARAM_SESSION : uint - { - TLS_TICKET_KEY = 0, - PEER_BIDI_STREAM_COUNT = 1, - PEER_UNIDI_STREAM_COUNT = 2, - IDLE_TIMEOUT = 3, - DISCONNECT_TIMEOUT = 4, - MAX_BYTES_PER_KEY = 5, - MIGRATION_ENABLED = 6, - DATAGRAM_RECEIVE_ENABLED = 7, - SERVER_RESUMPTION_LEVEL = 8 - } - - internal enum QUIC_PARAM_LISTENER : uint - { - LOCAL_ADDRESS = 0, - STATS = 1 - } - - internal enum QUIC_PARAM_CONN : uint - { - QUIC_VERSION = 0, - LOCAL_ADDRESS = 1, - REMOTE_ADDRESS = 2, - IDLE_TIMEOUT = 3, - PEER_BIDI_STREAM_COUNT = 4, - PEER_UNIDI_STREAM_COUNT = 5, - LOCAL_BIDI_STREAM_COUNT = 6, - LOCAL_UNIDI_STREAM_COUNT = 7, - CLOSE_REASON_PHRASE = 8, - STATISTICS = 9, - STATISTICS_PLAT = 10, - CERT_VALIDATION_FLAGS = 11, - KEEP_ALIVE = 12, - DISCONNECT_TIMEOUT = 13, - SEC_CONFIG = 14, - SEND_BUFFERING = 15, - SEND_PACING = 16, - SHARE_UDP_BINDING = 17, - IDEAL_PROCESSOR = 18, - MAX_STREAM_IDS = 19, - STREAM_SCHEDULING_SCHEME = 20, - DATAGRAM_RECEIVE_ENABLED = 21, - DATAGRAM_SEND_ENABLED = 22, - DISABLE_1RTT_ENCRYPTION = 23 - } - - internal enum QUIC_PARAM_STREAM : uint - { - ID = 0, - ZERORTT_LENGTH = 1, - IDEAL_SEND_BUFFER = 2 - } - - internal enum QUIC_LISTENER_EVENT : uint - { - NEW_CONNECTION = 0 - } - - internal enum QUIC_CONNECTION_EVENT : uint - { - CONNECTED = 0, - SHUTDOWN_INITIATED_BY_TRANSPORT = 1, - SHUTDOWN_INITIATED_BY_PEER = 2, - SHUTDOWN_COMPLETE = 3, - LOCAL_ADDRESS_CHANGED = 4, - PEER_ADDRESS_CHANGED = 5, - PEER_STREAM_STARTED = 6, - STREAMS_AVAILABLE = 7, - PEER_NEEDS_STREAMS = 8, - IDEAL_PROCESSOR_CHANGED = 9, - DATAGRAM_STATE_CHANGED = 10, - DATAGRAM_RECEIVED = 11, - DATAGRAM_SEND_STATE_CHANGED = 12, - RESUMED = 13, - RESUMPTION_TICKET_RECEIVED = 14 - } - - internal enum QUIC_STREAM_EVENT : uint - { - START_COMPLETE = 0, - RECEIVE = 1, - SEND_COMPLETE = 2, - PEER_SEND_SHUTDOWN = 3, - PEER_SEND_ABORTED = 4, - PEER_RECEIVE_ABORTED = 5, - SEND_SHUTDOWN_COMPLETE = 6, - SHUTDOWN_COMPLETE = 7, - IDEAL_SEND_BUFFER_SIZE = 8, - } -} diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs index cc5691b1cc72b..4b2ce8fcb84de 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicClientConnectionOptions.cs @@ -8,7 +8,7 @@ namespace System.Net.Quic /// /// Options to provide to the when connecting to a Listener. /// - public class QuicClientConnectionOptions + public class QuicClientConnectionOptions : QuicOptions { /// /// Client authentication options to use when establishing a . @@ -25,25 +25,9 @@ public class QuicClientConnectionOptions /// public EndPoint? RemoteEndPoint { get; set; } - /// - /// Limit on the number of bidirectional streams the peer connection can create - /// on an accepted connection. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxBidirectionalStreams { get; set; } = 100; - - /// - /// Limit on the number of unidirectional streams the peer connection can create - /// on an accepted connection. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxUnidirectionalStreams { get; set; } = 100; - - /// - /// Idle timeout for connections, after which the connection will be closed. - /// - public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(2); + public QuicClientConnectionOptions() + { + IdleTimeout = TimeSpan.FromTicks(2 * TimeSpan.TicksPerMinute); + } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs index fbd26f70002c4..ffe9196889a4c 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs @@ -55,7 +55,7 @@ internal QuicConnection(QuicConnectionProvider provider) /// public bool Connected => _provider.Connected; - public IPEndPoint LocalEndPoint => _provider.LocalEndPoint; + public IPEndPoint? LocalEndPoint => _provider.LocalEndPoint; public EndPoint RemoteEndPoint => _provider.RemoteEndPoint; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs index e465bc2b631a3..991a6b5925b34 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListener.cs @@ -33,7 +33,7 @@ public QuicListener(QuicListenerOptions options) // !!! TEMPORARY: Remove or make internal before shipping public QuicListener(QuicImplementationProvider implementationProvider, IPEndPoint listenEndPoint, SslServerAuthenticationOptions sslServerAuthenticationOptions) - : this(implementationProvider, new QuicListenerOptions() { ListenEndPoint = listenEndPoint, ServerAuthenticationOptions = sslServerAuthenticationOptions }) + : this(implementationProvider, new QuicListenerOptions() { ListenEndPoint = listenEndPoint, ServerAuthenticationOptions = sslServerAuthenticationOptions }) { } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs index 6e861b39ca815..b20a8b71285e2 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs @@ -8,23 +8,13 @@ namespace System.Net.Quic /// /// Options to provide to the . /// - public class QuicListenerOptions + public class QuicListenerOptions : QuicOptions { /// /// Server Ssl options to use for ALPN, SNI, etc. /// public SslServerAuthenticationOptions? ServerAuthenticationOptions { get; set; } - /// - /// Optional path to certificate file to configure the security configuration. - /// - public string? CertificateFilePath { get; set; } - - /// - /// Optional path to private key file to configure the security configuration. - /// - public string? PrivateKeyFilePath { get; set; } - /// /// The endpoint to listen on. /// @@ -35,24 +25,9 @@ public class QuicListenerOptions /// public int ListenBacklog { get; set; } = 512; - /// - /// Limit on the number of bidirectional streams an accepted connection can create - /// back to the client. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxBidirectionalStreams { get; set; } = 100; - - /// - /// Limit on the number of unidirectional streams the peer connection can create. - /// Default is 100. - /// - // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. - public long MaxUnidirectionalStreams { get; set; } = 100; - - /// - /// Idle timeout for connections, afterwhich the connection will be closed. - /// - public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(10); + public QuicListenerOptions() + { + IdleTimeout = TimeSpan.FromTicks(10 * TimeSpan.TicksPerMinute); + } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs new file mode 100644 index 0000000000000..86dd644aaac1c --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicOptions.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace System.Net.Quic +{ + /// + /// Options for QUIC + /// + public class QuicOptions + { + /// + /// Limit on the number of bidirectional streams the remote peer connection can create on an open connection. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxBidirectionalStreams { get; set; } = 100; + + /// + /// Limit on the number of unidirectional streams the remote peer connection can create on an open connection. + /// Default is 100. + /// + // TODO consider constraining these limits to 0 to whatever the max of the QUIC library we are using. + public long MaxUnidirectionalStreams { get; set; } = 100; + + /// + /// Idle timeout for connections, after which the connection will be closed. + /// + public TimeSpan IdleTimeout { get; set; } + } +} diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs index 98e8438a7afda..2178ec12c46fd 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTestBase.cs @@ -7,13 +7,16 @@ namespace System.Net.Quic.Tests { + // TODO: why do we hawe 2 base clase with some duplicated methods? public class MsQuicTestBase { public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { return new SslServerAuthenticationOptions() { - ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") } + ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, + // TODO: use a cert. MsQuic currently only allows certs that are trusted. + ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() }; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index 8ad870ad82bea..4f12d6c6830dc 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -5,14 +5,13 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Net.Security; using System.Text; using System.Threading.Tasks; using Xunit; namespace System.Net.Quic.Tests { - [ConditionalClass(typeof(MsQuicTests), nameof(MsQuicTests.IsMsQuicSupported))] + [ConditionalClass(typeof(MsQuicTests), nameof(IsMsQuicSupported))] public class MsQuicTests : MsQuicTestBase { public static bool IsMsQuicSupported => QuicImplementationProviders.MsQuic.IsSupported; @@ -50,14 +49,14 @@ public async Task UnidirectionalAndBidirectionalChangeValues() ValueTask clientTask = clientConnection.ConnectAsync(); using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); await clientTask; - Assert.Equal(20, clientConnection.GetRemoteAvailableUnidirectionalStreamCount()); - Assert.Equal(10, clientConnection.GetRemoteAvailableBidirectionalStreamCount()); - Assert.Equal(100, serverConnection.GetRemoteAvailableBidirectionalStreamCount()); - Assert.Equal(100, serverConnection.GetRemoteAvailableUnidirectionalStreamCount()); + Assert.Equal(100, clientConnection.GetRemoteAvailableBidirectionalStreamCount()); + Assert.Equal(100, clientConnection.GetRemoteAvailableUnidirectionalStreamCount()); + Assert.Equal(10, serverConnection.GetRemoteAvailableBidirectionalStreamCount()); + Assert.Equal(20, serverConnection.GetRemoteAvailableUnidirectionalStreamCount()); } [Fact] - [OuterLoop("May take serveral seconds")] + [OuterLoop("May take several seconds")] public async Task SetListenerTimeoutWorksWithSmallTimeout() { var quicOptions = new QuicListenerOptions(); @@ -82,6 +81,7 @@ public async Task SetListenerTimeoutWorksWithSmallTimeout() await Assert.ThrowsAsync(async () => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100))); } + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] [Theory] [MemberData(nameof(WriteData))] public async Task WriteTests(int[][] writes, WriteType writeType) diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index ab4a96132e70d..3d82ba8fa1d2d 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -17,8 +17,6 @@ public abstract class QuicConnectionTests : QuicTestBase public async Task TestConnect() { using QuicListener listener = CreateQuicListener(); - - listener.Start(); IPEndPoint listenEndPoint = listener.ListenEndPoint; using QuicConnection clientConnection = CreateQuicConnection(listenEndPoint); diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 5fc894bf82f6b..94984496af6af 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -35,10 +35,64 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest [ActiveIssue("https://github.com/dotnet/runtime/issues/756")] public override Task Parallel_ReadWriteMultipleStreamsConcurrently() => base.Parallel_ReadWriteMultipleStreamsConcurrently(); + // TODO: new additions, find out the actual reason for hanging + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadTimeout_Expires_Throws() => base.ReadTimeout_Expires_Throws(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ConcurrentBidirectionalReadsWrites_Success() => base.ConcurrentBidirectionalReadsWrites_Success(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ArgumentValidation_ThrowsExpectedException() => base.ArgumentValidation_ThrowsExpectedException(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException() => base.ReadWriteAsync_PrecanceledOperations_ThrowsCancellationException(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task Read_DataStoredAtDesiredOffset(ReadWriteMode mode) => base.Read_DataStoredAtDesiredOffset(mode); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads() => base.ReadAsync_CancelPendingRead_DoesntImpactSubsequentReads(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task Disposed_ThrowsObjectDisposedException() => base.Disposed_ThrowsObjectDisposedException(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task Timeout_Roundtrips() => base.Timeout_Roundtrips(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ZeroByteWrite_OtherDataReceivedSuccessfully(ReadWriteMode mode) => base.ZeroByteWrite_OtherDataReceivedSuccessfully(mode); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadAsync_ContinuesOnCurrentTaskSchedulerIfDesired(bool flowExecutionContext, bool? continueOnCapturedContext) => base.ReadAsync_ContinuesOnCurrentTaskSchedulerIfDesired(flowExecutionContext, continueOnCapturedContext); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ZeroByteRead_BlocksUntilDataAvailableOrNops(ReadWriteMode mode) => base.ZeroByteRead_BlocksUntilDataAvailableOrNops(mode); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadAsync_CancelPendingTask_ThrowsCancellationException() => base.ReadAsync_CancelPendingTask_ThrowsCancellationException(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadAsync_ContinuesOnCurrentSynchronizationContextIfDesired(bool flowExecutionContext, bool? continueOnCapturedContext) => base.ReadAsync_ContinuesOnCurrentSynchronizationContextIfDesired(flowExecutionContext, continueOnCapturedContext); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadWriteByte_Success() => base.ReadWriteByte_Success(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadWrite_Success(ReadWriteMode mode, int writeSize, bool startWithFlush) => base.ReadWrite_Success(mode, writeSize, startWithFlush); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadWrite_Success_Large(ReadWriteMode mode, int writeSize, bool startWithFlush) => base.ReadWrite_Success_Large(mode, writeSize, startWithFlush); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task Flush_ValidOnWriteableStreamWithNoData_Success() => base.Flush_ValidOnWriteableStreamWithNoData_Success(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadAsync_CancelPendingValueTask_ThrowsCancellationException() => base.ReadAsync_CancelPendingValueTask_ThrowsCancellationException(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadAsync_DuringReadAsync_ThrowsIfUnsupported() => base.ReadAsync_DuringReadAsync_ThrowsIfUnsupported(); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task ReadWrite_CustomMemoryManager_Success(bool useAsync) => base.ReadWrite_CustomMemoryManager_Success(useAsync); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] + public override Task Flush_ValidOnReadableStream_Success() => base.Flush_ValidOnReadableStream_Success(); + } public abstract class QuicStreamConformanceTests : ConnectedStreamConformanceTests { + public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() + { + return new SslServerAuthenticationOptions() + { + ApplicationProtocols = new List() { new SslApplicationProtocol("quictest") }, + // TODO: use a cert. MsQuic currently only allows certs that are trusted. + ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() + }; + } + protected abstract QuicImplementationProvider Provider { get; } protected override async Task CreateConnectedStreamsAsync() @@ -49,7 +103,7 @@ protected override async Task CreateConnectedStreamsAsync() var listener = new QuicListener( provider, new IPEndPoint(IPAddress.Loopback, 0), - new SslServerAuthenticationOptions { ApplicationProtocols = new List { protocol } }); + GetSslServerAuthenticationOptions()); listener.Start(); QuicConnection connection1 = null, connection2 = null; diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index 01cf73a6bc316..4944ce22d9c23 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -16,6 +16,7 @@ public abstract class QuicStreamTests : QuicTestBase { private static ReadOnlyMemory s_data = Encoding.UTF8.GetBytes("Hello world!"); + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] [Fact] public async Task BasicTest() { @@ -63,6 +64,7 @@ public async Task BasicTest() } } + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] [Fact] public async Task MultipleReadsAndWrites() { @@ -228,6 +230,7 @@ public async Task GetStreamIdWithoutStartWorks() Assert.Equal(0, clientStream.StreamId); } + [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] [Fact] public async Task LargeDataSentAndReceived() { @@ -432,7 +435,7 @@ private static async Task SendAndReceiveEOFAsync(QuicStream s1, QuicStream s2) public async Task ReadWrite_Random_Success(int readSize, int writeSize) { byte[] testBuffer = new byte[8192]; - new Random().NextBytes(testBuffer); + Random.Shared.NextBytes(testBuffer); await RunClientServer( async clientConnection => @@ -468,7 +471,8 @@ await RunClientServer( totalBytesRead += bytesRead; } - Assert.True(receiveBuffer.AsSpan().SequenceEqual(testBuffer)); + Assert.Equal(testBuffer.Length, receiveBuffer.Length); + Assert.Equal(testBuffer, receiveBuffer); }); } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index e5fc3779f3b9a..6fbcc41bdc6d5 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -22,7 +22,8 @@ public SslServerAuthenticationOptions GetSslServerAuthenticationOptions() { return new SslServerAuthenticationOptions() { - ApplicationProtocols = new List() { ApplicationProtocol } + ApplicationProtocols = new List() { ApplicationProtocol }, + ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate() }; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj index fc7d6fb1986b1..504bc9d62f9f9 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/System.Net.Quic.Functional.Tests.csproj @@ -8,7 +8,8 @@ - + + @@ -18,4 +19,7 @@ + + + diff --git a/src/libraries/System.Net.Requests/src/System/Net/CommandStream.cs b/src/libraries/System.Net.Requests/src/System/Net/CommandStream.cs index 0031514161017..41f22087d5a8a 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/CommandStream.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/CommandStream.cs @@ -373,7 +373,7 @@ internal enum PipelineEntryFlags DontLogParameter = 0x8 } - internal class PipelineEntry + internal sealed class PipelineEntry { internal PipelineEntry(string command) { @@ -678,7 +678,7 @@ private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead) /// /// Contains the parsed status line from the server /// - internal class ResponseDescription + internal sealed class ResponseDescription { internal const int NoStatus = -1; internal bool Multiline; @@ -699,7 +699,7 @@ internal class ResponseDescription /// /// State information that is used during ReceiveCommandResponse()'s async operations /// - internal class ReceiveState + internal sealed class ReceiveState { private const int bufferSize = 1024; diff --git a/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs b/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs index a2dc4cf852828..31b3dec5497c5 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs @@ -24,7 +24,7 @@ internal enum FtpLoginState : byte /// This means basic command sending and parsing. /// /// - internal class FtpControlStream : CommandStream + internal sealed class FtpControlStream : CommandStream { private Socket? _dataSocket; private IPEndPoint? _passiveEndPoint; @@ -500,7 +500,7 @@ protected override PipelineEntry[] BuildCommandsList(WebRequest req) if (domainUserName.Length == 0 && password.Length == 0) { domainUserName = "anonymous"; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Anonymous FTP credential in production code.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Anonymous FTP credential in production code.")] password = "anonymous@"; } @@ -1129,7 +1129,7 @@ private string FormatFtpCommand(string command, string? parameter) /// This will handle either connecting to a port or listening for one /// /// - protected Socket CreateFtpDataSocket(FtpWebRequest request, Socket templateSocket) + private Socket CreateFtpDataSocket(FtpWebRequest request, Socket templateSocket) { // Safe to be called under an Assert. Socket socket = new Socket(templateSocket.AddressFamily, templateSocket.SocketType, templateSocket.ProtocolType); diff --git a/src/libraries/System.Net.Requests/src/System/Net/FtpDataStream.cs b/src/libraries/System.Net.Requests/src/System/Net/FtpDataStream.cs index 10fafe3c0b5b1..fb86000f41774 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/FtpDataStream.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/FtpDataStream.cs @@ -12,7 +12,7 @@ namespace System.Net /// The FtpDataStream class implements the FTP data connection. /// /// - internal class FtpDataStream : Stream, ICloseEx + internal sealed class FtpDataStream : Stream, ICloseEx { private readonly FtpWebRequest _request; private readonly NetworkStream _networkStream; diff --git a/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs b/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs index b1f9f4a7c1c29..93f3b2169ab08 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs @@ -47,7 +47,7 @@ internal enum FtpMethodFlags MustChangeWorkingDirectoryToPath = 0x100 } - internal class FtpMethodInfo + internal sealed class FtpMethodInfo { internal string Method; internal FtpOperation Operation; @@ -221,7 +221,7 @@ public sealed class FtpWebRequest : WebRequest private LazyAsyncResult? _readAsyncResult; private LazyAsyncResult? _requestCompleteAsyncResult; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Anonymous FTP credential in production code.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Anonymous FTP credential in production code.")] private static readonly NetworkCredential s_defaultFtpNetworkCredential = new NetworkCredential("anonymous", "anonymous@", string.Empty); private const int s_DefaultTimeout = 100000; // 100 seconds private static readonly TimerThread.Queue s_DefaultTimerQueue = TimerThread.GetOrCreateQueue(s_DefaultTimeout); @@ -655,7 +655,7 @@ public override IAsyncResult BeginGetResponse(AsyncCallback? callback, object? s lock (_syncObject) { if (_requestStage >= RequestStage.ReadReady) - asyncResult = null; ; + asyncResult = null; } } @@ -1754,7 +1754,7 @@ internal void DataStreamClosed(CloseExState closeState) // // Class used by the WebRequest.Create factory to create FTP requests // - internal class FtpWebRequestCreator : IWebRequestCreate + internal sealed class FtpWebRequestCreator : IWebRequestCreate { internal FtpWebRequestCreator() { diff --git a/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs b/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs index ff9527f9bfed5..c79dc626fbccd 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/HttpWebRequest.cs @@ -96,7 +96,7 @@ private enum Booleans : uint Default = AllowAutoRedirect | AllowWriteStreamBuffering | ExpectContinue } - private class HttpClientParameters + private sealed class HttpClientParameters { public readonly bool Async; public readonly DecompressionMethods AutomaticDecompression; diff --git a/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs b/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs index 3e62fa9c921ad..da9c32abc3633 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/TimerThread.cs @@ -161,7 +161,7 @@ internal static Queue GetOrCreateQueue(int durationMilliseconds) /// /// Represents a queue of timers of fixed duration. /// - private class TimerQueue : Queue + private sealed class TimerQueue : Queue { // This is a GCHandle that holds onto the TimerQueue when active timers are in it. // The TimerThread only holds WeakReferences to it so that it can be collected when the user lets go of it. @@ -269,7 +269,7 @@ internal bool Fire(out int nextExpiration) /// /// A special dummy implementation for a queue of timers of infinite duration. /// - private class InfiniteTimerQueue : Queue + private sealed class InfiniteTimerQueue : Queue { internal InfiniteTimerQueue() : base(Timeout.Infinite) { } @@ -282,7 +282,7 @@ internal InfiniteTimerQueue() : base(Timeout.Infinite) { } /// /// Internal representation of an individual timer. /// - private class TimerNode : Timer + private sealed class TimerNode : Timer { private TimerState _timerState; private Callback? _callback; @@ -444,7 +444,7 @@ internal bool Fire() /// /// A dummy infinite timer. /// - private class InfiniteTimer : Timer + private sealed class InfiniteTimer : Timer { internal InfiniteTimer() : base(Timeout.Infinite) { } diff --git a/src/libraries/System.Net.Requests/src/System/Net/WebRequest.cs b/src/libraries/System.Net.Requests/src/System/Net/WebRequest.cs index bcdce17c44bf8..0614718c2b95c 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/WebRequest.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/WebRequest.cs @@ -16,7 +16,7 @@ namespace System.Net { public abstract class WebRequest : MarshalByRefObject, ISerializable { - internal class WebRequestPrefixElement + internal sealed class WebRequestPrefixElement { public readonly string Prefix; public readonly IWebRequestCreate Creator; @@ -324,7 +324,7 @@ public static bool RegisterPrefix(string prefix, IWebRequestCreate creator) return !Error; } - internal class HttpRequestCreator : IWebRequestCreate + internal sealed class HttpRequestCreator : IWebRequestCreate { // Create - Create an HttpWebRequest. // diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index c60878d25835e..6c0a5b530fdf5 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -10,9 +10,7 @@ SR.SystemNetSecurity_PlatformNotSupported - - true + true true diff --git a/src/libraries/System.Net.Security/src/System/Net/NTAuthentication.cs b/src/libraries/System.Net.Security/src/System/Net/NTAuthentication.cs index 3fb11880265f7..93b77ffbba0d3 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NTAuthentication.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NTAuthentication.cs @@ -8,7 +8,7 @@ namespace System.Net { - internal partial class NTAuthentication + internal sealed partial class NTAuthentication { internal string? AssociatedName { @@ -86,7 +86,7 @@ internal bool IsNTLM } } - private class InitializeCallbackContext + private sealed class InitializeCallbackContext { internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlagsPal requestedContextFlags, ChannelBinding channelBinding) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Android.cs index 1dfc4818d5888..b96c301c7d512 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Android.cs @@ -6,7 +6,7 @@ namespace System.Net.Security { - internal class CipherSuitesPolicyPal + internal sealed class CipherSuitesPolicyPal { internal TlsCipherSuite[] TlsCipherSuites { get; private set; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Linux.cs b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Linux.cs index 0cbeb85dbd331..0ddef7980c836 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Linux.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Linux.cs @@ -12,7 +12,7 @@ namespace System.Net.Security { - internal class CipherSuitesPolicyPal + internal sealed class CipherSuitesPolicyPal { private static readonly byte[] AllowNoEncryptionDefault = Encoding.ASCII.GetBytes("ALL:eNULL\0"); @@ -184,7 +184,7 @@ internal static bool WantsTls13(SslProtocols protocols) } } - private class OpenSslStringBuilder : StreamWriter + private sealed class OpenSslStringBuilder : StreamWriter { private const string SSL_TXT_Separator = ":"; private static readonly byte[] EmptyString = new byte[1] { 0 }; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.OSX.cs index a5fa2ba4072fe..4838e4738a399 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.OSX.cs @@ -6,7 +6,7 @@ namespace System.Net.Security { - internal class CipherSuitesPolicyPal + internal sealed class CipherSuitesPolicyPal { internal uint[] TlsCipherSuites { get; private set; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Windows.cs index adecd6fa44253..899637c97c2fb 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/CipherSuitesPolicyPal.Windows.cs @@ -5,7 +5,7 @@ namespace System.Net.Security { - internal class CipherSuitesPolicyPal + internal sealed class CipherSuitesPolicyPal { internal CipherSuitesPolicyPal(IEnumerable allowedCipherSuites) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs index 06ecc8d9ed1d7..44fc356c33668 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs @@ -13,6 +13,11 @@ namespace System.Net { internal sealed class SafeDeleteSslContext : SafeDeleteContext { + // mapped from OSX error codes + private const int OSStatus_writErr = -20; + private const int OSStatus_readErr = -19; + private const int OSStatus_noErr = 0; + private const int OSStatus_errSSLWouldBlock = -9803; private const int InitialBufferSize = 2048; private SafeSslHandle _sslContext; private Interop.AppleCrypto.SSLReadFunc _readCallback; @@ -155,48 +160,66 @@ protected override void Dispose(bool disposing) private unsafe int WriteToConnection(void* connection, byte* data, void** dataLength) { - ulong length = (ulong)*dataLength; - Debug.Assert(length <= int.MaxValue); + // We don't pool these buffers and we can't because there's a race between their us in the native + // read/write callbacks and being disposed when the SafeHandle is disposed. This race is benign currently, + // but if we were to pool the buffers we would have a potential use-after-free issue. + try + { + ulong length = (ulong)*dataLength; + Debug.Assert(length <= int.MaxValue); - int toWrite = (int)length; - var inputBuffer = new ReadOnlySpan(data, toWrite); + int toWrite = (int)length; + var inputBuffer = new ReadOnlySpan(data, toWrite); - _outputBuffer.EnsureAvailableSpace(toWrite); - inputBuffer.CopyTo(_outputBuffer.AvailableSpan); - _outputBuffer.Commit(toWrite); + _outputBuffer.EnsureAvailableSpace(toWrite); + inputBuffer.CopyTo(_outputBuffer.AvailableSpan); + _outputBuffer.Commit(toWrite); + // Since we can enqueue everything, no need to re-assign *dataLength. - // Since we can enqueue everything, no need to re-assign *dataLength. - const int noErr = 0; - return noErr; + return OSStatus_noErr; + } + catch (Exception e) + { + if (NetEventSource.Log.IsEnabled()) + NetEventSource.Error(this, $"WritingToConnection failed: {e.Message}"); + return OSStatus_writErr; + } } private unsafe int ReadFromConnection(void* connection, byte* data, void** dataLength) { - const int noErr = 0; - const int errSSLWouldBlock = -9803; - ulong toRead = (ulong)*dataLength; - - if (toRead == 0) + try { - return noErr; - } + ulong toRead = (ulong)*dataLength; + + if (toRead == 0) + { + return OSStatus_noErr; + } - uint transferred = 0; + uint transferred = 0; - if (_inputBuffer.ActiveLength == 0) - { - *dataLength = (void*)0; - return errSSLWouldBlock; - } + if (_inputBuffer.ActiveLength == 0) + { + *dataLength = (void*)0; + return OSStatus_errSSLWouldBlock; + } - int limit = Math.Min((int)toRead, _inputBuffer.ActiveLength); + int limit = Math.Min((int)toRead, _inputBuffer.ActiveLength); - _inputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span(data, limit)); - _inputBuffer.Discard(limit); - transferred = (uint)limit; + _inputBuffer.ActiveSpan.Slice(0, limit).CopyTo(new Span(data, limit)); + _inputBuffer.Discard(limit); + transferred = (uint)limit; - *dataLength = (void*)transferred; - return noErr; + *dataLength = (void*)transferred; + return OSStatus_noErr; + } + catch (Exception e) + { + if (NetEventSource.Log.IsEnabled()) + NetEventSource.Error(this, $"ReadFromConnectionfailed: {e.Message}"); + return OSStatus_readErr; + } } internal void Write(ReadOnlySpan buf) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/ReadWriteAdapter.cs b/src/libraries/System.Net.Security/src/System/Net/Security/ReadWriteAdapter.cs index 33674ab17d402..2833fe1c7d5bc 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/ReadWriteAdapter.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/ReadWriteAdapter.cs @@ -38,7 +38,7 @@ public ValueTask WriteAsync(byte[] buffer, int offset, int count) => public Task WaitAsync(TaskCompletionSource waiter) => waiter.Task; - public Task FlushAsync() => _stream.FlushAsync(); + public Task FlushAsync() => _stream.FlushAsync(CancellationToken); public CancellationToken CancellationToken { get; } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs index 8c54dbbd71921..0e287e097c60d 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs @@ -15,7 +15,7 @@ namespace System.Net.Security { // SecureChannel - a wrapper on SSPI based functionality. // Provides an additional abstraction layer over SSPI for SslStream. - internal class SecureChannel + internal sealed class SecureChannel { // When reading a frame from the wire first read this many bytes for the header. internal const int ReadHeaderSize = 5; @@ -1215,7 +1215,7 @@ private void LogCertificateValidation(RemoteCertificateValidationCallback? remot } // ProtocolToken - used to process and handle the return codes from the SSPI wrapper - internal class ProtocolToken + internal sealed class ProtocolToken { internal SecurityStatusPal Status; internal byte[]? Payload; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index d3f99e6e9b415..b538821ada0b8 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -8,7 +8,7 @@ namespace System.Net.Security { - internal class SslAuthenticationOptions + internal sealed class SslAuthenticationOptions { internal SslAuthenticationOptions(SslClientAuthenticationOptions sslClientAuthenticationOptions, RemoteCertificateValidationCallback? remoteCallback, LocalCertSelectionCallback? localCallback) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs index ba5dc74d1d2b7..1d468dba8a574 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs @@ -7,7 +7,7 @@ namespace System.Net.Security { - internal partial class SslConnectionInfo + internal sealed partial class SslConnectionInfo { public SslConnectionInfo(SafeSslHandle sslContext) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Linux.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Linux.cs index 88472c27def2b..4bff0b9175982 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Linux.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Linux.cs @@ -6,7 +6,7 @@ namespace System.Net.Security { - internal partial class SslConnectionInfo + internal sealed partial class SslConnectionInfo { public SslConnectionInfo(SafeSslHandle sslContext) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs index ace19868dd374..67707d981aa0f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs @@ -7,7 +7,7 @@ namespace System.Net.Security { - internal partial class SslConnectionInfo + internal sealed partial class SslConnectionInfo { public SslConnectionInfo(SafeSslHandle sslContext) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs index 6d1d5d9183686..b13ba361ff326 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Unix.cs @@ -7,7 +7,7 @@ namespace System.Net.Security { - internal partial class SslConnectionInfo + internal sealed partial class SslConnectionInfo { private void MapCipherSuite(TlsCipherSuite cipherSuite) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs index 205526d34f39b..3741d4eaa6f12 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Windows.cs @@ -3,7 +3,7 @@ namespace System.Net.Security { - internal partial class SslConnectionInfo + internal sealed partial class SslConnectionInfo { public SslConnectionInfo(SecPkgContext_ConnectionInfo interopConnectionInfo, TlsCipherSuite cipherSuite) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs index 28cfd1b323120..52a0b00d43bb2 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.cs @@ -3,7 +3,7 @@ namespace System.Net.Security { - internal partial class SslConnectionInfo + internal sealed partial class SslConnectionInfo { public int Protocol { get; } public TlsCipherSuite TlsCipherSuite { get; private set; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs index ed40024958208..4ae127aadcc15 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.cs @@ -39,39 +39,45 @@ public static SslStreamCertificateContext Create(X509Certificate2 target, X509Ce } int count = chain.ChainElements.Count - 1; -#pragma warning disable 0162 // Disable unreachable code warning. TrimRootCertificate is const bool = false on some platforms - if (TrimRootCertificate) + + // Some platforms (e.g. Android) can't ignore all verification and will return zero + // certificates on failure to build a chain. Treat this as not finding any intermediates. + if (count >= 0) { - count--; - foreach (X509ChainStatus status in chain.ChainStatus) +#pragma warning disable 0162 // Disable unreachable code warning. TrimRootCertificate is const bool = false on some platforms + if (TrimRootCertificate) { - if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain)) + count--; + foreach (X509ChainStatus status in chain.ChainStatus) { - // The last cert isn't a root cert - count++; - break; + if (status.Status.HasFlag(X509ChainStatusFlags.PartialChain)) + { + // The last cert isn't a root cert + count++; + break; + } } } - } #pragma warning restore 0162 - // Count can be zero for a self-signed certificate, or a cert issued directly from a root. - if (count > 0 && chain.ChainElements.Count > 1) - { - intermediates = new X509Certificate2[count]; - for (int i = 0; i < count; i++) + // Count can be zero for a self-signed certificate, or a cert issued directly from a root. + if (count > 0 && chain.ChainElements.Count > 1) { - intermediates[i] = chain.ChainElements[i + 1].Certificate; + intermediates = new X509Certificate2[count]; + for (int i = 0; i < count; i++) + { + intermediates[i] = chain.ChainElements[i + 1].Certificate; + } } - } - // Dispose the copy of the target cert. - chain.ChainElements[0].Certificate.Dispose(); + // Dispose the copy of the target cert. + chain.ChainElements[0].Certificate.Dispose(); - // Dispose the last cert, if we didn't include it. - for (int i = count + 1; i < chain.ChainElements.Count; i++) - { - chain.ChainElements[i].Certificate.Dispose(); + // Dispose the last cert, if we didn't include it. + for (int i = count + 1; i < chain.ChainElements.Count; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs index 29fc55ad5c51b..8104387dc07ff 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/TlsFrameHelper.cs @@ -92,7 +92,7 @@ internal struct TlsFrameHeader public override string ToString() => $"{Version}:{Type}[{Length}]"; } - internal class TlsFrameHelper + internal static class TlsFrameHelper { public const int HeaderSize = 5; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index ea1e5c5ac35f7..0812e23e72a4b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -242,8 +242,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [ActiveIssue("https://github.com/dotnet/runtime/issues/46837", TestPlatforms.OSX)] public async Task SslStream_UntrustedCaWithCustomCallback_OK(bool usePartialChain) { - var rnd = new Random(); - int split = rnd.Next(0, certificates.serverChain.Count - 1); + int split = Random.Shared.Next(0, certificates.serverChain.Count - 1); var clientOptions = new SslClientAuthenticationOptions() { TargetHost = "localhost" }; clientOptions.RemoteCertificateValidationCallback = diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index c6421ec4e1986..bbddbb391d54b 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -263,6 +263,8 @@ Link="Common\Interop\Unix\Interop.Poll.Structs.cs" /> + ExposedHandleOrUntrackedConfiguration = true; @@ -238,6 +240,11 @@ private unsafe SocketError DoCloseHandle(bool abortive) { Interop.Error errorCode = Interop.Error.SUCCESS; + if (!IsSocket) + { + return SocketPal.GetSocketErrorForErrorCode(CloseHandle(handle)); + } + // If abortive is not set, we're not running on the finalizer thread, so it's safe to block here. // We can honor the linger options set on the socket. It also means closesocket() might return // EWOULDBLOCK, in which case we need to do some recovery. @@ -281,6 +288,7 @@ private unsafe SocketError DoCloseHandle(bool abortive) case Interop.Error.SUCCESS: case Interop.Error.EINVAL: case Interop.Error.ENOPROTOOPT: + case Interop.Error.ENOTSOCK: errorCode = CloseHandle(handle); break; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs index a9e08bd076c96..79ba1e95c1d3f 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs @@ -75,21 +75,32 @@ partial void ValidateForMultiConnect(bool isMultiEndpoint) } private static unsafe void LoadSocketTypeFromHandle( - SafeSocketHandle handle, out AddressFamily addressFamily, out SocketType socketType, out ProtocolType protocolType, out bool blocking, out bool isListening) + SafeSocketHandle handle, out AddressFamily addressFamily, out SocketType socketType, out ProtocolType protocolType, out bool blocking, out bool isListening, out bool isSocket) { - // Validate that the supplied handle is indeed a socket. - if (Interop.Sys.FStat(handle, out Interop.Sys.FileStatus stat) == -1 || - (stat.Mode & Interop.Sys.FileTypes.S_IFSOCK) != Interop.Sys.FileTypes.S_IFSOCK) + if (Interop.Sys.FStat(handle, out Interop.Sys.FileStatus stat) == -1) { throw new SocketException((int)SocketError.NotSocket); } + isSocket = (stat.Mode & Interop.Sys.FileTypes.S_IFSOCK) == Interop.Sys.FileTypes.S_IFSOCK; - // On Linux, GetSocketType will be able to query SO_DOMAIN, SO_TYPE, and SO_PROTOCOL to get the - // address family, socket type, and protocol type, respectively. On macOS, this will only succeed - // in getting the socket type, and the others will be unknown. Subsequently the Socket ctor - // can use getsockname to retrieve the address family as part of trying to get the local end point. - Interop.Error e = Interop.Sys.GetSocketType(handle, out addressFamily, out socketType, out protocolType, out isListening); - Debug.Assert(e == Interop.Error.SUCCESS, e.ToString()); + handle.IsSocket = isSocket; + + if (isSocket) + { + // On Linux, GetSocketType will be able to query SO_DOMAIN, SO_TYPE, and SO_PROTOCOL to get the + // address family, socket type, and protocol type, respectively. On macOS, this will only succeed + // in getting the socket type, and the others will be unknown. Subsequently the Socket ctor + // can use getsockname to retrieve the address family as part of trying to get the local end point. + Interop.Error e = Interop.Sys.GetSocketType(handle, out addressFamily, out socketType, out protocolType, out isListening); + Debug.Assert(e == Interop.Error.SUCCESS, e.ToString()); + } + else + { + addressFamily = AddressFamily.Unknown; + socketType = SocketType.Unknown; + protocolType = ProtocolType.Unknown; + isListening = false; + } // Get whether the socket is in non-blocking mode. On Unix, we automatically put the underlying // Socket into non-blocking mode whenever an async method is first invoked on the instance, but we @@ -101,7 +112,7 @@ private static unsafe void LoadSocketTypeFromHandle( bool nonBlocking; int rv = Interop.Sys.Fcntl.GetIsNonBlocking(handle, out nonBlocking); blocking = !nonBlocking; - Debug.Assert(rv == 0 || blocking, e.ToString()); // ignore failures + Debug.Assert(rv == 0 || blocking); // ignore failures } internal void ReplaceHandleIfNecessaryAfterFailedConnect() diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 267bfb0ab0aa5..11325495e0a31 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -94,7 +94,7 @@ public Socket(SocketInformation socketInformation) } private unsafe void LoadSocketTypeFromHandle( - SafeSocketHandle handle, out AddressFamily addressFamily, out SocketType socketType, out ProtocolType protocolType, out bool blocking, out bool isListening) + SafeSocketHandle handle, out AddressFamily addressFamily, out SocketType socketType, out ProtocolType protocolType, out bool blocking, out bool isListening, out bool isSocket) { // This can be called without winsock initialized. The handle is not going to be a valid socket handle in that case and the code will throw exception anyway. // Initializing winsock will ensure the error SocketError.NotSocket as opposed to SocketError.NotInitialized. @@ -121,6 +121,7 @@ private unsafe void LoadSocketTypeFromHandle( // This affects the result of querying Socket.Blocking, which will mostly only affect user code that happens to query // that property, though there are a few places we check it internally, e.g. as part of NetworkStream argument validation. blocking = true; + isSocket = true; } [SupportedOSPlatform("windows")] diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index eb5277e8e01a2..2671112ffa139 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -60,7 +60,7 @@ public partial class Socket : IDisposable // These caches are one degree off of Socket since they're not used in the sync case/when disabled in config. private CacheSet? _caches; - private class CacheSet + private sealed class CacheSet { internal CallbackClosure? AcceptClosureCache; internal CallbackClosure? SendClosureCache; @@ -136,95 +136,98 @@ private unsafe Socket(SafeSocketHandle handle, bool loadPropertiesFromHandle) try { // Get properties like address family and blocking mode from the OS. - LoadSocketTypeFromHandle(handle, out _addressFamily, out _socketType, out _protocolType, out _willBlockInternal, out _isListening); + LoadSocketTypeFromHandle(handle, out _addressFamily, out _socketType, out _protocolType, out _willBlockInternal, out _isListening, out bool isSocket); - // We should change stackalloc if this ever grows too big. - Debug.Assert(SocketPal.MaximumAddressSize <= 512); - // Try to get the address of the socket. - Span buffer = stackalloc byte[SocketPal.MaximumAddressSize]; - int bufferLength = buffer.Length; - fixed (byte* bufferPtr = buffer) + if (isSocket) { - if (SocketPal.GetSockName(handle, bufferPtr, &bufferLength) != SocketError.Success) + // We should change stackalloc if this ever grows too big. + Debug.Assert(SocketPal.MaximumAddressSize <= 512); + // Try to get the address of the socket. + Span buffer = stackalloc byte[SocketPal.MaximumAddressSize]; + int bufferLength = buffer.Length; + fixed (byte* bufferPtr = buffer) { - return; + if (SocketPal.GetSockName(handle, bufferPtr, &bufferLength) != SocketError.Success) + { + return; + } } - } - - Debug.Assert(bufferLength <= buffer.Length); - - // Try to get the local end point. That will in turn enable the remote - // end point to be retrieved on-demand when the property is accessed. - Internals.SocketAddress? socketAddress = null; - switch (_addressFamily) - { - case AddressFamily.InterNetwork: - _rightEndPoint = new IPEndPoint( - new IPAddress((long)SocketAddressPal.GetIPv4Address(buffer.Slice(0, bufferLength)) & 0x0FFFFFFFF), - SocketAddressPal.GetPort(buffer)); - break; - case AddressFamily.InterNetworkV6: - Span address = stackalloc byte[IPAddressParserStatics.IPv6AddressBytes]; - SocketAddressPal.GetIPv6Address(buffer.Slice(0, bufferLength), address, out uint scope); - _rightEndPoint = new IPEndPoint( - new IPAddress(address, scope), - SocketAddressPal.GetPort(buffer)); - break; + Debug.Assert(bufferLength <= buffer.Length); - case AddressFamily.Unix: - socketAddress = new Internals.SocketAddress(_addressFamily, buffer.Slice(0, bufferLength)); - _rightEndPoint = new UnixDomainSocketEndPoint(IPEndPointExtensions.GetNetSocketAddress(socketAddress)); - break; - } + // Try to get the local end point. That will in turn enable the remote + // end point to be retrieved on-demand when the property is accessed. + Internals.SocketAddress? socketAddress = null; + switch (_addressFamily) + { + case AddressFamily.InterNetwork: + _rightEndPoint = new IPEndPoint( + new IPAddress((long)SocketAddressPal.GetIPv4Address(buffer.Slice(0, bufferLength)) & 0x0FFFFFFFF), + SocketAddressPal.GetPort(buffer)); + break; + + case AddressFamily.InterNetworkV6: + Span address = stackalloc byte[IPAddressParserStatics.IPv6AddressBytes]; + SocketAddressPal.GetIPv6Address(buffer.Slice(0, bufferLength), address, out uint scope); + _rightEndPoint = new IPEndPoint( + new IPAddress(address, scope), + SocketAddressPal.GetPort(buffer)); + break; + + case AddressFamily.Unix: + socketAddress = new Internals.SocketAddress(_addressFamily, buffer.Slice(0, bufferLength)); + _rightEndPoint = new UnixDomainSocketEndPoint(IPEndPointExtensions.GetNetSocketAddress(socketAddress)); + break; + } - // Try to determine if we're connected, based on querying for a peer, just as we would in RemoteEndPoint, - // but ignoring any failures; this is best-effort (RemoteEndPoint also does a catch-all around the Create call). - if (_rightEndPoint != null) - { - try + // Try to determine if we're connected, based on querying for a peer, just as we would in RemoteEndPoint, + // but ignoring any failures; this is best-effort (RemoteEndPoint also does a catch-all around the Create call). + if (_rightEndPoint != null) { - // Local and remote end points may be different sizes for protocols like Unix Domain Sockets. - bufferLength = buffer.Length; - switch (SocketPal.GetPeerName(handle, buffer, ref bufferLength)) + try { - case SocketError.Success: - switch (_addressFamily) - { - case AddressFamily.InterNetwork: - _remoteEndPoint = new IPEndPoint( - new IPAddress((long)SocketAddressPal.GetIPv4Address(buffer.Slice(0, bufferLength)) & 0x0FFFFFFFF), - SocketAddressPal.GetPort(buffer)); - break; - - case AddressFamily.InterNetworkV6: - Span address = stackalloc byte[IPAddressParserStatics.IPv6AddressBytes]; - SocketAddressPal.GetIPv6Address(buffer.Slice(0, bufferLength), address, out uint scope); - _remoteEndPoint = new IPEndPoint( - new IPAddress(address, scope), - SocketAddressPal.GetPort(buffer)); - break; - - case AddressFamily.Unix: - socketAddress = new Internals.SocketAddress(_addressFamily, buffer.Slice(0, bufferLength)); - _remoteEndPoint = new UnixDomainSocketEndPoint(IPEndPointExtensions.GetNetSocketAddress(socketAddress)); - break; - } + // Local and remote end points may be different sizes for protocols like Unix Domain Sockets. + bufferLength = buffer.Length; + switch (SocketPal.GetPeerName(handle, buffer, ref bufferLength)) + { + case SocketError.Success: + switch (_addressFamily) + { + case AddressFamily.InterNetwork: + _remoteEndPoint = new IPEndPoint( + new IPAddress((long)SocketAddressPal.GetIPv4Address(buffer.Slice(0, bufferLength)) & 0x0FFFFFFFF), + SocketAddressPal.GetPort(buffer)); + break; + + case AddressFamily.InterNetworkV6: + Span address = stackalloc byte[IPAddressParserStatics.IPv6AddressBytes]; + SocketAddressPal.GetIPv6Address(buffer.Slice(0, bufferLength), address, out uint scope); + _remoteEndPoint = new IPEndPoint( + new IPAddress(address, scope), + SocketAddressPal.GetPort(buffer)); + break; + + case AddressFamily.Unix: + socketAddress = new Internals.SocketAddress(_addressFamily, buffer.Slice(0, bufferLength)); + _remoteEndPoint = new UnixDomainSocketEndPoint(IPEndPointExtensions.GetNetSocketAddress(socketAddress)); + break; + } - _isConnected = true; - break; - - case SocketError.InvalidArgument: - // On some OSes (e.g. macOS), EINVAL means the socket has been shut down. - // This can happen if, for example, socketpair was used and the parent - // process closed its copy of the child's socket. Since we don't know - // whether we're actually connected or not, err on the side of saying - // we're connected. - _isConnected = true; - break; + _isConnected = true; + break; + + case SocketError.InvalidArgument: + // On some OSes (e.g. macOS), EINVAL means the socket has been shut down. + // This can happen if, for example, socketpair was used and the parent + // process closed its copy of the child's socket. Since we don't know + // whether we're actually connected or not, err on the side of saying + // we're connected. + _isConnected = true; + break; + } } + catch { } } - catch { } } } catch diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 4d15435d3567e..bf57c9311602c 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -787,9 +787,12 @@ public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation { Trace(context, $"Enter"); - if (!context.IsRegistered) + if (!context.IsRegistered && !context.TryRegister(out Interop.Error error)) { - context.Register(); + HandleFailedRegistration(context, operation, error); + + Trace(context, "Leave, not registered"); + return false; } while (true) @@ -867,6 +870,31 @@ public bool StartAsyncOperation(SocketAsyncContext context, TOperation operation return false; } } + + static void HandleFailedRegistration(SocketAsyncContext context, TOperation operation, Interop.Error error) + { + Debug.Assert(error != Interop.Error.SUCCESS); + + // macOS: kevent returns EPIPE when adding pipe fd for which the other end is closed. + if (error == Interop.Error.EPIPE) + { + // Because the other end close, we expect the operation to complete when we retry it. + // If it doesn't, we fall through and throw an Exception. + if (operation.TryComplete(context)) + { + return; + } + } + + if (error == Interop.Error.ENOMEM || error == Interop.Error.ENOSPC) + { + throw new OutOfMemoryException(); + } + else + { + throw new InternalException(error); + } + } } public AsyncOperation? ProcessSyncEventOrGetAsyncEvent(SocketAsyncContext context, bool skipAsyncEvents = false) @@ -1224,7 +1252,7 @@ public bool PreferInlineCompletions get => _socket.PreferInlineCompletions; } - private void Register() + private bool TryRegister(out Interop.Error error) { Debug.Assert(_nonBlockingSet); lock (_registerLock) @@ -1236,9 +1264,18 @@ private void Register() { _socket.DangerousAddRef(ref addedRef); IntPtr handle = _socket.DangerousGetHandle(); - Volatile.Write(ref _asyncEngine, SocketAsyncEngine.RegisterSocket(handle, this)); + if (SocketAsyncEngine.TryRegisterSocket(handle, this, out SocketAsyncEngine? engine, out error)) + { + Volatile.Write(ref _asyncEngine, engine); - Trace("Registered"); + Trace("Registered"); + return true; + } + else + { + Trace("Registration failed"); + return false; + } } finally { @@ -1248,6 +1285,8 @@ private void Register() } } } + error = Interop.Error.SUCCESS; + return true; } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs index 28e7c3a092ef3..983ca51ed27ee 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEngine.Unix.cs @@ -97,15 +97,16 @@ private static SocketAsyncEngine[] CreateEngines() // // Registers the Socket with a SocketAsyncEngine, and returns the associated engine. // - public static SocketAsyncEngine RegisterSocket(IntPtr socketHandle, SocketAsyncContext context) + public static bool TryRegisterSocket(IntPtr socketHandle, SocketAsyncContext context, out SocketAsyncEngine? engine, out Interop.Error error) { int engineIndex = Math.Abs(Interlocked.Increment(ref s_allocateFromEngine) % s_engines.Length); - SocketAsyncEngine engine = s_engines[engineIndex]; - engine.RegisterCore(socketHandle, context); - return engine; + SocketAsyncEngine nextEngine = s_engines[engineIndex]; + bool registered = nextEngine.TryRegisterCore(socketHandle, context, out error); + engine = registered ? nextEngine : null; + return registered; } - private void RegisterCore(IntPtr socketHandle, SocketAsyncContext context) + private bool TryRegisterCore(IntPtr socketHandle, SocketAsyncContext context, out Interop.Error error) { bool added = _handleToContextMap.TryAdd(socketHandle, new SocketAsyncContextWrapper(context)); if (!added) @@ -115,22 +116,15 @@ private void RegisterCore(IntPtr socketHandle, SocketAsyncContext context) throw new InvalidOperationException(SR.net_sockets_handle_already_used); } - Interop.Error error = Interop.Sys.TryChangeSocketEventRegistration(_port, socketHandle, Interop.Sys.SocketEvents.None, + error = Interop.Sys.TryChangeSocketEventRegistration(_port, socketHandle, Interop.Sys.SocketEvents.None, Interop.Sys.SocketEvents.Read | Interop.Sys.SocketEvents.Write, socketHandle); if (error == Interop.Error.SUCCESS) { - return; + return true; } _handleToContextMap.TryRemove(socketHandle, out _); - if (error == Interop.Error.ENOMEM || error == Interop.Error.ENOSPC) - { - throw new OutOfMemoryException(); - } - else - { - throw new InternalException(error); - } + return false; } public void UnregisterSocket(IntPtr socketHandle) 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 6f800064abad7..735ece8f8d0a7 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 @@ -91,8 +91,25 @@ public static unsafe SocketError CreateSocket(AddressFamily addressFamily, Socke return errorCode; } + private static unsafe int SysRead(SafeSocketHandle handle, Span buffer, out Interop.Error errno) + { + Debug.Assert(!handle.IsSocket); + + int received = 0; + + fixed (byte* b = &MemoryMarshal.GetReference(buffer)) + { + received = Interop.Sys.Read(handle, b, buffer.Length); + errno = received != -1 ? Interop.Error.SUCCESS : Interop.Sys.GetLastError(); + } + + return received; + } + private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, Span buffer, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); + int received = 0; fixed (byte* b = &MemoryMarshal.GetReference(buffer)) @@ -115,6 +132,8 @@ private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, Span buffer, byte[]? socketAddress, ref int socketAddressLen, out SocketFlags receivedFlags, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); + Debug.Assert(socketAddress != null || socketAddressLen == 0, $"Unexpected values: socketAddress={socketAddress}, socketAddressLen={socketAddressLen}"); long received = 0; @@ -154,14 +173,40 @@ private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, return checked((int)received); } + private static unsafe int SysWrite(SafeSocketHandle handle, ReadOnlySpan buffer, ref int offset, ref int count, out Interop.Error errno) + { + Debug.Assert(!handle.IsSocket); + + int sent; + + fixed (byte* b = &MemoryMarshal.GetReference(buffer)) + { + sent = Interop.Sys.Write(handle, b + offset, count); + if (sent == -1) + { + errno = Interop.Sys.GetLastError(); + } + else + { + errno = Interop.Error.SUCCESS; + offset += sent; + count -= sent; + } + } + + return sent; + } + private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, ReadOnlySpan buffer, ref int offset, ref int count, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); + int sent; fixed (byte* b = &MemoryMarshal.GetReference(buffer)) { errno = Interop.Sys.Send( socket, - &b[offset], + b + offset, count, flags, &sent); @@ -179,13 +224,15 @@ private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, Re private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, ReadOnlySpan buffer, ref int offset, ref int count, byte[] socketAddress, int socketAddressLen, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); + int sent; fixed (byte* sockAddr = socketAddress) fixed (byte* b = &MemoryMarshal.GetReference(buffer)) { var iov = new Interop.Sys.IOVector { - Base = &b[offset], + Base = b + offset, Count = (UIntPtr)count }; @@ -219,6 +266,8 @@ private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, Re private static unsafe int SysSend(SafeSocketHandle socket, SocketFlags flags, IList> buffers, ref int bufferIndex, ref int offset, byte[]? socketAddress, int socketAddressLen, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); + // Pin buffers and set up iovecs. int startIndex = bufferIndex, startOffset = offset; @@ -313,6 +362,8 @@ private static unsafe long SendFile(SafeSocketHandle socket, SafeFileHandle file private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, IList> buffers, byte[]? socketAddress, ref int socketAddressLen, out SocketFlags receivedFlags, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); + int maxBuffers = buffers.Count; bool allocOnStack = maxBuffers <= IovStackThreshold; @@ -412,6 +463,7 @@ private static unsafe int SysReceive(SafeSocketHandle socket, SocketFlags flags, private static unsafe int SysReceiveMessageFrom(SafeSocketHandle socket, SocketFlags flags, Span buffer, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); Debug.Assert(socketAddress != null, "Expected non-null socketAddress"); int cmsgBufferLen = Interop.Sys.GetControlMessageBufferSize(Convert.ToInt32(isIPv4), Convert.ToInt32(isIPv6)); @@ -465,6 +517,7 @@ private static unsafe int SysReceiveMessageFrom( byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, out Interop.Error errno) { + Debug.Assert(socket.IsSocket); Debug.Assert(socketAddress != null, "Expected non-null socketAddress"); int buffersCount = buffers.Count; @@ -678,7 +731,12 @@ public static unsafe bool TryCompleteReceive(SafeSocketHandle socket, Span Interop.Error errno; int received; - if (buffer.Length == 0) + if (!socket.IsSocket) + { + Debug.Assert(flags == SocketFlags.None); + received = SysRead(socket, buffer, out errno); + } + else if (buffer.Length == 0) { // Special case a receive of 0 bytes into a single buffer. A common pattern is to ReceiveAsync 0 bytes in order // to be asynchronously notified when data is available, without needing to dedicate a buffer. Some platforms (e.g. macOS), @@ -732,7 +790,16 @@ public static unsafe bool TryCompleteReceiveFrom(SafeSocketHandle socket, Span Interop.Error errno; try { - sent = buffers != null ? - SysSend(socket, flags, buffers, ref bufferIndex, ref offset, socketAddress, socketAddressLen, out errno) : - socketAddress == null ? SysSend(socket, flags, buffer, ref offset, ref count, out errno) : - SysSend(socket, flags, buffer, ref offset, ref count, socketAddress, socketAddressLen, out errno); + if (!socket.IsSocket) + { + Debug.Assert(flags == SocketFlags.None); + Debug.Assert(buffers == null); + sent = SysWrite(socket, buffer, ref offset, ref count, out errno); + } + else + { + sent = buffers != null ? + SysSend(socket, flags, buffers, ref bufferIndex, ref offset, socketAddress, socketAddressLen, out errno) : + socketAddress == null ? SysSend(socket, flags, buffer, ref offset, ref count, out errno) : + SysSend(socket, flags, buffer, ref offset, ref count, socketAddress, socketAddressLen, out errno); + } } catch (ObjectDisposedException) { @@ -1483,7 +1559,6 @@ public static unsafe SocketError GetSockOpt(SafeSocketHandle handle, SocketOptio { fixed (byte* pinnedValue = &optionValue[0]) { - Debug.Assert(BitConverter.IsLittleEndian, "Expected little endian"); *((int*)pinnedValue) = outError; } optionLength = sizeof(int); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs index c291583fd54d7..c678cb3431bf0 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/CreateSocketTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.IO.Pipes; @@ -249,12 +250,18 @@ public void Ctor_SafeHandle_Invalid_ThrowsException() { AssertExtensions.Throws("handle", () => new Socket(null)); AssertExtensions.Throws("handle", () => new Socket(new SafeSocketHandle((IntPtr)(-1), false))); + } - using (var pipe = new AnonymousPipeServerStream()) - { - SocketException se = Assert.Throws(() => new Socket(new SafeSocketHandle(pipe.ClientSafePipeHandle.DangerousGetHandle(), false))); - Assert.Equal(SocketError.NotSocket, se.SocketErrorCode); - } + [Theory] + [InlineData(true)] + [InlineData(false)] + [PlatformSpecific(TestPlatforms.Linux)] + public void Ctor_Socket_FromPipeHandle_Ctor_Dispose_Success(bool ownsHandle) + { + (int fd1, int fd2) = pipe2(); + close(fd2); + + using var _ = new Socket(new SafeSocketHandle(new IntPtr(fd1), ownsHandle)); } [Theory] @@ -624,6 +631,25 @@ public unsafe void Ctor_SafeHandle_UnknownSocket_Success() [DllImport("libc")] private static extern int close(int fd); + [DllImport("libc", SetLastError = true)] + private static unsafe extern int pipe2(int* pipefd, int flags); + + private static unsafe (int, int) pipe2(int flags = 0) + { + Span pipefd = stackalloc int[2]; + fixed (int* ptr = pipefd) + { + if (pipe2(ptr, flags) == 0) + { + return (pipefd[0], pipefd[1]); + } + else + { + throw new Win32Exception(); + } + } + } + [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] public unsafe void Ctor_SafeHandle_SocketPair_Success() diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisposedSocketTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisposedSocketTests.cs index 5e670126c9d8c..c8394930b552a 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisposedSocketTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisposedSocketTests.cs @@ -747,20 +747,6 @@ public void EndAccept_Throws_ObjectDisposed() Assert.Throws(() => GetDisposedSocket().EndAccept(null)); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] - [InlineData(false)] - [InlineData(true)] - public async Task NonDisposedSocket_SafeHandlesCollected(bool clientAsync) - { - List handles = await CreateHandlesAsync(clientAsync); - RetryHelper.Execute(() => - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - Assert.Equal(0, handles.Count(h => h.IsAlive)); - }); - } - [Fact] public void SocketWithDanglingReferenceDoesntHangFinalizerThread() { @@ -776,6 +762,25 @@ private static void CreateSocketWithDanglingReference() bool dummy = false; socket.SafeHandle.DangerousAddRef(ref dummy); } + } + + [Collection(nameof(NoParallelTests))] + public class DisposedSocketTestsNonParallel + { + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsPreciseGcSupported))] + [InlineData(false)] + [InlineData(true)] + public async Task NonDisposedSocket_SafeHandlesCollected(bool clientAsync) + { + TimeSpan timeout = TimeSpan.FromMilliseconds(TestSettings.PassingTestTimeout); + List handles = await CreateHandlesAsync(clientAsync).WaitAsync(timeout); + await RetryHelper.ExecuteAsync(() => Task.Run(() => + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.Equal(0, handles.Count(h => h.IsAlive)); + })).WaitAsync(timeout); + } [MethodImpl(MethodImplOptions.NoInlining)] private static async Task> CreateHandlesAsync(bool clientAsync) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs index 00bed9e00ea1a..8beef969bae1e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs @@ -561,7 +561,7 @@ public void AcceptAsync_WithReceiveBuffer_Success() const int acceptBufferSize = acceptBufferOverheadSize + acceptBufferDataSize; byte[] sendBuffer = new byte[acceptBufferDataSize]; - new Random().NextBytes(sendBuffer); + Random.Shared.NextBytes(sendBuffer); SocketAsyncEventArgs acceptArgs = new SocketAsyncEventArgs(); acceptArgs.Completed += OnAcceptCompleted; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs index d6a5963f65816..45bbdc29389fa 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs @@ -16,7 +16,6 @@ namespace System.Net.Sockets.Tests public class UnixDomainSocketTest { private readonly ITestOutputHelper _log; - private static Random _random = new Random(); public UnixDomainSocketTest(ITestOutputHelper output) { @@ -251,7 +250,7 @@ public async Task Socket_SendReceiveAsync_Success() public async Task Socket_SendReceiveAsync_PropagateToStream_Success(int iterations, int writeBufferSize, int readBufferSize) { var writeBuffer = new byte[writeBufferSize * iterations]; - new Random().NextBytes(writeBuffer); + Random.Shared.NextBytes(writeBuffer); var readData = new MemoryStream(); string path = GetRandomNonExistingFilePath(); @@ -317,7 +316,7 @@ public async Task ConcurrentSendReceive(bool forceNonBlocking) const int Iters = 25; byte[] sendData = new byte[Iters]; byte[] receiveData = new byte[sendData.Length]; - new Random().NextBytes(sendData); + Random.Shared.NextBytes(sendData); string path = GetRandomNonExistingFilePath(); @@ -359,7 +358,7 @@ public async Task ConcurrentSendReceiveAsync() const int Iters = 2048; byte[] sendData = new byte[Iters]; byte[] receiveData = new byte[sendData.Length]; - new Random().NextBytes(sendData); + Random.Shared.NextBytes(sendData); string path = GetRandomNonExistingFilePath(); @@ -498,7 +497,7 @@ private static string GetRandomNonExistingFilePath() do { // get random name and append random number of characters to get variable name length. - result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + new string('A', _random.Next(1, 32))); + result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + new string('A', Random.Shared.Next(1, 32))); } while (File.Exists(result)); diff --git a/src/libraries/System.Net.WebClient/tests/WebClientTest.cs b/src/libraries/System.Net.WebClient/tests/WebClientTest.cs index 571aefc3d6815..a9b9d80e363fc 100644 --- a/src/libraries/System.Net.WebClient/tests/WebClientTest.cs +++ b/src/libraries/System.Net.WebClient/tests/WebClientTest.cs @@ -660,11 +660,8 @@ public async Task UploadData_LargeData_Success(Uri server) byte[] ignored = await UploadDataAsync(wc, server.ToString(), Encoding.UTF8.GetBytes(largeText)); } - private static string GetRandomText(int length) - { - var rand = new Random(); - return new string(Enumerable.Range(0, 512 * 1024).Select(_ => (char)('a' + rand.Next(0, 26))).ToArray()); - } + private static string GetRandomText(int length) => + new string(Enumerable.Range(0, 512 * 1024).Select(_ => (char)('a' + Random.Shared.Next(0, 26))).ToArray()); [OuterLoop("Uses external servers")] [Theory] diff --git a/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs b/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs index 88403331ff2cf..29608b7254b8c 100644 --- a/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs +++ b/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfo.cs @@ -3,7 +3,7 @@ namespace System.Net { - internal class HeaderInfo + internal sealed class HeaderInfo { internal readonly bool IsRequestRestricted; internal readonly bool IsResponseRestricted; diff --git a/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs b/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs index 4e23fcc418aee..bd1bc3bd10278 100644 --- a/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs +++ b/src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs @@ -7,7 +7,7 @@ namespace System.Net { - internal class HeaderInfoTable + internal sealed class HeaderInfoTable { private static readonly Func s_singleParser = value => new[] { value }; private static readonly Func s_multiParser = value => ParseValueHelper(value, isSetCookie: false); diff --git a/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj b/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj index bd89d8a9ab77b..5fd9532a47b37 100644 --- a/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj +++ b/src/libraries/System.Net.WebProxy/src/System.Net.WebProxy.csproj @@ -1,12 +1,14 @@ true - $(NetCoreAppCurrent) + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser enable + + @@ -15,5 +17,6 @@ + diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs new file mode 100644 index 0000000000000..38fb0d0250be7 --- /dev/null +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.Browser.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Serialization; + +namespace System.Net +{ + public partial class WebProxy : IWebProxy, ISerializable + { + private bool IsLocal(Uri host) + { + if (host.IsLoopback) + { + return true; + } + + string hostString = host.Host; + return + !IPAddress.TryParse(hostString, out _) && + !hostString.Contains('.'); + } + } +} diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs new file mode 100644 index 0000000000000..33894f9145d48 --- /dev/null +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.NonBrowser.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net.NetworkInformation; +using System.Runtime.Serialization; +using System.Threading; + +namespace System.Net +{ + public partial class WebProxy : IWebProxy, ISerializable + { + private static volatile string? s_domainName; + private static volatile IPAddress[]? s_localAddresses; + private static int s_networkChangeRegistered; + + private bool IsLocal(Uri host) + { + if (host.IsLoopback) + { + return true; + } + + string hostString = host.Host; + + if (IPAddress.TryParse(hostString, out IPAddress? hostAddress)) + { + EnsureNetworkChangeRegistration(); + IPAddress[] localAddresses = s_localAddresses ??= Dns.GetHostEntry(Dns.GetHostName()).AddressList; + return Array.IndexOf(localAddresses, hostAddress) != -1; + } + + // No dot? Local. + int dot = hostString.IndexOf('.'); + if (dot == -1) + { + return true; + } + + // If it matches the primary domain, it's local (whether or not the hostname matches). + EnsureNetworkChangeRegistration(); + string local = s_domainName ??= "." + IPGlobalProperties.GetIPGlobalProperties().DomainName; + return + local.Length == (hostString.Length - dot) && + string.Compare(local, 0, hostString, dot, local.Length, StringComparison.OrdinalIgnoreCase) == 0; + } + + /// Ensures we've registered with NetworkChange to clear out statically-cached state upon a network change notification. + private static void EnsureNetworkChangeRegistration() + { + if (s_networkChangeRegistered == 0) + { + Register(); + + static void Register() + { + if (Interlocked.Exchange(ref s_networkChangeRegistered, 1) != 0) + { + return; + } + + // Clear out cached state when we get notification of a network-related change. + NetworkChange.NetworkAddressChanged += (s, e) => + { + s_domainName = null; + s_localAddresses = null; + }; + NetworkChange.NetworkAvailabilityChanged += (s, e) => + { + s_domainName = null; + s_localAddresses = null; + }; + } + } + } + } +} diff --git a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs index a5db1a07205d8..2a35d10674a8c 100644 --- a/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs +++ b/src/libraries/System.Net.WebProxy/src/System/Net/WebProxy.cs @@ -4,14 +4,12 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Net.NetworkInformation; using System.Runtime.Serialization; -using System.Runtime.Versioning; using System.Text.RegularExpressions; namespace System.Net { - public class WebProxy : IWebProxy, ISerializable + public partial class WebProxy : IWebProxy, ISerializable { private ArrayList? _bypassList; private Regex[]? _regexBypassList; @@ -68,7 +66,7 @@ public WebProxy(string? Address, bool BypassOnLocal, string[]? BypassList, ICred [AllowNull] public string[] BypassList { - get { return _bypassList != null ? (string[])_bypassList.ToArray(typeof(string)) : Array.Empty(); } + get => _bypassList != null ? (string[])_bypassList.ToArray(typeof(string)) : Array.Empty(); set { _bypassList = value != null ? new ArrayList(value) : null; @@ -82,8 +80,8 @@ public string[] BypassList public bool UseDefaultCredentials { - get { return Credentials == CredentialCache.DefaultCredentials; } - set { Credentials = value ? CredentialCache.DefaultCredentials : null; } + get => Credentials == CredentialCache.DefaultCredentials; + set => Credentials = value ? CredentialCache.DefaultCredentials : null; } public Uri? GetProxy(Uri destination) @@ -132,13 +130,13 @@ private void UpdateRegexList(bool canThrow) private bool IsMatchInBypassList(Uri input) { - UpdateRegexList(false); + UpdateRegexList(canThrow: false); if (_regexBypassList != null) { string matchUriString = input.IsDefaultPort ? - input.Scheme + "://" + input.Host : - input.Scheme + "://" + input.Host + ":" + input.Port.ToString(); + $"{input.Scheme}://{input.Host}" : + $"{input.Scheme}://{input.Host}:{(uint)input.Port}"; foreach (Regex r in _regexBypassList) { @@ -152,54 +150,6 @@ private bool IsMatchInBypassList(Uri input) return false; } - private bool IsLocal(Uri host) - { - if (host.IsLoopback) - { - return true; - } - - string hostString = host.Host; - -#pragma warning disable CA1416 // Validate platform compatibility, issue: https://github.com/dotnet/runtime/issues/43751 - if (IPAddress.TryParse(hostString, out IPAddress? hostAddress)) - { - return IPAddress.IsLoopback(hostAddress) || IsAddressLocal(hostAddress); - } - - // No dot? Local. - int dot = hostString.IndexOf('.'); - if (dot == -1) - { - return true; - } - - // If it matches the primary domain, it's local. (Whether or not the hostname matches.) - string local = "." + IPGlobalProperties.GetIPGlobalProperties().DomainName; -#pragma warning restore CA1416 // Validate platform compatibility - return - local.Length == (hostString.Length - dot) && - string.Compare(local, 0, hostString, dot, local.Length, StringComparison.OrdinalIgnoreCase) == 0; - } - - [UnsupportedOSPlatform("browser")] - private static bool IsAddressLocal(IPAddress ipAddress) - { - // Perf note: The .NET Framework caches this and then uses network change notifications to track - // whether the set should be recomputed. We could consider doing the same if this is observed as - // a bottleneck, but that tracking has its own costs. - IPAddress[] localAddresses = Dns.GetHostEntry(Dns.GetHostName()).AddressList; - for (int i = 0; i < localAddresses.Length; i++) - { - if (ipAddress.Equals(localAddresses[i])) - { - return true; - } - } - - return false; - } - public bool IsBypassed(Uri host) { if (host == null) @@ -213,27 +163,19 @@ public bool IsBypassed(Uri host) IsMatchInBypassList(host); } - protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext) - { + protected WebProxy(SerializationInfo serializationInfo, StreamingContext streamingContext) => throw new PlatformNotSupportedException(); - } - void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) - { + void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) => throw new PlatformNotSupportedException(); - } - protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) - { + protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext) => throw new PlatformNotSupportedException(); - } [Obsolete("This method has been deprecated. Please use the proxy selected for you by default. https://go.microsoft.com/fwlink/?linkid=14202")] - public static WebProxy GetDefaultProxy() - { + public static WebProxy GetDefaultProxy() => // The .NET Framework here returns a proxy that fetches IE settings and // executes JavaScript to determine the correct proxy. throw new PlatformNotSupportedException(); - } } } diff --git a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs index a378566af65ca..d61f368e7aae8 100644 --- a/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs +++ b/src/libraries/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs @@ -17,9 +17,6 @@ namespace System.Net.WebSockets { internal sealed class WebSocketHandle { - /// GUID appended by the server as part of the security key response. Defined in the RFC. - private const string WSServerGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - /// Shared, lazily-initialized handler for when using default options. private static SocketsHttpHandler? s_defaultHandler; @@ -165,7 +162,18 @@ public async Task ConnectAsync(Uri uri, CancellationToken cancellationToken, Cli string[] subprotocolArray = (string[])subprotocolEnumerableValues; if (subprotocolArray.Length > 0 && !string.IsNullOrEmpty(subprotocolArray[0])) { - subprotocol = options.RequestedSubProtocols.Find(requested => string.Equals(requested, subprotocolArray[0], StringComparison.OrdinalIgnoreCase)); + if (options._requestedSubProtocols is not null) + { + foreach (string requestedProtocol in options._requestedSubProtocols) + { + if (requestedProtocol.Equals(subprotocolArray[0], StringComparison.OrdinalIgnoreCase)) + { + subprotocol = requestedProtocol; + break; + } + } + } + if (subprotocol == null) { throw new WebSocketException( @@ -242,10 +250,35 @@ private static void AddWebSocketHeaders(HttpRequestMessage request, string secKe [SuppressMessage("Microsoft.Security", "CA5350", Justification = "Required by RFC6455")] private static KeyValuePair CreateSecKeyAndSecWebSocketAccept() { - string secKey = Convert.ToBase64String(Guid.NewGuid().ToByteArray()); + // GUID appended by the server as part of the security key response. Defined in the RFC. + ReadOnlySpan wsServerGuidBytes = new byte[] + { + (byte)'2', (byte)'5', (byte)'8', (byte)'E', (byte)'A', (byte)'F', (byte)'A', (byte)'5', (byte)'-', + (byte)'E', (byte)'9', (byte)'1', (byte)'4', (byte)'-', + (byte)'4', (byte)'7', (byte)'D', (byte)'A', (byte)'-', + (byte)'9', (byte)'5', (byte)'C', (byte)'A', (byte)'-', + (byte)'C', (byte)'5', (byte)'A', (byte)'B', (byte)'0', (byte)'D', (byte)'C', (byte)'8', (byte)'5', (byte)'B', (byte)'1', (byte)'1' + }; + + Span bytes = stackalloc byte[24 /* Base64 guid length */ + wsServerGuidBytes.Length]; + + // Base64-encode a new Guid's bytes to get the security key + bool success = Guid.NewGuid().TryWriteBytes(bytes); + Debug.Assert(success); + string secKey = Convert.ToBase64String(bytes.Slice(0, 16 /*sizeof(Guid)*/)); + + // Get the corresponding ASCII bytes for seckey+wsServerGuidBytes + for (int i = 0; i < secKey.Length; i++) bytes[i] = (byte)secKey[i]; + wsServerGuidBytes.CopyTo(bytes.Slice(secKey.Length)); + + // Hash the seckey+wsServerGuidBytes bytes + SHA1.TryHashData(bytes, bytes, out int bytesWritten); + Debug.Assert(bytesWritten == 20 /* SHA1 hash length */); + + // Return the security key + the base64 encoded hashed bytes return new KeyValuePair( secKey, - Convert.ToBase64String(SHA1.HashData(Encoding.ASCII.GetBytes(secKey + WSServerGuid)))); + Convert.ToBase64String(bytes.Slice(0, bytesWritten))); } private static void ValidateHeader(HttpHeaders headers, string name, string expectedValue) diff --git a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs index d6f9512b15ce4..1d644b4239de1 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/ConnectTest.cs @@ -226,8 +226,7 @@ await LoopbackServer.CreateClientAndServerAsync(async uri => using (var clientSocket = new ClientWebSocket()) using (var cts = new CancellationTokenSource(TimeOutMilliseconds)) { - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy authorisation header.")] - clientSocket.Options.SetRequestHeader("Authorization", "AWS4-HMAC-SHA256 Credential= AKIAXXXXXXXXXXXYSZA /20190301/us-east-2/neptune-db/aws4_request, SignedHeaders=host;x-amz-date, Signature=b8155de54d9faab00000000000000000000000000a07e0d7dda49902e4d9202"); + clientSocket.Options.SetRequestHeader("Authorization", "AWS4-HMAC-SHA256 Credential=PLACEHOLDER /20190301/us-east-2/neptune-db/aws4_request, SignedHeaders=host;x-amz-date, Signature=b8155de54d9faab00000000000000000000000000a07e0d7dda49902e4d9202"); await clientSocket.ConnectAsync(uri, cts.Token); } }, server => server.AcceptConnectionAsync(async connection => diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index 430c324f40ae5..787bed60c61c1 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -91,9 +91,8 @@ public async Task SendReceive_PartialMessageDueToSmallReceiveBuffer_Success(Uri [PlatformSpecific(~TestPlatforms.Browser)] // JS Websocket does not support see issue https://github.com/dotnet/runtime/issues/46983 public async Task SendReceive_PartialMessageBeforeCompleteMessageArrives_Success(Uri server) { - var rand = new Random(); var sendBuffer = new byte[ushort.MaxValue + 1]; - rand.NextBytes(sendBuffer); + Random.Shared.NextBytes(sendBuffer); var sendSegment = new ArraySegment(sendBuffer); // Ask the remote server to echo back received messages without ever signaling "end of message". @@ -464,7 +463,6 @@ public async Task ZeroByteReceive_CompletesWhenDataAvailable(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) { - var rand = new Random(); var ctsDefault = new CancellationTokenSource(TimeOutMilliseconds); // Do a 0-byte receive. It shouldn't complete yet. diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs index 9af0f37186cca..b54d42ebf768a 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs @@ -119,6 +119,15 @@ public virtual void Fill(T value) { } protected virtual int IndexOf(T item) { throw null; } public abstract System.Numerics.Tensors.Tensor Reshape(System.ReadOnlySpan dimensions); public abstract void SetValue(int index, T value); + public struct Enumerator : System.Collections.Generic.IEnumerator + { + public T Current { get; private set; } + object? System.Collections.IEnumerator.Current => throw null; + public bool MoveNext() => throw null; + public void Reset() { } + public void Dispose() { } + } + public Enumerator GetEnumerator() => throw null; void System.Collections.Generic.ICollection.Add(T item) { } void System.Collections.Generic.ICollection.Clear() { } bool System.Collections.Generic.ICollection.Contains(T item) { throw null; } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs index bcb3815320dbb..270da201c53b4 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/Tensor.cs @@ -691,6 +691,62 @@ public virtual T this[ReadOnlySpan indices] /// The new value to set at the specified position in this Tensor. public abstract void SetValue(int index, T value); + /// + /// The type that implements enumerators for instances. + /// + public struct Enumerator : IEnumerator + { + private readonly Tensor _tensor; + private int _index; + + internal Enumerator(Tensor tensor) + { + Debug.Assert(tensor != null); + + _tensor = tensor; + _index = 0; + Current = default; + } + + public T Current { get; private set; } + + object? IEnumerator.Current => Current; + + public bool MoveNext() + { + if (_index < _tensor.Length) + { + Current = _tensor.GetValue(_index); + ++_index; + return true; + } + else + { + Current = default; + return false; + } + } + + /// + /// Resets the enumerator to the beginning. + /// + public void Reset() + { + _index = 0; + Current = default; + } + + /// + /// Disposes the enumerator. + /// + public void Dispose() { } + } + + /// + /// Gets an enumerator that enumerates the elements of the . + /// + /// An enumerator for the current . + public Enumerator GetEnumerator() => new Enumerator(this); #region statics /// @@ -717,10 +773,7 @@ public static bool Equals(Tensor left, Tensor right) #endregion #region IEnumerable members - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable)this).GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #region ICollection members @@ -828,13 +881,7 @@ void IList.RemoveAt(int index) #endregion #region IEnumerable members - IEnumerator IEnumerable.GetEnumerator() - { - for (int i = 0; i < Length; i++) - { - yield return GetValue(i); - } - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #region ICollection members diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs index 2c9c7ba33bc04..6dd40408655a5 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTests.cs @@ -1445,7 +1445,7 @@ public void PostfixIncrement(TensorConstructor tensorConstructor) { {1, 2, 3}, {4, 5, 6} - }); ; + }); var actual = tensor; tensor = TensorOperations.Increment(tensor); @@ -1509,7 +1509,7 @@ public void PostfixDecrement(TensorConstructor tensorConstructor) { {-1, 0, 1}, {2, 3, 4} - }); ; + }); var actual = tensor; tensor = TensorOperations.Decrement(tensor); @@ -2419,5 +2419,68 @@ public void TestIReadOnlyTMembers(TensorConstructor constructor) int expectedIndexValue = constructor.IsReversedStride ? 4 : 2; Assert.Equal(expectedIndexValue, tensorList[1]); } + + [Theory] + [MemberData(nameof(GetConstructedTensors))] + public void TestGetEnumerator(Tensor tensor) + { + static IEnumerable GetExpected(Tensor tensor) + { + for (int index = 0; index < tensor.Length; ++index) + yield return tensor.GetValue(index); + } + + Assert.Equal(GetExpected(tensor), tensor); + } + + [Theory] + [MemberData(nameof(GetConstructedTensors))] + public void TestEnumeratorReset(Tensor tensor) + { + static long AdvanceEnumerator(ref Tensor.Enumerator enumerator, long maxCount) + { + long count = 0; + while (count < maxCount && enumerator.MoveNext()) + count++; + + return count; + } + + static void TestStepCountIfInRange(Tensor tensor, long stepCount) + { + if (stepCount < 0 || stepCount > tensor.Length) + return; + + var enumerator = tensor.GetEnumerator(); + long actualStepCount = AdvanceEnumerator(ref enumerator, stepCount); + + Assert.Equal(stepCount, actualStepCount); + + enumerator.Reset(); + + var itemsPostReset = new List(); + while (enumerator.MoveNext()) + itemsPostReset.Add(enumerator.Current); + + Assert.Equal(tensor, itemsPostReset); + } + + TestStepCountIfInRange(tensor, 1); + TestStepCountIfInRange(tensor, tensor.Length - 1); + TestStepCountIfInRange(tensor, tensor.Length / 4); + TestStepCountIfInRange(tensor, tensor.Length - tensor.Length / 4); + TestStepCountIfInRange(tensor, tensor.Length / 2); + TestStepCountIfInRange(tensor, tensor.Length); + } + + [Theory] + [MemberData(nameof(GetConstructedTensors))] + public void TestEnumeratorDispose_DoesNotThrow(Tensor tensor) + { + var enumerator = tensor.GetEnumerator(); + + enumerator.Dispose(); + enumerator.Dispose(); + } } } diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorTestsBase.cs b/src/libraries/System.Numerics.Tensors/tests/TensorTestsBase.cs index 26ed47ec639ea..9774dd22662e6 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorTestsBase.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorTestsBase.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq; namespace System.Numerics.Tensors.Tests { @@ -141,6 +142,27 @@ public static IEnumerable GetTensorAndResultConstructor() } } + public static IEnumerable GetConstructedTensors() + { + foreach (var ctor in GetSingleTensorConstructors().Select(x => (TensorConstructor)x[0])) + { + yield return new object[] { ctor.CreateFromArray(Array.Empty()) }; + yield return new object[] { ctor.CreateFromArray(new[] { 7 }) }; + yield return new object[] { ctor.CreateFromArray(new[] { 7, 14 }) }; + yield return new object[] { ctor.CreateFromArray(new[] { 7, 14, 21 }) }; + yield return new object[] + { + ctor.CreateFromArray(new[,] + { + { 3, 6, 9 }, + { 5, 10, 15 }, + { 7, 14, 21 }, + { 11, 22, 33 } + }) + }; + } + } + public static NativeMemory NativeMemoryFromArray(T[] array) { return NativeMemoryFromArray((Array)array); diff --git a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs index 0713a7a6c38cd..e65e1d034e888 100644 --- a/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs +++ b/src/libraries/System.Numerics.Vectors/ref/System.Numerics.Vectors.cs @@ -304,6 +304,7 @@ public partial struct Vector2 : System.IEquatable, Syst public float Y; public Vector2(float value) { throw null; } public Vector2(float x, float y) { throw null; } + public Vector2(System.ReadOnlySpan values) { throw null; } public static System.Numerics.Vector2 One { get { throw null; } } public static System.Numerics.Vector2 UnitX { get { throw null; } } public static System.Numerics.Vector2 UnitY { get { throw null; } } @@ -313,6 +314,8 @@ public partial struct Vector2 : System.IEquatable, Syst public static System.Numerics.Vector2 Clamp(System.Numerics.Vector2 value1, System.Numerics.Vector2 min, System.Numerics.Vector2 max) { throw null; } public readonly void CopyTo(float[] array) { } public readonly void CopyTo(float[] array, int index) { } + public readonly void CopyTo(System.Span destination) { } + public readonly bool TryCopyTo(System.Span destination) { throw null; } public static float Distance(System.Numerics.Vector2 value1, System.Numerics.Vector2 value2) { throw null; } public static float DistanceSquared(System.Numerics.Vector2 value1, System.Numerics.Vector2 value2) { throw null; } public static System.Numerics.Vector2 Divide(System.Numerics.Vector2 left, System.Numerics.Vector2 right) { throw null; } @@ -361,6 +364,7 @@ public partial struct Vector3 : System.IEquatable, Syst public Vector3(System.Numerics.Vector2 value, float z) { throw null; } public Vector3(float value) { throw null; } public Vector3(float x, float y, float z) { throw null; } + public Vector3(System.ReadOnlySpan values) { throw null; } public static System.Numerics.Vector3 One { get { throw null; } } public static System.Numerics.Vector3 UnitX { get { throw null; } } public static System.Numerics.Vector3 UnitY { get { throw null; } } @@ -371,6 +375,8 @@ public partial struct Vector3 : System.IEquatable, Syst public static System.Numerics.Vector3 Clamp(System.Numerics.Vector3 value1, System.Numerics.Vector3 min, System.Numerics.Vector3 max) { throw null; } public readonly void CopyTo(float[] array) { } public readonly void CopyTo(float[] array, int index) { } + public readonly void CopyTo(System.Span destination) { } + public readonly bool TryCopyTo(System.Span destination) { throw null; } public static System.Numerics.Vector3 Cross(System.Numerics.Vector3 vector1, System.Numerics.Vector3 vector2) { throw null; } public static float Distance(System.Numerics.Vector3 value1, System.Numerics.Vector3 value2) { throw null; } public static float DistanceSquared(System.Numerics.Vector3 value1, System.Numerics.Vector3 value2) { throw null; } @@ -420,6 +426,7 @@ public partial struct Vector4 : System.IEquatable, Syst public Vector4(System.Numerics.Vector3 value, float w) { throw null; } public Vector4(float value) { throw null; } public Vector4(float x, float y, float z, float w) { throw null; } + public Vector4(System.ReadOnlySpan values) { throw null; } public static System.Numerics.Vector4 One { get { throw null; } } public static System.Numerics.Vector4 UnitW { get { throw null; } } public static System.Numerics.Vector4 UnitX { get { throw null; } } @@ -431,6 +438,8 @@ public partial struct Vector4 : System.IEquatable, Syst public static System.Numerics.Vector4 Clamp(System.Numerics.Vector4 value1, System.Numerics.Vector4 min, System.Numerics.Vector4 max) { throw null; } public readonly void CopyTo(float[] array) { } public readonly void CopyTo(float[] array, int index) { } + public readonly void CopyTo(System.Span destination) { } + public readonly bool TryCopyTo(System.Span destination) { throw null; } public static float Distance(System.Numerics.Vector4 value1, System.Numerics.Vector4 value2) { throw null; } public static float DistanceSquared(System.Numerics.Vector4 value1, System.Numerics.Vector4 value2) { throw null; } public static System.Numerics.Vector4 Divide(System.Numerics.Vector4 left, System.Numerics.Vector4 right) { throw null; } diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 7031a7341f453..59763ef9d9004 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -716,7 +716,7 @@ private void TestTryCopyToSpan() where T : struct // us to check that we didn't overwrite any part of the destination // if it was too small to contain the entire output. - new Random().NextBytes(MemoryMarshal.AsBytes(destination)); + Random.Shared.NextBytes(MemoryMarshal.AsBytes(destination)); T[] destinationCopy = destination.ToArray(); Assert.False(vector.TryCopyTo(destination.Slice(1))); diff --git a/src/libraries/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj b/src/libraries/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj index 057f0f6240fbc..c8cff8865d09d 100644 --- a/src/libraries/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj +++ b/src/libraries/System.Numerics.Vectors/tests/System.Numerics.Vectors.Tests.csproj @@ -3,9 +3,6 @@ true $(NetCoreAppCurrent) true - - true diff --git a/src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs b/src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs index e46d916561db0..32e98103ff51b 100644 --- a/src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs @@ -38,6 +38,36 @@ public void Vector2CopyToTest() Assert.Equal(3.0, b[1]); } + [Fact] + public void Vector2CopyToSpanTest() + { + Vector2 vector = new Vector2(1.0f, 2.0f); + Span destination = new float[2]; + + Assert.Throws(() => vector.CopyTo(new Span(new float[1]))); + vector.CopyTo(destination); + + Assert.Equal(1.0f, vector.X); + Assert.Equal(2.0f, vector.Y); + Assert.Equal(vector.X, destination[0]); + Assert.Equal(vector.Y, destination[1]); + } + + [Fact] + public void Vector2TryCopyToTest() + { + Vector2 vector = new Vector2(1.0f, 2.0f); + Span destination = new float[2]; + + Assert.False(vector.TryCopyTo(new Span(new float[1]))); + Assert.True(vector.TryCopyTo(destination)); + + Assert.Equal(1.0f, vector.X); + Assert.Equal(2.0f, vector.Y); + Assert.Equal(vector.X, destination[0]); + Assert.Equal(vector.Y, destination[1]); + } + [Fact] public void Vector2GetHashCodeTest() { @@ -834,6 +864,18 @@ public void Vector2ConstructorTest4() Assert.Equal(expected, target); } + // A test for Vector2f (ReadOnlySpan) + [Fact] + public void Vector2ConstructorTest5() + { + float value = 1.0f; + Vector2 target = new Vector2(new[] { value, value }); + Vector2 expected = new Vector2(value); + + Assert.Equal(expected, target); + Assert.Throws(() => new Vector2(new float[1])); + } + // A test for Add (Vector2f, Vector2f) [Fact] public void Vector2AddTest() diff --git a/src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs b/src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs index 372400ae453ab..5536ebd8fab95 100644 --- a/src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs @@ -40,6 +40,40 @@ public void Vector3CopyToTest() Assert.Equal(3.3f, b[2]); } + [Fact] + public void Vector3CopyToSpanTest() + { + Vector3 vector = new Vector3(1.0f, 2.0f, 3.0f); + Span destination = new float[3]; + + Assert.Throws(() => vector.CopyTo(new Span(new float[2]))); + vector.CopyTo(destination); + + Assert.Equal(1.0f, vector.X); + Assert.Equal(2.0f, vector.Y); + Assert.Equal(3.0f, vector.Z); + Assert.Equal(vector.X, destination[0]); + Assert.Equal(vector.Y, destination[1]); + Assert.Equal(vector.Z, destination[2]); + } + + [Fact] + public void Vector3TryCopyToTest() + { + Vector3 vector = new Vector3(1.0f, 2.0f, 3.0f); + Span destination = new float[3]; + + Assert.False(vector.TryCopyTo(new Span(new float[2]))); + Assert.True(vector.TryCopyTo(destination)); + + Assert.Equal(1.0f, vector.X); + Assert.Equal(2.0f, vector.Y); + Assert.Equal(3.0f, vector.Z); + Assert.Equal(vector.X, destination[0]); + Assert.Equal(vector.Y, destination[1]); + Assert.Equal(vector.Z, destination[2]); + } + [Fact] public void Vector3GetHashCodeTest() { @@ -126,6 +160,7 @@ public void Vector3CrossTest1() // A test for Distance (Vector3f, Vector3f) [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49824")] public void Vector3DistanceTest() { Vector3 a = new Vector3(1.0f, 2.0f, 3.0f); @@ -141,6 +176,7 @@ public void Vector3DistanceTest() // A test for Distance (Vector3f, Vector3f) // Distance from the same point [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49824")] public void Vector3DistanceTest1() { Vector3 a = new Vector3(1.051f, 2.05f, 3.478f); @@ -886,6 +922,18 @@ public void Vector3ConstructorTest4() Assert.True(float.IsPositiveInfinity(target.Z), "Vector3f.constructor (Vector3f) did not return the expected value."); } + // A test for Vector3f (ReadOnlySpan) + [Fact] + public void Vector3ConstructorTest6() + { + float value = 1.0f; + Vector3 target = new Vector3(new[] { value, value, value }); + Vector3 expected = new Vector3(value); + + Assert.Equal(expected, target); + Assert.Throws(() => new Vector3(new float[2])); + } + // A test for Add (Vector3f, Vector3f) [Fact] public void Vector3AddTest() diff --git a/src/libraries/System.Numerics.Vectors/tests/Vector4Tests.cs b/src/libraries/System.Numerics.Vectors/tests/Vector4Tests.cs index 5b8d417529c47..6fe43b76b0515 100644 --- a/src/libraries/System.Numerics.Vectors/tests/Vector4Tests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/Vector4Tests.cs @@ -42,6 +42,44 @@ public void Vector4CopyToTest() Assert.Equal(3.3f, b[3]); } + [Fact] + public void Vector4CopyToSpanTest() + { + Vector4 vector = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); + Span destination = new float[4]; + + Assert.Throws(() => vector.CopyTo(new Span(new float[3]))); + vector.CopyTo(destination); + + Assert.Equal(1.0f, vector.X); + Assert.Equal(2.0f, vector.Y); + Assert.Equal(3.0f, vector.Z); + Assert.Equal(4.0f, vector.W); + Assert.Equal(vector.X, destination[0]); + Assert.Equal(vector.Y, destination[1]); + Assert.Equal(vector.Z, destination[2]); + Assert.Equal(vector.W, destination[3]); + } + + [Fact] + public void Vector4TryCopyToTest() + { + Vector4 vector = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); + Span destination = new float[4]; + + Assert.False(vector.TryCopyTo(new Span(new float[3]))); + Assert.True(vector.TryCopyTo(destination)); + + Assert.Equal(1.0f, vector.X); + Assert.Equal(2.0f, vector.Y); + Assert.Equal(3.0f, vector.Z); + Assert.Equal(4.0f, vector.W); + Assert.Equal(vector.X, destination[0]); + Assert.Equal(vector.Y, destination[1]); + Assert.Equal(vector.Z, destination[2]); + Assert.Equal(vector.W, destination[3]); + } + [Fact] public void Vector4GetHashCodeTest() { @@ -1099,6 +1137,18 @@ public void Vector4ConstructorTest5() Assert.True(float.Equals(float.Epsilon, target.W), "Vector4f.constructor (float, float, float, float) did not return the expected value."); } + // A test for Vector4f (ReadOnlySpan) + [Fact] + public void Vector4ConstructorTest7() + { + float value = 1.0f; + Vector4 target = new Vector4(new[] { value, value, value, value }); + Vector4 expected = new Vector4(value); + + Assert.Equal(expected, target); + Assert.Throws(() => new Vector4(new float[3])); + } + // A test for Add (Vector4f, Vector4f) [Fact] public void Vector4AddTest() diff --git a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs index 9b30dd4de5323..d855d34634b1e 100644 --- a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs +++ b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Emitter.cs @@ -12,7 +12,7 @@ namespace Generators { public partial class EventSourceGenerator { - private class Emitter + private sealed class Emitter { private readonly StringBuilder _builder = new StringBuilder(1024); private readonly GeneratorExecutionContext _context; diff --git a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs index 03e9dad395ad3..be971bb185f0b 100644 --- a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs +++ b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.Parser.cs @@ -16,7 +16,7 @@ namespace Generators { public partial class EventSourceGenerator { - private class Parser + private sealed class Parser { private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; diff --git a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs index b86cdebb0a1e4..1a1339352ba77 100644 --- a/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs +++ b/src/libraries/System.Private.CoreLib/generators/EventSourceGenerator.cs @@ -97,7 +97,7 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode) } } - private class EventSourceClass + private sealed class EventSourceClass { public string Namespace = string.Empty; public string ClassName = string.Empty; diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.Private.CoreLib/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index c93507de0f94c..d7ffcf62b410b 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -19,9 +19,6 @@ - - - diff --git a/src/libraries/System.Private.CoreLib/src/Internal/IO/File.cs b/src/libraries/System.Private.CoreLib/src/Internal/IO/File.cs index 34bff3febe863..127d72daa2e36 100644 --- a/src/libraries/System.Private.CoreLib/src/Internal/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/Internal/IO/File.cs @@ -64,7 +64,7 @@ public static byte[] ReadAllBytes(string path) { int n = fs.Read(bytes, index, count); if (n == 0) - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); index += n; count -= n; } diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 2278e4046c6de..807cca91a37ae 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -1819,9 +1819,6 @@ startIndex cannot be larger than length of string. - - startIndex must be less than length of string. - Stream length must be non-negative and less than 2^31 - 1 - origin. @@ -2998,6 +2995,9 @@ Value was either too large or too small for an Int64. + + The current thread attempted to reacquire a mutex that has reached its maximum acquire count. + Negating the minimum value of a twos complement number is invalid. @@ -3577,6 +3577,9 @@ COM unregister function must have a System.Type parameter and a void return type. + + The handle is invalid. + Type '{0}' has more than one COM registration function. @@ -3679,7 +3682,10 @@ The assembly update failed. + + Assembly updates cannot be applied while a debugger is attached. + Method body replacement not supported in this runtime. - + \ No newline at end of file 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 d9900d80412cd..9256e41f8a633 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 @@ -11,10 +11,12 @@ enable true + true true true $(MSBuildThisFileDirectory)ILLink\ true + true $(DefineConstants);TARGET_32BIT @@ -43,7 +45,7 @@ - $(ILLinkSharedDirectory)ILLinkTrim_LibraryBuild.xml + $(ILLinkSharedDirectory)ILLink.Descriptors.LibraryBuild.xml @@ -63,6 +65,7 @@ + @@ -391,11 +394,9 @@ - - @@ -404,12 +405,10 @@ - - @@ -425,6 +424,11 @@ + + + + + @@ -495,7 +499,7 @@ - + @@ -689,6 +693,8 @@ + + @@ -944,10 +950,6 @@ - - - - @@ -1082,6 +1084,9 @@ Common\Interop\Interop.ICU.cs + + Common\Interop\Interop.ICU.iOS.cs + Common\Interop\Interop.Idna.cs @@ -1094,10 +1099,10 @@ Common\Interop\Interop.ResultCode.cs - + Common\Interop\Interop.TimeZoneDisplayNameType.cs - + Common\Interop\Interop.TimeZoneInfo.cs @@ -1168,6 +1173,14 @@ Common\System\Threading\Tasks\TaskToApm.cs + + + + + + + + Common\Interop\Windows\Advapi32\Interop.ActivityControl.cs @@ -1200,7 +1213,7 @@ - + @@ -1236,7 +1249,7 @@ - + @@ -1634,12 +1647,15 @@ - - - + + + + + + @@ -1831,20 +1847,22 @@ - + + + - - - - + + + + @@ -1884,7 +1902,7 @@ - + @@ -1893,7 +1911,7 @@ - + @@ -1962,7 +1980,6 @@ - @@ -1980,4 +1997,26 @@ + + + + + + + + + + + + + + + + + Interop\Windows\Kernel32\Interop.GetLastError.cs + + + Interop\Windows\Kernel32\Interop.Threading.cs + + diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs b/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs index 9175a3e8ba912..331ef281bb32a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContextConfigHelper.cs @@ -10,6 +10,20 @@ internal static class AppContextConfigHelper internal static bool GetBooleanConfig(string configName, bool defaultValue) => AppContext.TryGetSwitch(configName, out bool value) ? value : defaultValue; + internal static bool GetBooleanConfig(string switchName, string envVariable) + { + if (!AppContext.TryGetSwitch(switchName, out bool ret)) + { + string? switchValue = Environment.GetEnvironmentVariable(envVariable); + if (switchValue != null) + { + ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1"); + } + } + + return ret; + } + internal static int GetInt32Config(string configName, int defaultValue, bool allowNegative = true) { try diff --git a/src/libraries/System.Private.CoreLib/src/System/Attribute.cs b/src/libraries/System.Private.CoreLib/src/System/Attribute.cs index 27ff2091d82b4..2bc115b9f686d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Attribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Attribute.cs @@ -14,7 +14,6 @@ public abstract partial class Attribute { protected Attribute() { } -#if !CORERT [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", Justification = "Unused fields don't make a difference for equality")] public override bool Equals([NotNullWhen(true)] object? obj) @@ -83,7 +82,6 @@ public override int GetHashCode() return type.GetHashCode(); } -#endif // Compares values of custom-attribute fields. private static bool AreFieldValuesEqual(object? thisValue, object? thatValue) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs index 4b366f7090891..888f5ab51d5a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs @@ -109,7 +109,7 @@ public static bool TryFormat(Guid value, Span destination, out int bytesWr DecomposedGuid guidAsBytes = default; guidAsBytes.Guid = value; - // When a GUID is blitted, the first three components are little-endian, and the last component is big-endian. + // When a GUID is blitted, the first three components are native-endian, and the last component is big-endian. // The line below forces the JIT to hoist the bounds check for the following segment. // The JIT will optimize away the read, but it cannot optimize away the bounds check @@ -117,10 +117,20 @@ public static bool TryFormat(Guid value, Span destination, out int bytesWr // We use 8 instead of 7 so that we also capture the dash if we're asked to insert one. { _ = destination[8]; } - HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower); + if (BitConverter.IsLittleEndian) + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower); + } + else + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 2, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 4, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 6, HexConverter.Casing.Lower); + } if (flags < 0 /* use dash? */) { @@ -133,8 +143,16 @@ public static bool TryFormat(Guid value, Span destination, out int bytesWr } { _ = destination[4]; } - HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower); + if (BitConverter.IsLittleEndian) + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower); + } + else + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 2, HexConverter.Casing.Lower); + } if (flags < 0 /* use dash? */) { @@ -147,8 +165,16 @@ public static bool TryFormat(Guid value, Span destination, out int bytesWr } { _ = destination[4]; } - HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower); + if (BitConverter.IsLittleEndian) + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower); + } + else + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 2, HexConverter.Casing.Lower); + } if (flags < 0 /* use dash? */) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 28670f8037226..c67c02a0c6630 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -22,7 +22,7 @@ namespace System [Serializable] [StructLayout(LayoutKind.Sequential)] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public readonly struct Char : IComparable, IComparable, IEquatable, IConvertible + public readonly struct Char : IComparable, IComparable, IEquatable, IConvertible, ISpanFormattable { // // Member Variables @@ -166,6 +166,19 @@ public string ToString(IFormatProvider? provider) // Provides a string representation of a character. public static string ToString(char c) => string.CreateFromChar(c); + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) + { + if (!destination.IsEmpty) + { + destination[0] = m_value; + charsWritten = 1; + return true; + } + + charsWritten = 0; + return false; + } + public static char Parse(string s) { if (s == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs index 5e672ac89bd45..d20f81a798ebd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ArrayList.cs @@ -743,7 +743,7 @@ public virtual void TrimToSize() // This class wraps an IList, exposing it as a ArrayList // Note this requires reimplementing half of ArrayList... - private class IListWrapper : ArrayList + private sealed class IListWrapper : ArrayList { private readonly IList _list; @@ -1183,7 +1183,7 @@ public void Reset() } } - private class SyncArrayList : ArrayList + private sealed class SyncArrayList : ArrayList { private readonly ArrayList _list; private readonly object _root; @@ -1510,7 +1510,7 @@ public override void TrimToSize() } - private class SyncIList : IList + private sealed class SyncIList : IList { private readonly IList _list; private readonly object _root; @@ -1521,19 +1521,19 @@ internal SyncIList(IList list) _root = list.SyncRoot; } - public virtual int Count + public int Count { get { lock (_root) { return _list.Count; } } } - public virtual bool IsReadOnly => _list.IsReadOnly; + public bool IsReadOnly => _list.IsReadOnly; - public virtual bool IsFixedSize => _list.IsFixedSize; + public bool IsFixedSize => _list.IsFixedSize; - public virtual bool IsSynchronized => true; + public bool IsSynchronized => true; - public virtual object? this[int index] + public object? this[int index] { get { @@ -1551,9 +1551,9 @@ public virtual object? this[int index] } } - public virtual object SyncRoot => _root; + public object SyncRoot => _root; - public virtual int Add(object? value) + public int Add(object? value) { lock (_root) { @@ -1562,7 +1562,7 @@ public virtual int Add(object? value) } - public virtual void Clear() + public void Clear() { lock (_root) { @@ -1570,7 +1570,7 @@ public virtual void Clear() } } - public virtual bool Contains(object? item) + public bool Contains(object? item) { lock (_root) { @@ -1578,7 +1578,7 @@ public virtual bool Contains(object? item) } } - public virtual void CopyTo(Array array, int index) + public void CopyTo(Array array, int index) { lock (_root) { @@ -1586,7 +1586,7 @@ public virtual void CopyTo(Array array, int index) } } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { lock (_root) { @@ -1594,7 +1594,7 @@ public virtual IEnumerator GetEnumerator() } } - public virtual int IndexOf(object? value) + public int IndexOf(object? value) { lock (_root) { @@ -1602,7 +1602,7 @@ public virtual int IndexOf(object? value) } } - public virtual void Insert(int index, object? value) + public void Insert(int index, object? value) { lock (_root) { @@ -1610,7 +1610,7 @@ public virtual void Insert(int index, object? value) } } - public virtual void Remove(object? value) + public void Remove(object? value) { lock (_root) { @@ -1618,7 +1618,7 @@ public virtual void Remove(object? value) } } - public virtual void RemoveAt(int index) + public void RemoveAt(int index) { lock (_root) { @@ -1627,7 +1627,7 @@ public virtual void RemoveAt(int index) } } - private class FixedSizeList : IList + private sealed class FixedSizeList : IList { private readonly IList _list; @@ -1636,69 +1636,69 @@ internal FixedSizeList(IList l) _list = l; } - public virtual int Count => _list.Count; + public int Count => _list.Count; - public virtual bool IsReadOnly => _list.IsReadOnly; + public bool IsReadOnly => _list.IsReadOnly; - public virtual bool IsFixedSize => true; + public bool IsFixedSize => true; - public virtual bool IsSynchronized => _list.IsSynchronized; + public bool IsSynchronized => _list.IsSynchronized; - public virtual object? this[int index] + public object? this[int index] { get => _list[index]; set => _list[index] = value; } - public virtual object SyncRoot => _list.SyncRoot; + public object SyncRoot => _list.SyncRoot; - public virtual int Add(object? obj) + public int Add(object? obj) { throw new NotSupportedException(SR.NotSupported_FixedSizeCollection); } - public virtual void Clear() + public void Clear() { throw new NotSupportedException(SR.NotSupported_FixedSizeCollection); } - public virtual bool Contains(object? obj) + public bool Contains(object? obj) { return _list.Contains(obj); } - public virtual void CopyTo(Array array, int index) + public void CopyTo(Array array, int index) { _list.CopyTo(array, index); } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } - public virtual int IndexOf(object? value) + public int IndexOf(object? value) { return _list.IndexOf(value); } - public virtual void Insert(int index, object? obj) + public void Insert(int index, object? obj) { throw new NotSupportedException(SR.NotSupported_FixedSizeCollection); } - public virtual void Remove(object? value) + public void Remove(object? value) { throw new NotSupportedException(SR.NotSupported_FixedSizeCollection); } - public virtual void RemoveAt(int index) + public void RemoveAt(int index) { throw new NotSupportedException(SR.NotSupported_FixedSizeCollection); } } - private class FixedSizeArrayList : ArrayList + private sealed class FixedSizeArrayList : ArrayList { private ArrayList _list; @@ -1885,7 +1885,7 @@ public override void TrimToSize() } } - private class ReadOnlyList : IList + private sealed class ReadOnlyList : IList { private readonly IList _list; @@ -1894,69 +1894,69 @@ internal ReadOnlyList(IList l) _list = l; } - public virtual int Count => _list.Count; + public int Count => _list.Count; - public virtual bool IsReadOnly => true; + public bool IsReadOnly => true; - public virtual bool IsFixedSize => true; + public bool IsFixedSize => true; - public virtual bool IsSynchronized => _list.IsSynchronized; + public bool IsSynchronized => _list.IsSynchronized; - public virtual object? this[int index] + public object? this[int index] { get => _list[index]; set => throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } - public virtual object SyncRoot => _list.SyncRoot; + public object SyncRoot => _list.SyncRoot; - public virtual int Add(object? obj) + public int Add(object? obj) { throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } - public virtual void Clear() + public void Clear() { throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } - public virtual bool Contains(object? obj) + public bool Contains(object? obj) { return _list.Contains(obj); } - public virtual void CopyTo(Array array, int index) + public void CopyTo(Array array, int index) { _list.CopyTo(array, index); } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } - public virtual int IndexOf(object? value) + public int IndexOf(object? value) { return _list.IndexOf(value); } - public virtual void Insert(int index, object? obj) + public void Insert(int index, object? obj) { throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } - public virtual void Remove(object? value) + public void Remove(object? value) { throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } - public virtual void RemoveAt(int index) + public void RemoveAt(int index) { throw new NotSupportedException(SR.NotSupported_ReadOnlyCollection); } } - private class ReadOnlyArrayList : ArrayList + private sealed class ReadOnlyArrayList : ArrayList { private ArrayList _list; @@ -2200,7 +2200,7 @@ public void Reset() // Implementation of a generic list subrange. An instance of this class // is returned by the default implementation of List.GetRange. - private class Range : ArrayList + private sealed class Range : ArrayList { private ArrayList _baseList; private readonly int _baseIndex; @@ -2686,7 +2686,7 @@ public void Reset() } } - internal class ArrayListDebugView + internal sealed class ArrayListDebugView { private readonly ArrayList _arrayList; diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs index e45ed1e6a21b4..7a49887d17c28 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueue.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Threading; namespace System.Collections.Concurrent @@ -93,7 +94,7 @@ public ConcurrentQueue(IEnumerable collection) int count = c.Count; if (count > length) { - length = Math.Min(ConcurrentQueueSegment.RoundUpToPowerOf2(count), MaxSegmentLength); + length = (int)Math.Min(BitOperations.RoundUpToPowerOf2((uint)count), MaxSegmentLength); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs index 89d8704a2da61..fc0d99bb6bdec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Concurrent/ConcurrentQueueSegment.cs @@ -71,19 +71,6 @@ internal ConcurrentQueueSegment(int boundedLength) } } - /// Round the specified value up to the next power of 2, if it isn't one already. - internal static int RoundUpToPowerOf2(int i) - { - // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - --i; - i |= i >> 1; - i |= i >> 2; - i |= i >> 4; - i |= i >> 8; - i |= i >> 16; - return i + 1; - } - /// Gets the number of elements this segment can store. internal int Capacity => _slots.Length; diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs index c619f92c287fd..0f50840b6fabe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.cs @@ -11,7 +11,7 @@ namespace System.Collections.Generic { #region ArraySortHelper for single arrays - internal partial class ArraySortHelper + internal sealed partial class ArraySortHelper { #region IArraySortHelper Members @@ -269,7 +269,7 @@ private static void InsertionSort(Span keys, Comparison comparer) } } - internal partial class GenericArraySortHelper + internal sealed partial class GenericArraySortHelper where T : IComparable { // Do not add a constructor to this class because ArraySortHelper.CreateSortHelper will not execute it @@ -603,7 +603,7 @@ private static bool GreaterThan(ref T left, ref T right) #region ArraySortHelper for paired key and value arrays - internal partial class ArraySortHelper + internal sealed partial class ArraySortHelper { public void Sort(Span keys, Span values, IComparer? comparer) { @@ -819,7 +819,7 @@ private static void InsertionSort(Span keys, Span values, ICompare } } - internal partial class GenericArraySortHelper + internal sealed partial class GenericArraySortHelper where TKey : IComparable { public void Sort(Span keys, Span values, IComparer? comparer) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs index c21928a7381fd..cdfc29a7a3e43 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs @@ -120,7 +120,7 @@ public override int GetHashCode() => [Serializable] internal sealed partial class EnumComparer : Comparer, ISerializable where T : struct, Enum { - internal EnumComparer() { } + public EnumComparer() { } // Used by the serialization engine. private EnumComparer(SerializationInfo info, StreamingContext context) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index 798b3d08c9453..adb97baacb170 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -54,7 +54,7 @@ public Dictionary(int capacity, IEqualityComparer? comparer) Initialize(capacity); } - if (comparer != null && comparer != EqualityComparer.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily + if (comparer is not null && comparer != EqualityComparer.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily { _comparer = comparer; } @@ -62,20 +62,12 @@ public Dictionary(int capacity, IEqualityComparer? comparer) // Special-case EqualityComparer.Default, StringComparer.Ordinal, and StringComparer.OrdinalIgnoreCase. // We use a non-randomized comparer for improved perf, falling back to a randomized comparer if the // hash buckets become unbalanced. - if (typeof(TKey) == typeof(string)) { - if (_comparer is null) - { - _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.WrappedAroundDefaultComparer; - } - else if (ReferenceEquals(_comparer, StringComparer.Ordinal)) - { - _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinal; - } - else if (ReferenceEquals(_comparer, StringComparer.OrdinalIgnoreCase)) + IEqualityComparer? stringComparer = NonRandomizedStringEqualityComparer.GetStringComparer(_comparer); + if (stringComparer is not null) { - _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinalIgnoreCase; + _comparer = (IEqualityComparer?)stringComparer; } } } @@ -360,7 +352,7 @@ public virtual void GetObjectData(SerializationInfo info, StreamingContext conte } } - private ref TValue FindValue(TKey key) + internal ref TValue FindValue(TKey key) { if (key == null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs index 0af18e7f7dd1a..e7f23a80ab5fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs @@ -180,7 +180,7 @@ public override int GetHashCode() => // Needs to be public to support binary serialization compatibility public sealed partial class EnumEqualityComparer : EqualityComparer, ISerializable where T : struct, Enum { - internal EnumEqualityComparer() { } + public EnumEqualityComparer() { } // This is used by the serialization engine. private EnumEqualityComparer(SerializationInfo information, StreamingContext context) { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs index d3516d66aca91..bda3510446545 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs @@ -54,7 +54,7 @@ public HashSet() : this((IEqualityComparer?)null) { } public HashSet(IEqualityComparer? comparer) { - if (comparer != null && comparer != EqualityComparer.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily + if (comparer is not null && comparer != EqualityComparer.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily { _comparer = comparer; } @@ -62,20 +62,12 @@ public HashSet(IEqualityComparer? comparer) // Special-case EqualityComparer.Default, StringComparer.Ordinal, and StringComparer.OrdinalIgnoreCase. // We use a non-randomized comparer for improved perf, falling back to a randomized comparer if the // hash buckets become unbalanced. - if (typeof(T) == typeof(string)) { - if (_comparer is null) - { - _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.WrappedAroundDefaultComparer; - } - else if (ReferenceEquals(_comparer, StringComparer.Ordinal)) - { - _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinal; - } - else if (ReferenceEquals(_comparer, StringComparer.OrdinalIgnoreCase)) + IEqualityComparer? stringComparer = NonRandomizedStringEqualityComparer.GetStringComparer(_comparer); + if (stringComparer is not null) { - _comparer = (IEqualityComparer)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinalIgnoreCase; + _comparer = (IEqualityComparer?)stringComparer; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs index 0ec1a501d56f7..1724cdcd1daf2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Globalization; using System.Runtime.Serialization; -using Internal.Runtime.CompilerServices; namespace System.Collections.Generic { @@ -20,9 +18,9 @@ public class NonRandomizedStringEqualityComparer : IEqualityComparer, I // that was passed in to the ctor. The caller chooses one of these singletons so that the // GetUnderlyingEqualityComparer method can return the correct value. - internal static readonly NonRandomizedStringEqualityComparer WrappedAroundDefaultComparer = new OrdinalComparer(EqualityComparer.Default); - internal static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinal = new OrdinalComparer(StringComparer.Ordinal); - internal static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinalIgnoreCase = new OrdinalIgnoreCaseComparer(StringComparer.OrdinalIgnoreCase); + private static readonly NonRandomizedStringEqualityComparer WrappedAroundDefaultComparer = new OrdinalComparer(EqualityComparer.Default); + private static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinal = new OrdinalComparer(StringComparer.Ordinal); + private static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinalIgnoreCase = new OrdinalIgnoreCaseComparer(StringComparer.OrdinalIgnoreCase); private readonly IEqualityComparer _underlyingComparer; @@ -109,5 +107,26 @@ internal override RandomizedStringEqualityComparer GetRandomizedEqualityComparer return RandomizedStringEqualityComparer.Create(_underlyingComparer, ignoreCase: true); } } + + public static IEqualityComparer? GetStringComparer(object? comparer) + { + // Special-case EqualityComparer.Default, StringComparer.Ordinal, and StringComparer.OrdinalIgnoreCase. + // We use a non-randomized comparer for improved perf, falling back to a randomized comparer if the + // hash buckets become unbalanced. + if (comparer is null) + { + return WrappedAroundDefaultComparer; + } + else if (ReferenceEquals(comparer, StringComparer.Ordinal)) + { + return WrappedAroundStringComparerOrdinal; + } + else if (ReferenceEquals(comparer, StringComparer.OrdinalIgnoreCase)) + { + return WrappedAroundStringComparerOrdinalIgnoreCase; + } + + return null; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs index 373304cd1d4e6..0b65c0469b4d7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Hashtable.cs @@ -1194,7 +1194,7 @@ public virtual void OnDeserialization(object? sender) // Implements a Collection for the keys of a hashtable. An instance of this // class is created by the GetKeys method of a hashtable. - private class KeyCollection : ICollection + private sealed class KeyCollection : ICollection { private readonly Hashtable _hashtable; @@ -1203,7 +1203,7 @@ internal KeyCollection(Hashtable hashtable) _hashtable = hashtable; } - public virtual void CopyTo(Array array, int arrayIndex) + public void CopyTo(Array array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array)); @@ -1216,21 +1216,21 @@ public virtual void CopyTo(Array array, int arrayIndex) _hashtable.CopyKeys(array, arrayIndex); } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return new HashtableEnumerator(_hashtable, HashtableEnumerator.Keys); } - public virtual bool IsSynchronized => _hashtable.IsSynchronized; + public bool IsSynchronized => _hashtable.IsSynchronized; - public virtual object SyncRoot => _hashtable.SyncRoot; + public object SyncRoot => _hashtable.SyncRoot; - public virtual int Count => _hashtable._count; + public int Count => _hashtable._count; } // Implements a Collection for the values of a hashtable. An instance of // this class is created by the GetValues method of a hashtable. - private class ValueCollection : ICollection + private sealed class ValueCollection : ICollection { private readonly Hashtable _hashtable; @@ -1239,7 +1239,7 @@ internal ValueCollection(Hashtable hashtable) _hashtable = hashtable; } - public virtual void CopyTo(Array array, int arrayIndex) + public void CopyTo(Array array, int arrayIndex) { if (array == null) throw new ArgumentNullException(nameof(array)); @@ -1252,22 +1252,22 @@ public virtual void CopyTo(Array array, int arrayIndex) _hashtable.CopyValues(array, arrayIndex); } - public virtual IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { return new HashtableEnumerator(_hashtable, HashtableEnumerator.Values); } - public virtual bool IsSynchronized => _hashtable.IsSynchronized; + public bool IsSynchronized => _hashtable.IsSynchronized; - public virtual object SyncRoot => _hashtable.SyncRoot; + public object SyncRoot => _hashtable.SyncRoot; - public virtual int Count => _hashtable._count; + public int Count => _hashtable._count; } // Synchronized wrapper for hashtable - private class SyncHashtable : Hashtable, IEnumerable + private sealed class SyncHashtable : Hashtable, IEnumerable { - protected Hashtable _table; + private Hashtable _table; internal SyncHashtable(Hashtable table) : base(false) { @@ -1416,7 +1416,7 @@ internal override KeyValuePairs[] ToKeyValuePairsArray() // Implements an enumerator for a hashtable. The enumerator uses the // internal version number of the hashtable to ensure that no modifications // are made to the hashtable while an enumeration is in progress. - private class HashtableEnumerator : IDictionaryEnumerator, ICloneable + private sealed class HashtableEnumerator : IDictionaryEnumerator, ICloneable { private readonly Hashtable _hashtable; private int _bucket; @@ -1441,7 +1441,7 @@ internal HashtableEnumerator(Hashtable hashtable, int getObjRetType) public object Clone() => MemberwiseClone(); - public virtual object Key + public object Key { get { @@ -1451,7 +1451,7 @@ public virtual object Key } } - public virtual bool MoveNext() + public bool MoveNext() { if (_version != _hashtable._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); @@ -1471,7 +1471,7 @@ public virtual bool MoveNext() return false; } - public virtual DictionaryEntry Entry + public DictionaryEntry Entry { get { @@ -1481,7 +1481,7 @@ public virtual DictionaryEntry Entry } } - public virtual object? Current + public object? Current { get { @@ -1497,7 +1497,7 @@ public virtual object? Current } } - public virtual object? Value + public object? Value { get { @@ -1507,7 +1507,7 @@ public virtual object? Value } } - public virtual void Reset() + public void Reset() { if (_version != _hashtable._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion); @@ -1519,7 +1519,7 @@ public virtual void Reset() } // internal debug view class for hashtable - internal class HashtableDebugView + internal sealed class HashtableDebugView { private readonly Hashtable _hashtable; diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/KeyValuePairs.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/KeyValuePairs.cs index 6d1e3622fb25b..58eb617a8e474 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/KeyValuePairs.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/KeyValuePairs.cs @@ -15,7 +15,7 @@ namespace System.Collections { [DebuggerDisplay("{_value}", Name = "[{_key}]")] - internal class KeyValuePairs + internal sealed class KeyValuePairs { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly object _key; diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/ListDictionaryInternal.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/ListDictionaryInternal.cs index e0419b18319d2..b06c9edb24965 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/ListDictionaryInternal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/ListDictionaryInternal.cs @@ -222,7 +222,7 @@ public void Remove(object key) count--; } - private class NodeEnumerator : IDictionaryEnumerator + private sealed class NodeEnumerator : IDictionaryEnumerator { private readonly ListDictionaryInternal list; private DictionaryNode? current; @@ -307,7 +307,7 @@ public void Reset() } } - private class NodeKeyValueCollection : ICollection + private sealed class NodeKeyValueCollection : ICollection { private readonly ListDictionaryInternal list; private readonly bool isKeys; @@ -357,7 +357,7 @@ IEnumerator IEnumerable.GetEnumerator() return new NodeKeyValueEnumerator(list, isKeys); } - private class NodeKeyValueEnumerator : IEnumerator + private sealed class NodeKeyValueEnumerator : IEnumerator { private readonly ListDictionaryInternal list; private DictionaryNode? current; @@ -420,7 +420,7 @@ public void Reset() } [Serializable] - private class DictionaryNode + private sealed class DictionaryNode { public object key = null!; public object? value; diff --git a/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs index 85ae555ac67f4..9d7ff597c9c71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ComponentModel/DefaultValueAttribute.cs @@ -27,7 +27,10 @@ public class DefaultValueAttribute : Attribute /// class, converting the specified value to the specified type, and using the U.S. English /// culture as the translation context. /// - public DefaultValueAttribute(Type type, string? value) + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] + public DefaultValueAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + string? value) { // The null check and try/catch here are because attributes should never throw exceptions. // We would fail to load an otherwise normal class. @@ -56,8 +59,12 @@ public DefaultValueAttribute(Type type, string? value) _value = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); } + [RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] // Looking for ad hoc created TypeDescriptor.ConvertFromInvariantString(Type, string) - static bool TryConvertFromInvariantString(Type? typeToConvert, string? stringValue, out object? conversionResult) + static bool TryConvertFromInvariantString( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type typeToConvert, + string? stringValue, + out object? conversionResult) { conversionResult = null; @@ -69,7 +76,7 @@ static bool TryConvertFromInvariantString(Type? typeToConvert, string? stringVal Volatile.Write(ref s_convertFromInvariantString, mi == null ? new object() : mi.CreateDelegate(typeof(Func))); } - if (!(s_convertFromInvariantString is Func convertFromInvariantString)) + if (!(s_convertFromInvariantString is Func convertFromInvariantString)) return false; try diff --git a/src/libraries/System.Private.CoreLib/src/System/CurrentSystemTimeZone.cs b/src/libraries/System.Private.CoreLib/src/System/CurrentSystemTimeZone.cs index 973a95c7253a0..3517d082ac298 100644 --- a/src/libraries/System.Private.CoreLib/src/System/CurrentSystemTimeZone.cs +++ b/src/libraries/System.Private.CoreLib/src/System/CurrentSystemTimeZone.cs @@ -22,7 +22,7 @@ namespace System { [Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")] - internal class CurrentSystemTimeZone : TimeZone + internal sealed class CurrentSystemTimeZone : TimeZone { // Standard offset in ticks to the Universal time if // no daylight saving is in used. diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs index 7814a9f5fdbcf..d936053f40318 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace System { @@ -15,31 +15,26 @@ public static unsafe DateTime UtcNow { get { - ulong fileTime; - s_pfnGetSystemTimeAsFileTime(&fileTime); + ulong fileTimeTmp; // mark only the temp local as address-taken + s_pfnGetSystemTimeAsFileTime(&fileTimeTmp); + ulong fileTime = fileTimeTmp; if (s_systemSupportsLeapSeconds) { - Interop.Kernel32.SYSTEMTIME time; - ulong hundredNanoSecond; + // Query the leap second cache first, which avoids expensive calls to GetFileTimeAsSystemTime. - if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time) != Interop.BOOL.FALSE) + LeapSecondCache cacheValue = s_leapSecondCache; + ulong ticksSinceStartOfCacheValidityWindow = fileTime - cacheValue.OSFileTimeTicksAtStartOfValidityWindow; + if (ticksSinceStartOfCacheValidityWindow < LeapSecondCache.ValidityPeriodInTicks) { - // to keep the time precision - ulong tmp = fileTime; // temp. variable avoids double read from memory - hundredNanoSecond = tmp % TicksPerMillisecond; - } - else - { - Interop.Kernel32.GetSystemTime(&time); - hundredNanoSecond = 0; + return new DateTime(dateData: cacheValue.DotnetDateDataAtStartOfValidityWindow + ticksSinceStartOfCacheValidityWindow); } - return CreateDateTimeFromSystemTime(in time, hundredNanoSecond); + return UpdateLeapSecondCacheAndReturnUtcNow(); // couldn't use the cache, go down the slow path } else { - return new DateTime(fileTime + FileTimeOffset | KindUtc); + return new DateTime(dateData: fileTime + (FileTimeOffset | KindUtc)); } } } @@ -109,7 +104,6 @@ private static unsafe ulong ToFileTimeLeapSecondsAware(long ticks) return fileTime + (uint)tick; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static DateTime CreateDateTimeFromSystemTime(in Interop.Kernel32.SYSTEMTIME time, ulong hundredNanoSecond) { uint year = time.Year; @@ -171,5 +165,171 @@ private static DateTime CreateDateTimeFromSystemTime(in Interop.Kernel32.SYSTEMT return (delegate* unmanaged[SuppressGCTransition])pfnGetSystemTime; } + + private static unsafe DateTime UpdateLeapSecondCacheAndReturnUtcNow() + { + // From conversations with the Windows team, the OS has the ability to update leap second + // data while applications are running. Leap second data is published on WU well ahead of + // the actual event. Additionally, the OS's list of leap seconds will only ever expand + // from the end. There won't be a situation where a leap second will ever be inserted into + // the middle of the list of all known leap seconds. + // + // Normally, this would mean that we could just ask "will a leap second occur in the next + // 24 hours?" and cache this value. However, it's possible that the current machine may have + // deferred updates so long that when a leap second is added to the end of the list, it + // actually occurs in the past (compared to UtcNow). To account for this possibility, we + // limit our cache's lifetime to just a few minutes (the "validity window"). If a deferred + // OS update occurs and a past leap second is added, this limits the window in which our + // cache will return incorrect values. + + Debug.Assert(s_systemSupportsLeapSeconds); + Debug.Assert(LeapSecondCache.ValidityPeriodInTicks < TicksPerDay - TicksPerSecond, "Leap second cache validity window should be less than 23:59:59."); + + ulong fileTimeNow; + s_pfnGetSystemTimeAsFileTime(&fileTimeNow); + + // If we reached this point, our leap second cache is stale, and we need to update it. + // First, convert the FILETIME to a SYSTEMTIME. + + Interop.Kernel32.SYSTEMTIME systemTimeNow; + ulong hundredNanoSecondNow = fileTimeNow % TicksPerMillisecond; + + // We need the FILETIME and the SYSTEMTIME to reflect each other's values. + // If FileTimeToSystemTime fails, call GetSystemTime and try again until it succeeds. + if (Interop.Kernel32.FileTimeToSystemTime(&fileTimeNow, &systemTimeNow) == Interop.BOOL.FALSE) + { + return LowGranularityNonCachedFallback(); + } + + // If we're currently within a positive leap second, early-exit since our cache can't handle + // this situation. Once midnight rolls around the next call to DateTime.UtcNow should update + // the cache correctly. + + if (systemTimeNow.Second >= 60) + { + return CreateDateTimeFromSystemTime(systemTimeNow, hundredNanoSecondNow); + } + + // Our cache will be valid for some amount of time (the "validity window"). + // Check if a leap second will occur within this window. + + ulong fileTimeAtEndOfValidityPeriod = fileTimeNow + LeapSecondCache.ValidityPeriodInTicks; + Interop.Kernel32.SYSTEMTIME systemTimeAtEndOfValidityPeriod; + if (Interop.Kernel32.FileTimeToSystemTime(&fileTimeAtEndOfValidityPeriod, &systemTimeAtEndOfValidityPeriod) == Interop.BOOL.FALSE) + { + return LowGranularityNonCachedFallback(); + } + + ulong fileTimeAtStartOfValidityWindow; + ulong dotnetDateDataAtStartOfValidityWindow; + + // A leap second can only occur at the end of the day, and we can only leap by +/- 1 second + // at a time. To see if a leap second occurs within the upcoming validity window, we can + // compare the 'seconds' values at the start and the end of the window. + + if (systemTimeAtEndOfValidityPeriod.Second == systemTimeNow.Second) + { + // If we reached this block, a leap second will not occur within the validity window. + // We can cache the validity window starting at UtcNow. + + fileTimeAtStartOfValidityWindow = fileTimeNow; + dotnetDateDataAtStartOfValidityWindow = CreateDateTimeFromSystemTime(systemTimeNow, hundredNanoSecondNow)._dateData; + } + else + { + // If we reached this block, a leap second will occur within the validity window. We cannot + // allow the cache to cover this entire window, otherwise the cache will start reporting + // incorrect values once the leap second occurs. To account for this, we slide the validity + // window back a little bit. The window will have the same duration as before, but instead + // of beginning now, we'll choose the proper begin time so that it ends at 23:59:59.000. + + Interop.Kernel32.SYSTEMTIME systemTimeAtBeginningOfDay = systemTimeNow; + systemTimeAtBeginningOfDay.Hour = 0; + systemTimeAtBeginningOfDay.Minute = 0; + systemTimeAtBeginningOfDay.Second = 0; + systemTimeAtBeginningOfDay.Milliseconds = 0; + + ulong fileTimeAtBeginningOfDay; + if (Interop.Kernel32.SystemTimeToFileTime(&systemTimeAtBeginningOfDay, &fileTimeAtBeginningOfDay) == Interop.BOOL.FALSE) + { + return LowGranularityNonCachedFallback(); + } + + // StartOfValidityWindow = MidnightUtc + 23:59:59 - ValidityPeriod + fileTimeAtStartOfValidityWindow = fileTimeAtBeginningOfDay + (TicksPerDay - TicksPerSecond) - LeapSecondCache.ValidityPeriodInTicks; + if (fileTimeNow - fileTimeAtStartOfValidityWindow >= LeapSecondCache.ValidityPeriodInTicks) + { + // If we're inside this block, then we slid the validity window back so far that the current time is no + // longer within the window. This can only occur if the current time is 23:59:59.xxx and the next second is a + // positive leap second (23:59:60.xxx). For example, if the current time is 23:59:59.123, assuming a + // 5-minute validity period, we'll slide the validity window back to [23:54:59.000, 23:59:59.000). + // + // Depending on how the current process is configured, the OS may report time data in one of two ways. If + // the current process is leap-second aware (has the PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND flag set), + // then a SYSTEMTIME object will report leap seconds by setting the 'wSecond' field to 60. If the current + // process is not leap-second aware, the OS will compress the last two seconds of the day as follows. + // + // Actual time GetSystemTime returns + // ======================================== + // 23:59:59.000 23:59:59.000 + // 23:59:59.500 23:59:59.250 + // 23:59:60.000 23:59:59.500 + // 23:59:60.500 23:59:59.750 + // 00:00:00.000 00:00:00.000 (next day) + // + // In this scenario, we'll skip the caching logic entirely, relying solely on the OS-provided SYSTEMTIME + // struct to tell us how to interpret the time information. + + Debug.Assert(systemTimeNow.Hour == 23 && systemTimeNow.Minute == 59 && systemTimeNow.Second == 59); + return CreateDateTimeFromSystemTime(systemTimeNow, hundredNanoSecondNow); + } + + dotnetDateDataAtStartOfValidityWindow = CreateDateTimeFromSystemTime(systemTimeAtBeginningOfDay, 0)._dateData + (TicksPerDay - TicksPerSecond) - LeapSecondCache.ValidityPeriodInTicks; + } + + // Finally, update the cache and return UtcNow. + + Debug.Assert(fileTimeNow - fileTimeAtStartOfValidityWindow < LeapSecondCache.ValidityPeriodInTicks, "We should be within the validity window."); + Volatile.Write(ref s_leapSecondCache, new LeapSecondCache() + { + OSFileTimeTicksAtStartOfValidityWindow = fileTimeAtStartOfValidityWindow, + DotnetDateDataAtStartOfValidityWindow = dotnetDateDataAtStartOfValidityWindow + }); + + return new DateTime(dateData: dotnetDateDataAtStartOfValidityWindow + fileTimeNow - fileTimeAtStartOfValidityWindow); + + static DateTime LowGranularityNonCachedFallback() + { + // If we reached this point, one of the Win32 APIs FileTimeToSystemTime or SystemTimeToFileTime + // failed. This should never happen in practice, as this would imply that the Win32 API + // GetSystemTimeAsFileTime returned an invalid value to us at the start of the calling method. + // But, just to be safe, if this ever does happen, we'll bypass the caching logic entirely + // and fall back to GetSystemTime. This results in a loss of granularity (millisecond-only, + // not rdtsc-based), but at least it means we won't fail. + + Debug.Fail("Our Win32 calls should never fail."); + + Interop.Kernel32.SYSTEMTIME systemTimeNow; + Interop.Kernel32.GetSystemTime(&systemTimeNow); + return CreateDateTimeFromSystemTime(systemTimeNow, 0); + } + } + + // The leap second cache. May be accessed by multiple threads simultaneously. + // Writers must not mutate the object's fields after the reference is published. + // Readers are not required to use volatile semantics. + private static LeapSecondCache s_leapSecondCache = new LeapSecondCache(); + + private sealed class LeapSecondCache + { + // The length of the validity window. Must be less than 23:59:59. + internal const ulong ValidityPeriodInTicks = TicksPerMinute * 5; + + // The FILETIME value at the beginning of the validity window. + internal ulong OSFileTimeTicksAtStartOfValidityWindow; + + // The DateTime._dateData value at the beginning of the validity window. + internal ulong DotnetDateDataAtStartOfValidityWindow; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs b/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs index 6b275ff8b21ca..25719351c4f0d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs @@ -1257,7 +1257,7 @@ private enum Primitives String = 1 << TypeCode.String, } - internal class BinderState + internal sealed class BinderState { internal readonly int[] _argsMap; internal readonly int _originalSize; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/DynamicallyAccessedMembersAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/DynamicallyAccessedMembersAttribute.cs index 393ebe511bbe6..160d63570e850 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/DynamicallyAccessedMembersAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/DynamicallyAccessedMembersAttribute.cs @@ -23,7 +23,8 @@ namespace System.Diagnostics.CodeAnalysis /// [AttributeUsage( AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method, + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresUnreferencedCodeAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresUnreferencedCodeAttribute.cs index bf99d389fe820..bcb31753c4084 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresUnreferencedCodeAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresUnreferencedCodeAttribute.cs @@ -11,7 +11,7 @@ namespace System.Diagnostics.CodeAnalysis /// This allows tools to understand which methods are unsafe to call when removing unreferenced /// code from an application. /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] #if SYSTEM_PRIVATE_CORELIB public #else diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs index e3f6632f59fb4..a1f724612d467 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/ActivityTracker.cs @@ -38,7 +38,7 @@ namespace System.Diagnostics.Tracing /// /// On any normal event log the event with activityTracker.CurrentActivityId /// - internal class ActivityTracker + internal sealed class ActivityTracker { /// /// Called on work item begins. The activity name = providerName + activityName without 'Start' suffix. @@ -300,7 +300,7 @@ private static string NormalizeActivityName(string providerName, string activity /// the 'list of live parents' which indicate of those ancestors, which are alive (if they /// are not marked dead they are alive). /// - private class ActivityInfo + private sealed class ActivityInfo { public ActivityInfo(string name, long uniqueId, ActivityInfo? creator, Guid activityIDToRestore, EventActivityOptions options) { @@ -618,9 +618,9 @@ private void ActivityChanging(AsyncLocalValueChangedArgs args) /// /// [EventSource(Name = "Microsoft.Tasks.Nuget")] - internal class TplEventSource : EventSource + internal sealed class TplEventSource : EventSource { - public class Keywords + public static class Keywords { public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80; public const EventKeywords Debug = (EventKeywords)0x20000; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs index ce521a07fdad3..5d85b20e7b6bf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterGroup.cs @@ -15,8 +15,10 @@ namespace Microsoft.Diagnostics.Tracing namespace System.Diagnostics.Tracing #endif { +#if NETCOREAPP [UnsupportedOSPlatform("browser")] - internal class CounterGroup +#endif + internal sealed class CounterGroup { private readonly EventSource _eventSource; private readonly List _counters; @@ -271,10 +273,9 @@ private static void PollForValues() // We cache these outside of the scope of s_counterGroupLock because // calling into the callbacks can cause a re-entrancy into CounterGroup.Enable() // and result in a deadlock. (See https://github.com/dotnet/runtime/issues/40190 for details) - List onTimers = new List(); + var onTimers = new List(); while (true) { - onTimers.Clear(); int sleepDurationInMilliseconds = int.MaxValue; lock (s_counterGroupLock) { @@ -284,7 +285,7 @@ private static void PollForValues() DateTime now = DateTime.UtcNow; if (counterGroup._nextPollingTimeStamp < now + new TimeSpan(0, 0, 0, 0, 1)) { - onTimers.Add(() => counterGroup.OnTimer()); + onTimers.Add(counterGroup); } int millisecondsTillNextPoll = (int)((counterGroup._nextPollingTimeStamp - now).TotalMilliseconds); @@ -292,10 +293,11 @@ private static void PollForValues() sleepDurationInMilliseconds = Math.Min(sleepDurationInMilliseconds, millisecondsTillNextPoll); } } - foreach (Action onTimer in onTimers) + foreach (CounterGroup onTimer in onTimers) { - onTimer.Invoke(); + onTimer.OnTimer(); } + onTimers.Clear(); if (sleepDurationInMilliseconds == int.MaxValue) { sleepDurationInMilliseconds = -1; // WaitOne uses -1 to mean infinite diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterPayload.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterPayload.cs index cb8fbaf744b86..6c872159e65de 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterPayload.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/CounterPayload.cs @@ -15,7 +15,7 @@ namespace System.Diagnostics.Tracing #endif { [EventData] - internal class CounterPayload : IEnumerable> + internal sealed class CounterPayload : IEnumerable> { public string? Name { get; set; } @@ -76,7 +76,7 @@ IEnumerator IEnumerable.GetEnumerator() } [EventData] - internal class IncrementingCounterPayload : IEnumerable> + internal sealed class IncrementingCounterPayload : IEnumerable> { public string? Name { get; set; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs index 16724c371c49f..0c23cf99222b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/DiagnosticCounter.cs @@ -20,7 +20,9 @@ namespace System.Diagnostics.Tracing /// DiagnosticCounter is an abstract class that serves as the parent class for various Counter* classes, /// namely EventCounter, PollingCounter, IncrementingEventCounter, and IncrementingPollingCounter. /// +#if NETCOREAPP [UnsupportedOSPlatform("browser")] +#endif public abstract class DiagnosticCounter : IDisposable { /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs index a5ae224e4e589..8a53d56908210 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventCounter.cs @@ -24,7 +24,9 @@ namespace System.Diagnostics.Tracing /// See https://github.com/dotnet/runtime/blob/main/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestEventCounter.cs /// which shows tests, which are also useful in seeing actual use. /// +#if NETCOREAPP [UnsupportedOSPlatform("browser")] +#endif public partial class EventCounter : DiagnosticCounter { /// @@ -198,7 +200,7 @@ protected void Flush() /// This is the payload that is sent in the with EventSource.Write /// [EventData] - internal class CounterPayloadType + internal sealed class CounterPayloadType { public CounterPayloadType(CounterPayload payload) { Payload = payload; } public CounterPayload Payload { get; set; } 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 9e1d568f83279..ff9278b4a49eb 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 @@ -2401,7 +2401,7 @@ internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string? eve /// /// This class lets us hook the 'OnEventCommand' from the eventSource. /// - private class OverrideEventProvider : EventProvider + private sealed class OverrideEventProvider : EventProvider { public OverrideEventProvider(EventSource eventSource, EventProviderType providerType) : base(providerType) @@ -5127,7 +5127,7 @@ public bool this[int perEventSourceSessionId] /// one EventListener (although EventDispatcher does not 'remember' the EventSource it is /// associated with. /// - internal class EventDispatcher + internal sealed class EventDispatcher { internal EventDispatcher(EventDispatcher? next, bool[]? eventEnabled, EventListener listener) { @@ -5181,7 +5181,7 @@ public enum EventManifestOptions /// ManifestBuilder is designed to isolate the details of the message of the event from the /// rest of EventSource. This one happens to create XML. /// - internal class ManifestBuilder + internal sealed class ManifestBuilder { /// /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'. @@ -6087,7 +6087,7 @@ private int TranslateIndexToManifestConvention(int idx, string evtName) } #if FEATURE_MANAGED_ETW_CHANNELS - private class ChannelInfo + private sealed class ChannelInfo { public string? Name; public ulong Keywords; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs index 633ab218a5e16..13e5f1906ec32 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingEventCounter.cs @@ -96,7 +96,7 @@ internal void UpdateMetric() /// This is the payload that is sent in the with EventSource.Write /// [EventData] - internal class IncrementingEventCounterPayloadType + internal sealed class IncrementingEventCounterPayloadType { public IncrementingEventCounterPayloadType(IncrementingCounterPayload payload) { Payload = payload; } public IncrementingCounterPayload Payload { get; set; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs index 1097c38f80fe9..86d46a040faa5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/IncrementingPollingCounter.cs @@ -99,7 +99,7 @@ internal override void WritePayload(float intervalSec, int pollingIntervalMillis /// This is the payload that is sent in the with EventSource.Write /// [EventData] - internal class IncrementingPollingCounterPayloadType + internal sealed class IncrementingPollingCounterPayloadType { public IncrementingPollingCounterPayloadType(IncrementingCounterPayload payload) { Payload = payload; } public IncrementingCounterPayload Payload { get; set; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs index f7de902164f11..bc5901403874c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.cs @@ -1,10 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace System.Diagnostics.Tracing { /// @@ -27,6 +23,7 @@ internal sealed partial class NativeRuntimeEventSource : EventSource // as you can't make a constructor partial. private NativeRuntimeEventSource(int _) { } +#if FEATURE_PERFTRACING /// /// Dispatch a single event with the specified event ID and payload. /// @@ -62,5 +59,6 @@ internal unsafe void ProcessEvent(uint eventID, uint osThreadID, DateTime timeSt childActivityID: &childActivityId, args: decodedPayloadFields); } +#endif // FEATURE_PERFTRACING } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs index 5b3063f934c31..fee9867d2c9fe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/PollingCounter.cs @@ -89,7 +89,7 @@ internal override void WritePayload(float intervalSec, int pollingIntervalMillis /// This is the payload that is sent in the with EventSource.Write /// [EventData] - internal class PollingPayloadType + internal sealed class PollingPayloadType { public PollingPayloadType(CounterPayload payload) { Payload = payload; } public CounterPayload Payload { get; set; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs index 053157c01d36c..8b37a0b70cc11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/RuntimeEventSource.cs @@ -28,6 +28,7 @@ internal sealed partial class RuntimeEventSource : EventSource private IncrementingPollingCounter? _allocRateCounter; private PollingCounter? _timerCounter; private PollingCounter? _fragmentationCounter; + private PollingCounter? _committedCounter; #if !MONO private IncrementingPollingCounter? _exceptionCounter; @@ -76,6 +77,7 @@ protected override void OnEventCommand(EventCommandEventArgs command) var gcInfo = GC.GetGCMemoryInfo(); return gcInfo.HeapSizeBytes != 0 ? gcInfo.FragmentedBytes * 100d / gcInfo.HeapSizeBytes : 0; }) { DisplayName = "GC Fragmentation", DisplayUnits = "%" }; + _committedCounter ??= new PollingCounter("gc-committed", this, () => GC.GetGCMemoryInfo().TotalCommittedBytes) { DisplayName = "GC Committed Bytes", DisplayUnits = "B" }; #if !MONO _exceptionCounter ??= new IncrementingPollingCounter("exception-count", this, () => Exception.GetExceptionCount()) { DisplayName = "Exception Count", DisplayRateTimeScale = new TimeSpan(0, 0, 1) }; _gcTimeCounter ??= new PollingCounter("time-in-gc", this, () => GC.GetLastGCPercentTimeInGC()) { DisplayName = "% Time in GC since last GC", DisplayUnits = "%" }; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs index 005ffb19f25df..98cd97f567a5e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs @@ -20,11 +20,11 @@ namespace System.Diagnostics.Tracing /// EventSource APIs. /// Preserving the order of the elements as they were found inside user defined types is the most important characteristic of this class. /// - internal class EventPayload : IDictionary + internal sealed class EventPayload : IDictionary { - internal EventPayload(List payloadNames, List payloadValues) + internal EventPayload(string[] payloadNames, object?[] payloadValues) { - Debug.Assert(payloadNames.Count == payloadValues.Count); + Debug.Assert(payloadNames.Length == payloadValues.Length); m_names = payloadNames; m_values = payloadValues; @@ -88,7 +88,7 @@ public bool ContainsKey(string key) return false; } - public int Count => m_names.Count; + public int Count => m_names.Length; public bool IsReadOnly => true; @@ -142,8 +142,8 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out object? value) } #region private - private readonly List m_names; - private readonly List m_values; + private readonly string[] m_names; + private readonly object?[] m_values; #endregion } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs index cd5db860c31a9..776dc1ca652ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs @@ -15,7 +15,7 @@ namespace System.Diagnostics.Tracing /// TraceLogging: Contains the information needed to generate tracelogging /// metadata for an event field. /// - internal class FieldMetadata + internal sealed class FieldMetadata { /// /// Name of the field diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs index 0cc42deb4fde2..1fc44ac48917c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs @@ -77,13 +77,13 @@ public override void WriteData(PropertyValue value) { if (this.properties != null) { - var membersNames = new List(); - var membersValues = new List(); + var membersNames = new string[this.properties.Length]; + var membersValues = new object?[this.properties.Length]; for (int i = 0; i < this.properties.Length; i++) { object? propertyValue = properties[i].propertyInfo.GetValue(value); - membersNames.Add(properties[i].name); - membersValues.Add(properties[i].typeInfo.GetData(propertyValue)); + membersNames[i] = properties[i].name; + membersValues[i] = properties[i].typeInfo.GetData(propertyValue); } return new EventPayload(membersNames, membersValues); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs index 880c9a5941fd5..0396063927dab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs @@ -22,8 +22,12 @@ namespace System.Diagnostics.Tracing /// internal sealed class NullTypeInfo : TraceLoggingTypeInfo { + private static NullTypeInfo? s_instance; + public NullTypeInfo() : base(typeof(EmptyStruct)) { } + public static TraceLoggingTypeInfo Instance() => s_instance ??= new NullTypeInfo(); + public override void WriteMetadata( TraceLoggingMetadataCollector collector, string? name, @@ -47,22 +51,35 @@ public override void WriteData(PropertyValue value) /// internal sealed class ScalarTypeInfo : TraceLoggingTypeInfo { - private readonly Func formatFunc; + private static ScalarTypeInfo? s_boolean; + private static ScalarTypeInfo? s_byte; + private static ScalarTypeInfo? s_sbyte; + private static ScalarTypeInfo? s_char; + private static ScalarTypeInfo? s_int16; + private static ScalarTypeInfo? s_uint16; + private static ScalarTypeInfo? s_int32; + private static ScalarTypeInfo? s_uint32; + private static ScalarTypeInfo? s_int64; + private static ScalarTypeInfo? s_uint64; + private static ScalarTypeInfo? s_intptr; + private static ScalarTypeInfo? s_uintptr; + private static ScalarTypeInfo? s_single; + private static ScalarTypeInfo? s_double; + private static ScalarTypeInfo? s_guid; + private readonly TraceLoggingDataType nativeFormat; private ScalarTypeInfo( Type type, - Func formatFunc, TraceLoggingDataType nativeFormat) : base(type) { - this.formatFunc = formatFunc; this.nativeFormat = nativeFormat; } public override void WriteMetadata(TraceLoggingMetadataCollector collector, string? name, EventFieldFormat format) { - collector.AddScalar(name!, formatFunc(format, nativeFormat)); + collector.AddScalar(name!, Statics.FormatScalar(format, nativeFormat)); } public override void WriteData(PropertyValue value) @@ -70,48 +87,60 @@ public override void WriteData(PropertyValue value) TraceLoggingDataCollector.AddScalar(value); } - public static TraceLoggingTypeInfo Boolean() { return new ScalarTypeInfo(typeof(bool), Statics.Format8, TraceLoggingDataType.Boolean8); } - public static TraceLoggingTypeInfo Byte() { return new ScalarTypeInfo(typeof(byte), Statics.Format8, TraceLoggingDataType.UInt8); } - public static TraceLoggingTypeInfo SByte() { return new ScalarTypeInfo(typeof(sbyte), Statics.Format8, TraceLoggingDataType.Int8); } - public static TraceLoggingTypeInfo Char() { return new ScalarTypeInfo(typeof(char), Statics.Format16, TraceLoggingDataType.Char16); } - public static TraceLoggingTypeInfo Int16() { return new ScalarTypeInfo(typeof(short), Statics.Format16, TraceLoggingDataType.Int16); } - public static TraceLoggingTypeInfo UInt16() { return new ScalarTypeInfo(typeof(ushort), Statics.Format16, TraceLoggingDataType.UInt16); } - public static TraceLoggingTypeInfo Int32() { return new ScalarTypeInfo(typeof(int), Statics.Format32, TraceLoggingDataType.Int32); } - public static TraceLoggingTypeInfo UInt32() { return new ScalarTypeInfo(typeof(uint), Statics.Format32, TraceLoggingDataType.UInt32); } - public static TraceLoggingTypeInfo Int64() { return new ScalarTypeInfo(typeof(long), Statics.Format64, TraceLoggingDataType.Int64); } - public static TraceLoggingTypeInfo UInt64() { return new ScalarTypeInfo(typeof(ulong), Statics.Format64, TraceLoggingDataType.UInt64); } - public static TraceLoggingTypeInfo IntPtr() { return new ScalarTypeInfo(typeof(IntPtr), Statics.FormatPtr, Statics.IntPtrType); } - public static TraceLoggingTypeInfo UIntPtr() { return new ScalarTypeInfo(typeof(UIntPtr), Statics.FormatPtr, Statics.UIntPtrType); } - public static TraceLoggingTypeInfo Single() { return new ScalarTypeInfo(typeof(float), Statics.Format32, TraceLoggingDataType.Float); } - public static TraceLoggingTypeInfo Double() { return new ScalarTypeInfo(typeof(double), Statics.Format64, TraceLoggingDataType.Double); } - public static TraceLoggingTypeInfo Guid() { return new ScalarTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid); } + public static TraceLoggingTypeInfo Boolean() => s_boolean ??= new ScalarTypeInfo(typeof(bool), TraceLoggingDataType.Boolean8); + public static TraceLoggingTypeInfo Byte() => s_byte ??= new ScalarTypeInfo(typeof(byte), TraceLoggingDataType.UInt8); + public static TraceLoggingTypeInfo SByte() => s_sbyte ??= new ScalarTypeInfo(typeof(sbyte), TraceLoggingDataType.Int8); + public static TraceLoggingTypeInfo Char() => s_char ??= new ScalarTypeInfo(typeof(char), TraceLoggingDataType.Char16); + public static TraceLoggingTypeInfo Int16() => s_int16 ??= new ScalarTypeInfo(typeof(short), TraceLoggingDataType.Int16); + public static TraceLoggingTypeInfo UInt16() => s_uint16 ??= new ScalarTypeInfo(typeof(ushort), TraceLoggingDataType.UInt16); + public static TraceLoggingTypeInfo Int32() => s_int32 ??= new ScalarTypeInfo(typeof(int), TraceLoggingDataType.Int32); + public static TraceLoggingTypeInfo UInt32() => s_uint32 ??= new ScalarTypeInfo(typeof(uint), TraceLoggingDataType.UInt32); + public static TraceLoggingTypeInfo Int64() => s_int64 ??= new ScalarTypeInfo(typeof(long), TraceLoggingDataType.Int64); + public static TraceLoggingTypeInfo UInt64() => s_uint64 ??= new ScalarTypeInfo(typeof(ulong), TraceLoggingDataType.UInt64); + public static TraceLoggingTypeInfo IntPtr() => s_intptr ??= new ScalarTypeInfo(typeof(IntPtr), Statics.IntPtrType); + public static TraceLoggingTypeInfo UIntPtr() => s_uintptr ??= new ScalarTypeInfo(typeof(UIntPtr), Statics.UIntPtrType); + public static TraceLoggingTypeInfo Single() => s_single ??= new ScalarTypeInfo(typeof(float), TraceLoggingDataType.Float); + public static TraceLoggingTypeInfo Double() => s_double ??= new ScalarTypeInfo(typeof(double), TraceLoggingDataType.Double); + public static TraceLoggingTypeInfo Guid() => s_guid ??= new ScalarTypeInfo(typeof(Guid), TraceLoggingDataType.Guid); } - /// /// Type handler for arrays of scalars /// internal sealed class ScalarArrayTypeInfo : TraceLoggingTypeInfo { - private readonly Func formatFunc; + private static ScalarArrayTypeInfo? s_boolean; + private static ScalarArrayTypeInfo? s_byte; + private static ScalarArrayTypeInfo? s_sbyte; + private static ScalarArrayTypeInfo? s_char; + private static ScalarArrayTypeInfo? s_int16; + private static ScalarArrayTypeInfo? s_uint16; + private static ScalarArrayTypeInfo? s_int32; + private static ScalarArrayTypeInfo? s_uint32; + private static ScalarArrayTypeInfo? s_int64; + private static ScalarArrayTypeInfo? s_uint64; + private static ScalarArrayTypeInfo? s_intptr; + private static ScalarArrayTypeInfo? s_uintptr; + private static ScalarArrayTypeInfo? s_single; + private static ScalarArrayTypeInfo? s_double; + private static ScalarArrayTypeInfo? s_guid; + private readonly TraceLoggingDataType nativeFormat; private readonly int elementSize; private ScalarArrayTypeInfo( Type type, - Func formatFunc, TraceLoggingDataType nativeFormat, int elementSize) : base(type) { - this.formatFunc = formatFunc; this.nativeFormat = nativeFormat; this.elementSize = elementSize; } public override void WriteMetadata(TraceLoggingMetadataCollector collector, string? name, EventFieldFormat format) { - collector.AddArray(name!, formatFunc(format, nativeFormat)); + collector.AddArray(name!, Statics.FormatScalar(format, nativeFormat)); } public override void WriteData(PropertyValue value) @@ -119,21 +148,21 @@ public override void WriteData(PropertyValue value) TraceLoggingDataCollector.AddArray(value, elementSize); } - public static TraceLoggingTypeInfo Boolean() { return new ScalarArrayTypeInfo(typeof(bool[]), Statics.Format8, TraceLoggingDataType.Boolean8, sizeof(bool)); } - public static TraceLoggingTypeInfo Byte() { return new ScalarArrayTypeInfo(typeof(byte[]), Statics.Format8, TraceLoggingDataType.UInt8, sizeof(byte)); } - public static TraceLoggingTypeInfo SByte() { return new ScalarArrayTypeInfo(typeof(sbyte[]), Statics.Format8, TraceLoggingDataType.Int8, sizeof(sbyte)); } - public static TraceLoggingTypeInfo Char() { return new ScalarArrayTypeInfo(typeof(char[]), Statics.Format16, TraceLoggingDataType.Char16, sizeof(char)); } - public static TraceLoggingTypeInfo Int16() { return new ScalarArrayTypeInfo(typeof(short[]), Statics.Format16, TraceLoggingDataType.Int16, sizeof(short)); } - public static TraceLoggingTypeInfo UInt16() { return new ScalarArrayTypeInfo(typeof(ushort[]), Statics.Format16, TraceLoggingDataType.UInt16, sizeof(ushort)); } - public static TraceLoggingTypeInfo Int32() { return new ScalarArrayTypeInfo(typeof(int[]), Statics.Format32, TraceLoggingDataType.Int32, sizeof(int)); } - public static TraceLoggingTypeInfo UInt32() { return new ScalarArrayTypeInfo(typeof(uint[]), Statics.Format32, TraceLoggingDataType.UInt32, sizeof(uint)); } - public static TraceLoggingTypeInfo Int64() { return new ScalarArrayTypeInfo(typeof(long[]), Statics.Format64, TraceLoggingDataType.Int64, sizeof(long)); } - public static TraceLoggingTypeInfo UInt64() { return new ScalarArrayTypeInfo(typeof(ulong[]), Statics.Format64, TraceLoggingDataType.UInt64, sizeof(ulong)); } - public static TraceLoggingTypeInfo IntPtr() { return new ScalarArrayTypeInfo(typeof(IntPtr[]), Statics.FormatPtr, Statics.IntPtrType, System.IntPtr.Size); } - public static TraceLoggingTypeInfo UIntPtr() { return new ScalarArrayTypeInfo(typeof(UIntPtr[]), Statics.FormatPtr, Statics.UIntPtrType, System.IntPtr.Size); } - public static TraceLoggingTypeInfo Single() { return new ScalarArrayTypeInfo(typeof(float[]), Statics.Format32, TraceLoggingDataType.Float, sizeof(float)); } - public static TraceLoggingTypeInfo Double() { return new ScalarArrayTypeInfo(typeof(double[]), Statics.Format64, TraceLoggingDataType.Double, sizeof(double)); } - public static unsafe TraceLoggingTypeInfo Guid() { return new ScalarArrayTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid, sizeof(Guid)); } + public static TraceLoggingTypeInfo Boolean() => s_boolean ??= new ScalarArrayTypeInfo(typeof(bool[]), TraceLoggingDataType.Boolean8, sizeof(bool)); + public static TraceLoggingTypeInfo Byte() => s_byte ??= new ScalarArrayTypeInfo(typeof(byte[]), TraceLoggingDataType.UInt8, sizeof(byte)); + public static TraceLoggingTypeInfo SByte() => s_sbyte ??= new ScalarArrayTypeInfo(typeof(sbyte[]), TraceLoggingDataType.Int8, sizeof(sbyte)); + public static TraceLoggingTypeInfo Char() => s_char ??= new ScalarArrayTypeInfo(typeof(char[]), TraceLoggingDataType.Char16, sizeof(char)); + public static TraceLoggingTypeInfo Int16() => s_int16 ??= new ScalarArrayTypeInfo(typeof(short[]), TraceLoggingDataType.Int16, sizeof(short)); + public static TraceLoggingTypeInfo UInt16() => s_uint16 ??= new ScalarArrayTypeInfo(typeof(ushort[]), TraceLoggingDataType.UInt16, sizeof(ushort)); + public static TraceLoggingTypeInfo Int32() => s_int32 ??= new ScalarArrayTypeInfo(typeof(int[]), TraceLoggingDataType.Int32, sizeof(int)); + public static TraceLoggingTypeInfo UInt32() => s_uint32 ??= new ScalarArrayTypeInfo(typeof(uint[]), TraceLoggingDataType.UInt32, sizeof(uint)); + public static TraceLoggingTypeInfo Int64() => s_int64 ??= new ScalarArrayTypeInfo(typeof(long[]), TraceLoggingDataType.Int64, sizeof(long)); + public static TraceLoggingTypeInfo UInt64() => s_uint64 ??= new ScalarArrayTypeInfo(typeof(ulong[]), TraceLoggingDataType.UInt64, sizeof(ulong)); + public static TraceLoggingTypeInfo IntPtr() => s_intptr ??= new ScalarArrayTypeInfo(typeof(IntPtr[]), Statics.IntPtrType, System.IntPtr.Size); + public static TraceLoggingTypeInfo UIntPtr() => s_uintptr ??= new ScalarArrayTypeInfo(typeof(UIntPtr[]), Statics.UIntPtrType, System.IntPtr.Size); + public static TraceLoggingTypeInfo Single() => s_single ??= new ScalarArrayTypeInfo(typeof(float[]), TraceLoggingDataType.Float, sizeof(float)); + public static TraceLoggingTypeInfo Double() => s_double ??= new ScalarArrayTypeInfo(typeof(double[]), TraceLoggingDataType.Double, sizeof(double)); + public static unsafe TraceLoggingTypeInfo Guid() => s_guid ??= new ScalarArrayTypeInfo(typeof(Guid[]), TraceLoggingDataType.Guid, sizeof(Guid)); } /// @@ -141,8 +170,12 @@ public override void WriteData(PropertyValue value) /// internal sealed class StringTypeInfo : TraceLoggingTypeInfo { + private static StringTypeInfo? s_instance; + public StringTypeInfo() : base(typeof(string)) { } + public static TraceLoggingTypeInfo Instance() => s_instance ??= new StringTypeInfo(); + public override void WriteMetadata( TraceLoggingMetadataCollector collector, string? name, @@ -176,8 +209,12 @@ public override object GetData(object? value) /// internal sealed class DateTimeTypeInfo : TraceLoggingTypeInfo { + private static DateTimeTypeInfo? s_instance; + public DateTimeTypeInfo() : base(typeof(DateTime)) { } + public static TraceLoggingTypeInfo Instance() => s_instance ??= new DateTimeTypeInfo(); + public override void WriteMetadata( TraceLoggingMetadataCollector collector, string? name, @@ -204,8 +241,12 @@ public override void WriteData(PropertyValue value) /// internal sealed class DateTimeOffsetTypeInfo : TraceLoggingTypeInfo { + private static DateTimeOffsetTypeInfo? s_instance; + public DateTimeOffsetTypeInfo() : base(typeof(DateTimeOffset)) { } + public static TraceLoggingTypeInfo Instance() => s_instance ??= new DateTimeOffsetTypeInfo(); + public override void WriteMetadata(TraceLoggingMetadataCollector collector, string? name, EventFieldFormat format) { TraceLoggingMetadataCollector group = collector.AddGroup(name); @@ -227,8 +268,12 @@ public override void WriteData(PropertyValue value) /// internal sealed class TimeSpanTypeInfo : TraceLoggingTypeInfo { + private static TimeSpanTypeInfo? s_instance; + public TimeSpanTypeInfo() : base(typeof(TimeSpan)) { } + public static TraceLoggingTypeInfo Instance() => s_instance ??= new TimeSpanTypeInfo(); + public override void WriteMetadata( TraceLoggingMetadataCollector collector, string? name, @@ -248,8 +293,12 @@ public override void WriteData(PropertyValue value) /// internal sealed class DecimalTypeInfo : TraceLoggingTypeInfo { + private static DecimalTypeInfo? s_instance; + public DecimalTypeInfo() : base(typeof(decimal)) { } + public static TraceLoggingTypeInfo Instance() => s_instance ??= new DecimalTypeInfo(); + public override void WriteMetadata( TraceLoggingMetadataCollector collector, string? name, diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/Statics.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/Statics.cs index 04d6843d3dc45..66e3a0b3729b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/Statics.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/Statics.cs @@ -313,6 +313,16 @@ public static TraceLoggingDataType FormatPtr( }; } + public static TraceLoggingDataType FormatScalar(EventFieldFormat format, TraceLoggingDataType nativeFormat) => + nativeFormat switch + { + TraceLoggingDataType.Boolean8 or TraceLoggingDataType.Int8 or TraceLoggingDataType.UInt8 => Format8(format, nativeFormat), + TraceLoggingDataType.Char16 or TraceLoggingDataType.Int16 or TraceLoggingDataType.UInt16 => Format16(format, nativeFormat), + TraceLoggingDataType.Int32 or TraceLoggingDataType.UInt32 or TraceLoggingDataType.Float => Format32(format, nativeFormat), + TraceLoggingDataType.Int64 or TraceLoggingDataType.UInt64 or TraceLoggingDataType.Double => Format64(format, nativeFormat), + _ => MakeDataType(nativeFormat, format), + }; + #endregion #region Reflection helpers @@ -480,7 +490,7 @@ public static TraceLoggingTypeInfo CreateDefaultTypeInfo( if (dataType == typeof(string)) { - result = new StringTypeInfo(); + result = StringTypeInfo.Instance(); } else if (dataType == typeof(bool)) { @@ -532,11 +542,11 @@ public static TraceLoggingTypeInfo CreateDefaultTypeInfo( } else if (dataType == typeof(DateTime)) { - result = new DateTimeTypeInfo(); + result = DateTimeTypeInfo.Instance(); } else if (dataType == typeof(decimal)) { - result = new DecimalTypeInfo(); + result = DecimalTypeInfo.Instance(); } else if (dataType == typeof(IntPtr)) { @@ -552,15 +562,15 @@ public static TraceLoggingTypeInfo CreateDefaultTypeInfo( } else if (dataType == typeof(TimeSpan)) { - result = new TimeSpanTypeInfo(); + result = TimeSpanTypeInfo.Instance(); } else if (dataType == typeof(DateTimeOffset)) { - result = new DateTimeOffsetTypeInfo(); + result = DateTimeOffsetTypeInfo.Instance(); } else if (dataType == typeof(EmptyStruct)) { - result = new NullTypeInfo(); + result = NullTypeInfo.Instance(); } else if (IsGenericMatch(dataType, typeof(Nullable<>))) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs index e0f2943cbce75..598898fc1b772 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs @@ -14,7 +14,7 @@ namespace System.Diagnostics.Tracing /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo. /// An instance of this type is provided to the TypeInfo.WriteMetadata method. /// - internal class TraceLoggingMetadataCollector + internal sealed class TraceLoggingMetadataCollector { private readonly Impl impl; private readonly FieldMetadata? currentGroup; @@ -313,7 +313,7 @@ private void AddField(FieldMetadata fieldMetadata) } } - private class Impl + private sealed class Impl { internal readonly List fields = new List(); internal short scratchSize; diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs index 51ce2ce2ce4f3..c341452b1fbbe 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.cs @@ -14,7 +14,7 @@ namespace System.Diagnostics.Tracing { - internal class XplatEventLogger : EventListener + internal sealed class XplatEventLogger : EventListener { public XplatEventLogger() {} diff --git a/src/libraries/System.Private.CoreLib/src/System/Exception.cs b/src/libraries/System.Private.CoreLib/src/System/Exception.cs index 14e84a603cce4..cef527ee50d15 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Exception.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Exception.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Diagnostics; using System.Runtime.Serialization; +using System.Text; namespace System { @@ -197,5 +198,34 @@ public int HResult public new Type GetType() => base.GetType(); partial void RestoreRemoteStackTrace(SerializationInfo info, StreamingContext context); + + [StackTraceHidden] + internal void SetCurrentStackTrace() + { + if (!CanSetRemoteStackTrace()) + { + return; // early-exit + } + + // Store the current stack trace into the "remote" stack trace, which was originally introduced to support + // remoting of exceptions cross app-domain boundaries, and is thus concatenated into Exception.StackTrace + // when it's retrieved. + var sb = new StringBuilder(256); + new StackTrace(fNeedFileInfo: true).ToString(System.Diagnostics.StackTrace.TraceFormat.TrailingNewLine, sb); + sb.AppendLine(SR.Exception_EndStackTraceFromPreviousThrow); + _remoteStackTraceString = sb.ToString(); + } + + internal void SetRemoteStackTrace(string stackTrace) + { + if (!CanSetRemoteStackTrace()) + { + return; // early-exit + } + + // Store the provided text into the "remote" stack trace, following the same format SetCurrentStackTrace + // would have generated. + _remoteStackTraceString = stackTrace + Environment.NewLineConst + SR.Exception_EndStackTraceFromPreviousThrow + Environment.NewLineConst; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs b/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs index 8aadade19cf4e..9754b21ef79f3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/GCMemoryInfo.cs @@ -54,7 +54,7 @@ public enum GCKind }; [StructLayout(LayoutKind.Sequential)] - internal class GCMemoryInfoData + internal sealed class GCMemoryInfoData { internal long _highMemoryLoadThresholdBytes; internal long _totalAvailableMemoryBytes; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs index 5a56d731ecbe8..efece6f4d571a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs @@ -29,7 +29,7 @@ internal enum CalendarDataType AbbrevEraNames = 14, } - internal partial class CalendarData + internal sealed partial class CalendarData { private bool IcuLoadCalendarDataFromSystem(string localeName, CalendarId calendarId) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs index ea0bcbb802695..4f47cd2685455 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Nls.cs @@ -8,7 +8,7 @@ namespace System.Globalization { - internal partial class CalendarData + internal sealed partial class CalendarData { // Get native two digit year max internal static int NlsGetTwoDigitYearMax(CalendarId calendarId) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs index 0834a91de7af1..22f473247620e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs @@ -3,7 +3,7 @@ namespace System.Globalization { - internal partial class CalendarData + internal sealed partial class CalendarData { private bool LoadCalendarDataFromSystemCore(string localeName, CalendarId calendarId) => IcuLoadCalendarDataFromSystem(localeName, calendarId); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs index cca0fb24cfc8a..7e538c8944671 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Windows.cs @@ -7,7 +7,7 @@ namespace System.Globalization { - internal partial class CalendarData + internal sealed partial class CalendarData { private const uint CAL_ICALINTVALUE = 0x00000001; private const uint CAL_RETURN_GENITIVE_NAMES = 0x10000000; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs index e1c1b71252012..35e493adcbc9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.cs @@ -12,7 +12,7 @@ namespace System.Globalization // NOTE: Calendars depend on the locale name that creates it. Only a few // properties are available without locales using CalendarData.GetCalendar(CalendarData) // - internal partial class CalendarData + internal sealed partial class CalendarData { // Max calendars internal const int MAX_CALENDARS = 23; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs index de0bee85720ee..17e75e19cd1e7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Icu.cs @@ -7,7 +7,7 @@ namespace System.Globalization { - internal partial class CultureData + internal sealed partial class CultureData { // ICU constants private const int ICU_ULOC_KEYWORD_AND_VALUES_CAPACITY = 100; // max size of keyword or value diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs index 5997990dd9690..be1b108e4e05c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs @@ -10,7 +10,7 @@ namespace System.Globalization { - internal partial class CultureData + internal sealed partial class CultureData { // Wrappers around the GetLocaleInfoEx APIs which handle marshalling the returned // data as either and Int or string. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs index 5db6bcf1ebf25..fd59f95c76d50 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs @@ -5,7 +5,7 @@ namespace System.Globalization { - internal partial class CultureData + internal sealed partial class CultureData { private bool InitCultureDataCore() => InitIcuCultureDataCore(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs index cb21ae62ac762..74854c3fd0b4a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs @@ -5,7 +5,7 @@ namespace System.Globalization { - internal partial class CultureData + internal sealed partial class CultureData { /// /// Check with the OS to see if this is a valid culture. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs index b390d2fff38b9..3a013a6444646 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -37,7 +37,7 @@ namespace System.Globalization /// en if you pass in en /// de-DE if you pass in de-DE_phoneb /// - internal partial class CultureData + internal sealed partial class CultureData { private const int LocaleNameMaxLength = 85; private const int undef = -1; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs index aa2b30f149c86..c91e7fa5c1435 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs @@ -2618,7 +2618,7 @@ private bool CompareStringIgnoreCaseOptimized(string string1, int offset1, int l return Culture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, CompareOptions.IgnoreCase) == 0; } - internal class TokenHashValue + internal sealed class TokenHashValue { internal string tokenString; internal TokenType tokenType; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfoScanner.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfoScanner.cs index bae033c675ffd..e459993ae3da3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfoScanner.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfoScanner.cs @@ -67,7 +67,7 @@ internal enum CalendarId : ushort LAST_CALENDAR = 23 // Last calendar ID } - internal class DateTimeFormatInfoScanner + internal sealed class DateTimeFormatInfoScanner { // Special prefix-like flag char in DateWord array. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.LoadICU.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.LoadICU.Unix.cs new file mode 100644 index 0000000000000..c91596d3dee99 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.LoadICU.Unix.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Globalization +{ + internal static partial class GlobalizationMode + { + private static int LoadICU() => Interop.Globalization.LoadICU(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.LoadICU.iOS.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.LoadICU.iOS.cs new file mode 100644 index 0000000000000..08c5feb8ad189 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.LoadICU.iOS.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Globalization +{ + internal static partial class GlobalizationMode + { + private static int LoadICU() + { + object? datPath = AppContext.GetData("ICU_DAT_FILE_PATH"); + return (datPath != null) ? Interop.Globalization.LoadICUData(datPath!.ToString()!) : Interop.Globalization.LoadICU(); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs index 0149463c6aa0e..cce3ae3a870de 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs @@ -22,7 +22,7 @@ private static bool GetGlobalizationInvariantMode() } else { - int loaded = Interop.Globalization.LoadICU(); + int loaded = LoadICU(); if (loaded == 0 && !OperatingSystem.IsBrowser()) { // This can't go into resources, because a resource lookup requires globalization, which requires ICU diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs index 1a0bf66c42e56..590aae9b3bdc7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs @@ -10,7 +10,7 @@ internal static partial class GlobalizationMode internal static bool Invariant { get; } = GetInvariantSwitchValue(); internal static bool UseNls { get; } = !Invariant && - (GetSwitchValue("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") || + (AppContextConfigHelper.GetBooleanConfig("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") || !LoadIcu()); private static bool LoadIcu() diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs index 7183786cf0d22..1d2172fb5510c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.cs @@ -9,27 +9,13 @@ namespace System.Globalization internal static partial class GlobalizationMode { private static bool GetInvariantSwitchValue() => - GetSwitchValue("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); + AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"); private static bool TryGetAppLocalIcuSwitchValue([NotNullWhen(true)] out string? value) => TryGetStringValue("System.Globalization.AppLocalIcu", "DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU", out value); internal static bool PredefinedCulturesOnly { get; } = - GetSwitchValue("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY"); - - private static bool GetSwitchValue(string switchName, string envVariable) - { - if (!AppContext.TryGetSwitch(switchName, out bool ret)) - { - string? switchValue = Environment.GetEnvironmentVariable(envVariable); - if (switchValue != null) - { - ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1"); - } - } - - return ret; - } + AppContextConfigHelper.GetBooleanConfig("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY"); private static bool TryGetStringValue(string switchName, string envVariable, [NotNullWhen(true)] out string? value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs index e1dd518d4174b..3fd6480717860 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs @@ -4,7 +4,7 @@ namespace System.Globalization { // Gregorian Calendars use Era Info - internal class EraInfo + internal sealed class EraInfo { internal int era; // The value of the era. internal long ticks; // The time in ticks when the era starts @@ -45,7 +45,7 @@ internal EraInfo(int era, int startYear, int startMonth, int startDay, int yearO // This calendar recognizes two era values: // 0 CurrentEra (AD) // 1 BeforeCurrentEra (BC) - internal class GregorianCalendarHelper + internal sealed class GregorianCalendarHelper { // 1 tick = 100ns = 10E-7 second // Number of ticks per time unit diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs index b11fa9b32e22c..4a6aef540a5af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/HebrewCalendar.cs @@ -892,7 +892,7 @@ public override int ToFourDigitYear(int year) return year; } - internal class DateBuffer + internal sealed class DateBuffer { internal int year; internal int month; diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/BinaryReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/BinaryReader.cs index 2f1b8ba21d509..39532de1f3e11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/BinaryReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/BinaryReader.cs @@ -114,7 +114,7 @@ private void ThrowIfDisposed() { if (_disposed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } } @@ -214,7 +214,7 @@ private byte InternalReadByte() int b = _stream.ReadByte(); if (b == -1) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } return (byte)b; @@ -229,7 +229,7 @@ public virtual char ReadChar() int value = Read(); if (value == -1) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } return (char)value; } @@ -296,7 +296,7 @@ public virtual string ReadString() n = _stream.Read(_charBytes, 0, readLength); if (n == 0) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0); @@ -536,7 +536,7 @@ private ReadOnlySpan InternalRead(int numBytes) int n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead); if (n == 0) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } bytesRead += n; } while (bytesRead < numBytes); @@ -569,7 +569,7 @@ protected virtual void FillBuffer(int numBytes) n = _stream.ReadByte(); if (n == -1) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } _buffer[0] = (byte)n; @@ -581,7 +581,7 @@ protected virtual void FillBuffer(int numBytes) n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead); if (n == 0) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } bytesRead += n; } while (bytesRead < numBytes); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs index bc1ea76470377..f6da2fd101d1c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/BufferedStream.cs @@ -67,7 +67,7 @@ public BufferedStream(Stream stream) public BufferedStream(Stream stream, int bufferSize) { if (stream == null) - throw new ArgumentNullException(nameof(stream)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stream); if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(bufferSize))); @@ -79,13 +79,13 @@ public BufferedStream(Stream stream, int bufferSize) // & writes are greater than or equal to buffer size. if (!_stream.CanRead && !_stream.CanWrite) - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); } private void EnsureNotClosed() { if (_stream == null) - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); } private void EnsureCanSeek() @@ -93,7 +93,7 @@ private void EnsureCanSeek() Debug.Assert(_stream != null); if (!_stream.CanSeek) - throw new NotSupportedException(SR.NotSupported_UnseekableStream); + ThrowHelper.ThrowNotSupportedException_UnseekableStream(); } private void EnsureCanRead() @@ -101,7 +101,7 @@ private void EnsureCanRead() Debug.Assert(_stream != null); if (!_stream.CanRead) - throw new NotSupportedException(SR.NotSupported_UnreadableStream); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } private void EnsureCanWrite() @@ -109,7 +109,7 @@ private void EnsureCanWrite() Debug.Assert(_stream != null); if (!_stream.CanWrite) - throw new NotSupportedException(SR.NotSupported_UnwritableStream); + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } private void EnsureShadowBufferAllocated() @@ -203,7 +203,7 @@ public override long Position set { if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); EnsureNotClosed(); EnsureCanSeek(); @@ -237,6 +237,7 @@ protected override void Dispose(bool disposing) { _stream = null; _buffer = null; + _writePos = 0; // WriteByte hot path relies on this // Call base.Dispose(bool) to cleanup async IO resources base.Dispose(disposing); @@ -263,6 +264,7 @@ public override async ValueTask DisposeAsync() { _stream = null; _buffer = null; + _writePos = 0; } } @@ -503,8 +505,7 @@ public override int Read(byte[] buffer, int offset, int count) offset += bytesFromBuffer; } - // So the read buffer is empty. - Debug.Assert(_readLen == _readPos); + Debug.Assert(_readLen == _readPos, "The read buffer must now be empty"); _readPos = _readLen = 0; // If there was anything in the write buffer, clear it. @@ -554,8 +555,7 @@ public override int Read(Span destination) destination = destination.Slice(bytesFromBuffer); } - // The read buffer must now be empty. - Debug.Assert(_readLen == _readPos); + Debug.Assert(_readLen == _readPos, "The read buffer must now be empty"); _readPos = _readLen = 0; // If there was anything in the write buffer, clear it. @@ -1164,6 +1164,18 @@ public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); public override void WriteByte(byte value) + { + if (_writePos > 0 && _writePos < _bufferSize - 1) + { + _buffer![_writePos++] = value; + } + else + { + WriteByteSlow(value); + } + } + + private void WriteByteSlow(byte value) { EnsureNotClosed(); @@ -1236,7 +1248,7 @@ public override long Seek(long offset, SeekOrigin origin) public override void SetLength(long value) { if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); EnsureNotClosed(); EnsureCanSeek(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Error.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Error.cs deleted file mode 100644 index d2e483433a872..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Error.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.IO -{ - /// - /// Provides centralized methods for creating exceptions for System.IO.FileSystem. - /// - internal static class Error - { - internal static Exception GetStreamIsClosed() - { - return new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); - } - - internal static Exception GetEndOfFile() - { - return new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); - } - - internal static Exception GetFileNotOpen() - { - return new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); - } - - internal static Exception GetReadNotSupported() - { - return new NotSupportedException(SR.NotSupported_UnreadableStream); - } - - internal static Exception GetSeekNotSupported() - { - return new NotSupportedException(SR.NotSupported_UnseekableStream); - } - - internal static Exception GetWriteNotSupported() - { - return new NotSupportedException(SR.NotSupported_UnwritableStream); - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs index 078b0bd0a4186..eb9d98750f758 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IO.Strategies; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Threading; @@ -46,7 +47,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS { ValidateHandle(safeHandle, access, bufferSize, isAsync); - _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync)); + _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, DefaultShare, bufferSize, isAsync); } catch { @@ -65,22 +66,27 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { if (handle.IsInvalid) + { throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle)); - - if (access < FileAccess.Read || access > FileAccess.ReadWrite) + } + else if (access < FileAccess.Read || access > FileAccess.ReadWrite) + { throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum); - if (bufferSize <= 0) + } + else if (bufferSize <= 0) + { throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); - - if (handle.IsClosed) - throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault()) + } + else if (handle.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault()) + { throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle)); + } } - private FileStreamStrategy WrapIfDerivedType(FileStreamStrategy impl) - => GetType() == typeof(FileStream) ? impl : new DerivedFileStreamStrategy(this, impl); - public FileStream(SafeFileHandle handle, FileAccess access) : this(handle, access, DefaultBufferSize) { @@ -95,56 +101,76 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool { ValidateHandle(handle, access, bufferSize, isAsync); - _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync)); + _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, DefaultShare, bufferSize, isAsync); } - public FileStream(string path, FileMode mode) : - this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync) - { } + public FileStream(string path, FileMode mode) + : this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync) + { + } - public FileStream(string path, FileMode mode, FileAccess access) : - this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync) - { } + public FileStream(string path, FileMode mode, FileAccess access) + : this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync) + { + } - public FileStream(string path, FileMode mode, FileAccess access, FileShare share) : - this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync) - { } + public FileStream(string path, FileMode mode, FileAccess access, FileShare share) + : this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync) + { + } - public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) : - this(path, mode, access, share, bufferSize, DefaultIsAsync) - { } + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) + : this(path, mode, access, share, bufferSize, DefaultIsAsync) + { + } - public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) : - this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None) - { } + public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) + : this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None) + { + } public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { if (path == null) + { throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path); - if (path.Length == 0) + } + else if (path.Length == 0) + { throw new ArgumentException(SR.Argument_EmptyPath, nameof(path)); + } // don't include inheritable in our bounds check for share FileShare tempshare = share & ~FileShare.Inheritable; string? badArg = null; if (mode < FileMode.CreateNew || mode > FileMode.Append) + { badArg = nameof(mode); + } else if (access < FileAccess.Read || access > FileAccess.ReadWrite) + { badArg = nameof(access); + } else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete)) + { badArg = nameof(share); + } if (badArg != null) + { throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum); + } // NOTE: any change to FileOptions enum needs to be matched here in the error validation if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0) + { throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum); - - if (bufferSize <= 0) + } + else if (bufferSize <= 0) + { throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); + } // Write access validation if ((access & FileAccess.Write) == 0) @@ -157,14 +183,15 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share } if ((access & FileAccess.Read) != 0 && mode == FileMode.Append) + { throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access)); - - if ((access & FileAccess.Write) == FileAccess.Write) + } + else if ((access & FileAccess.Write) == FileAccess.Write) { SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch); } - _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options)); + _strategy = FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options); } [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")] @@ -177,10 +204,9 @@ public virtual void Lock(long position, long length) { throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); } - - if (_strategy.IsClosed) + else if (_strategy.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } _strategy.Lock(position, length); @@ -193,10 +219,9 @@ public virtual void Unlock(long position, long length) { throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); } - - if (_strategy.IsClosed) + else if (_strategy.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } _strategy.Unlock(position, length); @@ -208,9 +233,9 @@ public override Task FlushAsync(CancellationToken cancellationToken) { return Task.FromCanceled(cancellationToken); } - if (_strategy.IsClosed) + else if (_strategy.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } return _strategy.FlushAsync(cancellationToken); @@ -230,10 +255,13 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel ValidateBufferArguments(buffer, offset, count); if (cancellationToken.IsCancellationRequested) + { return Task.FromCanceled(cancellationToken); - - if (_strategy.IsClosed) - throw Error.GetFileNotOpen(); + } + else if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } return _strategy.ReadAsync(buffer, offset, count, cancellationToken); } @@ -244,10 +272,9 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken { return ValueTask.FromCanceled(cancellationToken); } - - if (_strategy.IsClosed) + else if (_strategy.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } return _strategy.ReadAsync(buffer, cancellationToken); @@ -267,10 +294,13 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati ValidateBufferArguments(buffer, offset, count); if (cancellationToken.IsCancellationRequested) + { return Task.FromCanceled(cancellationToken); - - if (_strategy.IsClosed) - throw Error.GetFileNotOpen(); + } + else if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } return _strategy.WriteAsync(buffer, offset, count, cancellationToken); } @@ -281,10 +311,9 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo { return ValueTask.FromCanceled(cancellationToken); } - - if (_strategy.IsClosed) + else if (_strategy.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } return _strategy.WriteAsync(buffer, cancellationToken); @@ -305,7 +334,10 @@ public override void Flush() /// public virtual void Flush(bool flushToDisk) { - if (_strategy.IsClosed) throw Error.GetFileNotOpen(); + if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } _strategy.Flush(flushToDisk); } @@ -324,7 +356,9 @@ private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) { ValidateBufferArguments(buffer, offset, count); if (_strategy.IsClosed) - throw Error.GetFileNotOpen(); + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } } /// Sets the length of this stream to the given value. @@ -332,13 +366,21 @@ private void ValidateReadWriteArgs(byte[] buffer, int offset, int count) public override void SetLength(long value) { if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - if (_strategy.IsClosed) - throw Error.GetFileNotOpen(); - if (!CanSeek) - throw Error.GetSeekNotSupported(); - if (!CanWrite) - throw Error.GetWriteNotSupported(); + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + else if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if (!CanSeek) + { + ThrowHelper.ThrowNotSupportedException_UnseekableStream(); + } + else if (!CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } _strategy.SetLength(value); } @@ -356,8 +398,15 @@ public override long Length { get { - if (_strategy.IsClosed) throw Error.GetFileNotOpen(); - if (!CanSeek) throw Error.GetSeekNotSupported(); + if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if (!CanSeek) + { + ThrowHelper.ThrowNotSupportedException_UnseekableStream(); + } + return _strategy.Length; } } @@ -368,17 +417,22 @@ public override long Position get { if (_strategy.IsClosed) - throw Error.GetFileNotOpen(); - - if (!CanSeek) - throw Error.GetSeekNotSupported(); + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if (!CanSeek) + { + ThrowHelper.ThrowNotSupportedException_UnseekableStream(); + } return _strategy.Position; } set { if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } _strategy.Seek(value, SeekOrigin.Begin); } @@ -397,29 +451,29 @@ public override long Position /// The byte to write to the stream. public override void WriteByte(byte value) => _strategy.WriteByte(value); - ~FileStream() - { - // Preserved for compatibility since FileStream has defined a - // finalizer in past releases and derived classes may depend - // on Dispose(false) call. - Dispose(false); - } + protected override void Dispose(bool disposing) => _strategy.DisposeInternal(disposing); - protected override void Dispose(bool disposing) - { - _strategy?.DisposeInternal(disposing); // null _strategy possible in finalizer - } + internal void DisposeInternal(bool disposing) => Dispose(disposing); public override ValueTask DisposeAsync() => _strategy.DisposeAsync(); + public override void CopyTo(Stream destination, int bufferSize) => _strategy.CopyTo(destination, bufferSize); + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) => _strategy.CopyToAsync(destination, bufferSize, cancellationToken); public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (_strategy.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream); + + if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if (!CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } return _strategy.BeginRead(buffer, offset, count, callback, state); } @@ -427,7 +481,9 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy public override int EndRead(IAsyncResult asyncResult) { if (asyncResult == null) + { throw new ArgumentNullException(nameof(asyncResult)); + } return _strategy.EndRead(asyncResult); } @@ -435,8 +491,15 @@ public override int EndRead(IAsyncResult asyncResult) public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) { ValidateBufferArguments(buffer, offset, count); - if (_strategy.IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed); - if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream); + + if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + else if (!CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } return _strategy.BeginWrite(buffer, offset, count, callback, state); } @@ -444,7 +507,9 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As public override void EndWrite(IAsyncResult asyncResult) { if (asyncResult == null) + { throw new ArgumentNullException(nameof(asyncResult)); + } _strategy.EndWrite(asyncResult); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs deleted file mode 100644 index c7b56290ca7ee..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs +++ /dev/null @@ -1,257 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - -namespace System.IO -{ - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy - { - // This is an internal object extending TaskCompletionSource with fields - // for all of the relevant data necessary to complete the IO operation. - // This is used by IOCallback and all of the async methods. - private unsafe class FileStreamCompletionSource : TaskCompletionSource - { - private const long NoResult = 0; - private const long ResultSuccess = (long)1 << 32; - private const long ResultError = (long)2 << 32; - private const long RegisteringCancellation = (long)4 << 32; - private const long CompletedCallback = (long)8 << 32; - private const ulong ResultMask = ((ulong)uint.MaxValue) << 32; - - private static Action? s_cancelCallback; - - private readonly LegacyFileStreamStrategy _stream; - private readonly int _numBufferedBytes; - private CancellationTokenRegistration _cancellationRegistration; -#if DEBUG - private bool _cancellationHasBeenRegistered; -#endif - private NativeOverlapped* _overlapped; // Overlapped class responsible for operations in progress when an appdomain unload occurs - private long _result; // Using long since this needs to be used in Interlocked APIs - - // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) - protected FileStreamCompletionSource(LegacyFileStreamStrategy stream, int numBufferedBytes, byte[]? bytes) - : base(TaskCreationOptions.RunContinuationsAsynchronously) - { - _numBufferedBytes = numBufferedBytes; - _stream = stream; - _result = NoResult; - - // Create the native overlapped. We try to use the preallocated overlapped if possible: it's possible if the byte - // buffer is null (there's nothing to pin) or the same one that's associated with the preallocated overlapped (and - // thus is already pinned) and if no one else is currently using the preallocated overlapped. This is the fast-path - // for cases where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's - // buffer is used) and where operations on the FileStream are not being performed concurrently. - Debug.Assert(bytes == null || ReferenceEquals(bytes, _stream._buffer)); - - // The _preallocatedOverlapped is null if the internal buffer was never created, so we check for - // a non-null bytes before using the stream's _preallocatedOverlapped - _overlapped = bytes != null && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ? - _stream._fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(_stream._preallocatedOverlapped!) : // allocated when buffer was created, and buffer is non-null - _stream._fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(s_ioCallback, this, bytes); - Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null"); - } - - internal NativeOverlapped* Overlapped => _overlapped; - - public void SetCompletedSynchronously(int numBytes) - { - ReleaseNativeResource(); - TrySetResult(numBytes + _numBufferedBytes); - } - - public void RegisterForCancellation(CancellationToken cancellationToken) - { -#if DEBUG - Debug.Assert(cancellationToken.CanBeCanceled); - Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice"); - _cancellationHasBeenRegistered = true; -#endif - - // Quick check to make sure the IO hasn't completed - if (_overlapped != null) - { - Action? cancelCallback = s_cancelCallback ??= Cancel; - - // Register the cancellation only if the IO hasn't completed - long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult); - if (packedResult == NoResult) - { - _cancellationRegistration = cancellationToken.UnsafeRegister(cancelCallback, this); - - // Switch the result, just in case IO completed while we were setting the registration - packedResult = Interlocked.Exchange(ref _result, NoResult); - } - else if (packedResult != CompletedCallback) - { - // Failed to set the result, IO is in the process of completing - // Attempt to take the packed result - packedResult = Interlocked.Exchange(ref _result, NoResult); - } - - // If we have a callback that needs to be completed - if ((packedResult != NoResult) && (packedResult != CompletedCallback) && (packedResult != RegisteringCancellation)) - { - CompleteCallback((ulong)packedResult); - } - } - } - - internal virtual void ReleaseNativeResource() - { - // Ensure that cancellation has been completed and cleaned up. - _cancellationRegistration.Dispose(); - - // Free the overlapped. - // NOTE: The cancellation must *NOT* be running at this point, or it may observe freed memory - // (this is why we disposed the registration above). - if (_overlapped != null) - { - _stream._fileHandle.ThreadPoolBinding!.FreeNativeOverlapped(_overlapped); - _overlapped = null; - } - - // Ensure we're no longer set as the current completion source (we may not have been to begin with). - // Only one operation at a time is eligible to use the preallocated overlapped, - _stream.CompareExchangeCurrentOverlappedOwner(null, this); - } - - // When doing IO asynchronously (i.e. _isAsync==true), this callback is - // called by a free thread in the threadpool when the IO operation - // completes. - internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) - { - // Extract the completion source from the overlapped. The state in the overlapped - // will either be a FileStream (in the case where the preallocated overlapped was used), - // in which case the operation being completed is its _currentOverlappedOwner, or it'll - // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated - // overlapped was already in use by another operation). - object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); - Debug.Assert(state is LegacyFileStreamStrategy || state is FileStreamCompletionSource); - FileStreamCompletionSource completionSource = state is LegacyFileStreamStrategy fs ? - fs._currentOverlappedOwner! : // must be owned - (FileStreamCompletionSource)state!; - Debug.Assert(completionSource != null); - Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match"); - - // Handle reading from & writing to closed pipes. While I'm not sure - // this is entirely necessary anymore, maybe it's possible for - // an async read on a pipe to be issued and then the pipe is closed, - // returning this error. This may very well be necessary. - ulong packedResult; - if (errorCode != 0 && errorCode != ERROR_BROKEN_PIPE && errorCode != ERROR_NO_DATA) - { - packedResult = ((ulong)ResultError | errorCode); - } - else - { - packedResult = ((ulong)ResultSuccess | numBytes); - } - - // Stow the result so that other threads can observe it - // And, if no other thread is registering cancellation, continue - if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult)) - { - // Successfully set the state, attempt to take back the callback - if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult) - { - // Successfully got the callback, finish the callback - completionSource.CompleteCallback(packedResult); - } - // else: Some other thread stole the result, so now it is responsible to finish the callback - } - // else: Some other thread is registering a cancellation, so it *must* finish the callback - } - - private void CompleteCallback(ulong packedResult) - { - // Free up the native resource and cancellation registration - CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration - ReleaseNativeResource(); - - // Unpack the result and send it to the user - long result = (long)(packedResult & ResultMask); - if (result == ResultError) - { - int errorCode = unchecked((int)(packedResult & uint.MaxValue)); - if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED) - { - TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - } - else - { - Exception e = Win32Marshal.GetExceptionForWin32Error(errorCode); - e.SetCurrentStackTrace(); - TrySetException(e); - } - } - else - { - Debug.Assert(result == ResultSuccess, "Unknown result"); - TrySetResult((int)(packedResult & uint.MaxValue) + _numBufferedBytes); - } - } - - private static void Cancel(object? state) - { - // WARNING: This may potentially be called under a lock (during cancellation registration) - - Debug.Assert(state is FileStreamCompletionSource, "Unknown state passed to cancellation"); - FileStreamCompletionSource completionSource = (FileStreamCompletionSource)state; - Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet"); - - // If the handle is still valid, attempt to cancel the IO - if (!completionSource._stream._fileHandle.IsInvalid && - !Interop.Kernel32.CancelIoEx(completionSource._stream._fileHandle, completionSource._overlapped)) - { - int errorCode = Marshal.GetLastWin32Error(); - - // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel. - // This probably means that the IO operation has completed. - if (errorCode != Interop.Errors.ERROR_NOT_FOUND) - { - throw Win32Marshal.GetExceptionForWin32Error(errorCode); - } - } - } - - public static FileStreamCompletionSource Create(LegacyFileStreamStrategy stream, int numBufferedBytesRead, ReadOnlyMemory memory) - { - // If the memory passed in is the stream's internal buffer, we can use the base FileStreamCompletionSource, - // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived - // MemoryFileStreamCompletionSource, which Retains the memory, which will result in less pinning in the case - // where the underlying memory is backed by pre-pinned buffers. - return MemoryMarshal.TryGetArray(memory, out ArraySegment buffer) && ReferenceEquals(buffer.Array, stream._buffer) ? - new FileStreamCompletionSource(stream, numBufferedBytesRead, buffer.Array) : - new MemoryFileStreamCompletionSource(stream, numBufferedBytesRead, memory); - } - } - - /// - /// Extends with to support disposing of a - /// when the operation has completed. This should only be used - /// when memory doesn't wrap a byte[]. - /// - private sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource - { - private MemoryHandle _handle; // mutable struct; do not make this readonly - - internal MemoryFileStreamCompletionSource(LegacyFileStreamStrategy stream, int numBufferedBytes, ReadOnlyMemory memory) : - base(stream, numBufferedBytes, bytes: null) // this type handles the pinning, so null is passed for bytes - { - _handle = memory.Pin(); - } - - internal override void ReleaseNativeResource() - { - _handle.Dispose(); - base.ReleaseNativeResource(); - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs deleted file mode 100644 index 66dd3cb259e4f..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -namespace System.IO -{ - // this type defines a set of stateless FileStream/FileStreamStrategy helper methods - internal static class FileStreamHelpers - { - internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - => new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, isAsync); - - internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - => new LegacyFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); - - internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) - => CreateFileOpenHandle(path, mode, access, share, options); - - private static unsafe SafeFileHandle CreateFileOpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) - { - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); - - int fAccess = - ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) | - ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0); - - // Our Inheritable bit was stolen from Windows, but should be set in - // the security attributes class. Don't leave this bit set. - share &= ~FileShare.Inheritable; - - // Must use a valid Win32 constant here... - if (mode == FileMode.Append) - mode = FileMode.OpenOrCreate; - - int flagsAndAttributes = (int)options; - - // For mitigating local elevation of privilege attack through named pipes - // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the - // named pipe server can't impersonate a high privileged client security context - // (note that this is the effective default on CreateFile2) - flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); - - using (DisableMediaInsertionPrompt.Create()) - { - Debug.Assert(path != null); - return ValidateFileHandle( - Interop.Kernel32.CreateFile(path, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero), - path, - (options & FileOptions.Asynchronous) != 0); - } - } - - internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsync) - { - return handle.IsAsync ?? !IsHandleSynchronous(handle, ignoreInvalid: true) ?? defaultIsAsync; - } - - private static unsafe bool? IsHandleSynchronous(SafeFileHandle fileHandle, bool ignoreInvalid) - { - if (fileHandle.IsInvalid) - return null; - - uint fileMode; - - int status = Interop.NtDll.NtQueryInformationFile( - FileHandle: fileHandle, - IoStatusBlock: out _, - FileInformation: &fileMode, - Length: sizeof(uint), - FileInformationClass: Interop.NtDll.FileModeInformation); - - switch (status) - { - case 0: - // We were successful - break; - case Interop.NtDll.STATUS_INVALID_HANDLE: - if (!ignoreInvalid) - { - throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_INVALID_HANDLE); - } - else - { - return null; - } - default: - // Something else is preventing access - Debug.Fail("Unable to get the file mode information, status was" + status.ToString()); - return null; - } - - // If either of these two flags are set, the file handle is synchronous (not overlapped) - return (fileMode & (Interop.NtDll.FILE_SYNCHRONOUS_IO_ALERT | Interop.NtDll.FILE_SYNCHRONOUS_IO_NONALERT)) > 0; - } - - internal static void VerifyHandleIsSync(SafeFileHandle handle) - { - // As we can accurately check the handle type when we have access to NtQueryInformationFile we don't need to skip for - // any particular file handle type. - - // If the handle was passed in without an explicit async setting, we already looked it up in GetDefaultIsAsync - if (!handle.IsAsync.HasValue) - return; - - // If we can't check the handle, just assume it is ok. - if (!(IsHandleSynchronous(handle, ignoreInvalid: false) ?? true)) - throw new ArgumentException(SR.Arg_HandleNotSync, nameof(handle)); - } - - private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) - { - Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; - if ((share & FileShare.Inheritable) != 0) - { - secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES - { - nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), - bInheritHandle = Interop.BOOL.TRUE - }; - } - return secAttrs; - } - - private static SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle, string path, bool useAsyncIO) - { - if (fileHandle.IsInvalid) - { - // Return a meaningful exception with the full path. - - // NT5 oddity - when trying to open "C:\" as a Win32FileStream, - // we usually get ERROR_PATH_NOT_FOUND from the OS. We should - // probably be consistent w/ every other directory. - int errorCode = Marshal.GetLastWin32Error(); - - if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && path!.Length == PathInternal.GetRootLength(path)) - errorCode = Interop.Errors.ERROR_ACCESS_DENIED; - - throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); - } - - fileHandle.IsAsync = useAsyncIO; - return fileHandle; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs index 59316e780c843..8175eb9c74e23 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs @@ -108,13 +108,13 @@ public MemoryStream(byte[] buffer, int index, int count, bool writable, bool pub private void EnsureNotClosed() { if (!_isOpen) - throw Error.GetStreamIsClosed(); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); } private void EnsureWriteable() { if (!CanWrite) - throw Error.GetWriteNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } protected override void Dispose(bool disposing) @@ -233,7 +233,7 @@ internal ReadOnlySpan InternalReadSpan(int count) if ((uint)newPos > (uint)_length) { _position = _length; - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } var span = new ReadOnlySpan(_buffer, origPos, count); @@ -602,8 +602,8 @@ public virtual byte[] ToArray() int count = _length - _origin; if (count == 0) return Array.Empty(); - byte[] copy = new byte[count]; - Buffer.BlockCopy(_buffer, _origin, copy, 0, count); + byte[] copy = GC.AllocateUninitializedArray(count); + _buffer.AsSpan(_origin, count).CopyTo(copy); return copy; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs new file mode 100644 index 0000000000000..b7f24351a0bda --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -0,0 +1,383 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + internal sealed partial class AsyncWindowsFileStreamStrategy : WindowsFileStreamStrategy, IFileStreamCompletionSourceStrategy + { + private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations + private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped + + internal AsyncWindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) + : base(handle, access, share) + { + } + + internal AsyncWindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + : base(path, mode, access, share, options) + { + } + + internal override bool IsAsync => true; + + public override ValueTask DisposeAsync() + { + // the base class must dispose ThreadPoolBinding and FileHandle + // before _preallocatedOverlapped is disposed + ValueTask result = base.DisposeAsync(); + Debug.Assert(result.IsCompleted, "the method must be sync, as it performs no flushing"); + + _preallocatedOverlapped?.Dispose(); + + return result; + } + + protected override void Dispose(bool disposing) + { + // the base class must dispose ThreadPoolBinding and FileHandle + // before _preallocatedOverlapped is disposed + base.Dispose(disposing); + + _preallocatedOverlapped?.Dispose(); + } + + protected override void OnInitFromHandle(SafeFileHandle handle) + { + // This is necessary for async IO using IO Completion ports via our + // managed Threadpool API's. This calls the OS's + // BindIoCompletionCallback method, and passes in a stub for the + // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped + // struct for this request and gets a delegate to a managed callback + // from there, which it then calls on a threadpool thread. (We allocate + // our native OVERLAPPED structs 2 pointers too large and store EE + // state & a handle to a delegate there.) + // + // If, however, we've already bound this file handle to our completion port, + // don't try to bind it again because it will fail. A handle can only be + // bound to a single completion port at a time. + if (handle.IsAsync != true) + { + try + { + handle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(handle); + } + catch (Exception ex) + { + // If you passed in a synchronous handle and told us to use + // it asynchronously, throw here. + throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle), ex); + } + } + } + + protected override void OnInit() + { + // This is necessary for async IO using IO Completion ports via our + // managed Threadpool API's. This (theoretically) calls the OS's + // BindIoCompletionCallback method, and passes in a stub for the + // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped + // struct for this request and gets a delegate to a managed callback + // from there, which it then calls on a threadpool thread. (We allocate + // our native OVERLAPPED structs 2 pointers too large and store EE state + // & GC handles there, one to an IAsyncResult, the other to a delegate.) + try + { + _fileHandle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_fileHandle); + } + catch (ArgumentException ex) + { + throw new IOException(SR.IO_BindHandleFailed, ex); + } + finally + { + if (_fileHandle.ThreadPoolBinding == null) + { + // We should close the handle so that the handle is not open until SafeFileHandle GC + _fileHandle.Dispose(); + } + } + } + + // called by BufferedFileStreamStrategy + internal override void OnBufferAllocated(byte[] buffer) + { + Debug.Assert(_preallocatedOverlapped == null); + + _preallocatedOverlapped = new PreAllocatedOverlapped(FileStreamCompletionSource.s_ioCallback, this, buffer); + } + + SafeFileHandle IFileStreamCompletionSourceStrategy.FileHandle => _fileHandle; + + FileStreamCompletionSource? IFileStreamCompletionSourceStrategy.CurrentOverlappedOwner => _currentOverlappedOwner; + + FileStreamCompletionSource? IFileStreamCompletionSourceStrategy.CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource) + => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); + + public override int Read(byte[] buffer, int offset, int count) + => ReadAsyncInternal(new Memory(buffer, offset, count)).GetAwaiter().GetResult(); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => ReadAsyncInternal(new Memory(buffer, offset, count), cancellationToken); + + public override ValueTask ReadAsync(Memory destination, CancellationToken cancellationToken = default) + => new ValueTask(ReadAsyncInternal(destination, cancellationToken)); + + private unsafe Task ReadAsyncInternal(Memory destination, CancellationToken cancellationToken = default) + { + if (!CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + + // Create and store async stream class library specific data in the async result + FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, destination); + NativeOverlapped* intOverlapped = completionSource.Overlapped; + + // Calculate position in the file we should be at after the read is done + long positionBefore = _filePosition; + if (CanSeek) + { + long len = Length; + + if (positionBefore + destination.Length > len) + { + if (positionBefore <= len) + { + destination = destination.Slice(0, (int)(len - positionBefore)); + } + else + { + destination = default; + } + } + + // Now set the position to read from in the NativeOverlapped struct + // For pipes, we should leave the offset fields set to 0. + intOverlapped->OffsetLow = unchecked((int)positionBefore); + intOverlapped->OffsetHigh = (int)(positionBefore >> 32); + + // When using overlapped IO, the OS is not supposed to + // touch the file pointer location at all. We will adjust it + // ourselves, but only in memory. This isn't threadsafe. + _filePosition += destination.Length; + } + + // queue an async ReadFile operation and pass in a packed overlapped + int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, false, intOverlapped, out int errorCode); + + // ReadFile, the OS version, will return 0 on failure. But + // my ReadFileNative wrapper returns -1. My wrapper will return + // the following: + // On error, r==-1. + // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING + // on async requests that completed sequentially, r==0 + // You will NEVER RELIABLY be able to get the number of bytes + // read back from this call when using overlapped structures! You must + // not pass in a non-null lpNumBytesRead to ReadFile when using + // overlapped structures! This is by design NT behavior. + if (r == -1) + { + // For pipes, when they hit EOF, they will come here. + if (errorCode == ERROR_BROKEN_PIPE) + { + // Not an error, but EOF. AsyncFSCallback will NOT be + // called. Call the user callback here. + + // We clear the overlapped status bit for this special case. + // Failure to do so looks like we are freeing a pending overlapped later. + intOverlapped->InternalLow = IntPtr.Zero; + completionSource.SetCompletedSynchronously(0); + } + else if (errorCode != ERROR_IO_PENDING) + { + if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. + { + _filePosition = positionBefore; + } + + completionSource.ReleaseNativeResource(); + + if (errorCode == ERROR_HANDLE_EOF) + { + ThrowHelper.ThrowEndOfFileException(); + } + else + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + } + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING + { + // Only once the IO is pending do we register for cancellation + completionSource.RegisterForCancellation(cancellationToken); + } + } + else + { + // Due to a workaround for a race condition in NT's ReadFile & + // WriteFile routines, we will always be returning 0 from ReadFileNative + // when we do async IO instead of the number of bytes read, + // irregardless of whether the operation completed + // synchronously or asynchronously. We absolutely must not + // set asyncResult._numBytes here, since will never have correct + // results. + } + + return completionSource.Task; + } + + public override void Write(byte[] buffer, int offset, int count) + => WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), CancellationToken.None).AsTask().GetAwaiter().GetResult(); + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => WriteAsyncInternal(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + => WriteAsyncInternal(buffer, cancellationToken); + + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush + + private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + => new ValueTask(WriteAsyncInternalCore(source, cancellationToken)); + + private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, CancellationToken cancellationToken) + { + if (!CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } + + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + + // Create and store async stream class library specific data in the async result + FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, source); + NativeOverlapped* intOverlapped = completionSource.Overlapped; + + long positionBefore = _filePosition; + if (CanSeek) + { + // Now set the position to read from in the NativeOverlapped struct + // For pipes, we should leave the offset fields set to 0. + intOverlapped->OffsetLow = (int)positionBefore; + intOverlapped->OffsetHigh = (int)(positionBefore >> 32); + + // When using overlapped IO, the OS is not supposed to + // touch the file pointer location at all. We will adjust it + // ourselves, but only in memory. This isn't threadsafe. + _filePosition += source.Length; + UpdateLengthOnChangePosition(); + } + + // queue an async WriteFile operation and pass in a packed overlapped + int r = FileStreamHelpers.WriteFileNative(_fileHandle, source.Span, false, intOverlapped, out int errorCode); + + // WriteFile, the OS version, will return 0 on failure. But + // my WriteFileNative wrapper returns -1. My wrapper will return + // the following: + // On error, r==-1. + // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING + // On async requests that completed sequentially, r==0 + // You will NEVER RELIABLY be able to get the number of bytes + // written back from this call when using overlapped IO! You must + // not pass in a non-null lpNumBytesWritten to WriteFile when using + // overlapped structures! This is ByDesign NT behavior. + if (r == -1) + { + // For pipes, when they are closed on the other side, they will come here. + if (errorCode == ERROR_NO_DATA) + { + // Not an error, but EOF. AsyncFSCallback will NOT be called. + // Completing TCS and return cached task allowing the GC to collect TCS. + completionSource.SetCompletedSynchronously(0); + return Task.CompletedTask; + } + else if (errorCode != ERROR_IO_PENDING) + { + if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere. + { + _filePosition = positionBefore; + } + + completionSource.ReleaseNativeResource(); + + if (errorCode == ERROR_HANDLE_EOF) + { + ThrowHelper.ThrowEndOfFileException(); + } + else + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + } + else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING + { + // Only once the IO is pending do we register for cancellation + completionSource.RegisterForCancellation(cancellationToken); + } + } + else + { + // Due to a workaround for a race condition in NT's ReadFile & + // WriteFile routines, we will always be returning 0 from WriteFileNative + // when we do async IO instead of the number of bytes written, + // irregardless of whether the operation completed + // synchronously or asynchronously. We absolutely must not + // set asyncResult._numBytes here, since will never have correct + // results. + } + + return completionSource.Task; + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + ValidateCopyToArguments(destination, bufferSize); + + // Fail if the file was closed + if (_fileHandle.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + if (!CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + // Bail early for cancellation if cancellation has been requested + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + return AsyncModeCopyToAsync(destination, bufferSize, cancellationToken); + } + + private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + Debug.Assert(CanRead, "_parent.CanRead"); + + try + { + await FileStreamHelpers + .AsyncModeCopyToAsync(_fileHandle, _path, CanSeek, _filePosition, destination, bufferSize, cancellationToken) + .ConfigureAwait(false); + } + finally + { + // Make sure the stream's current position reflects where we ended up + if (!_fileHandle.IsClosed && CanSeek) + { + _filePosition = Length; + } + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs new file mode 100644 index 0000000000000..2b914e7bc0aec --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs @@ -0,0 +1,1081 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + // this type exists so we can avoid duplicating the buffering logic in every FileStreamStrategy implementation + internal sealed class BufferedFileStreamStrategy : FileStreamStrategy + { + private readonly FileStreamStrategy _strategy; + private readonly int _bufferSize; + + private byte[]? _buffer; + private int _writePos; + private int _readPos; + private int _readLen; + // The last successful Task returned from ReadAsync (perf optimization for successive reads of the same size) + private Task? _lastSyncCompletedReadTask; + + internal BufferedFileStreamStrategy(FileStreamStrategy strategy, int bufferSize) + { + Debug.Assert(bufferSize > 1, "Buffering must not be enabled for smaller buffer sizes"); + + _strategy = strategy; + _bufferSize = bufferSize; + } + + ~BufferedFileStreamStrategy() + { + try + { + // the finalizer must at least try to flush the write buffer + // so we enforce it by passing always true + Dispose(true); + } + catch (Exception e) when (FileStreamHelpers.IsIoRelatedException(e)) + { + // On finalization, ignore failures from trying to flush the write buffer, + // e.g. if this stream is wrapping a pipe and the pipe is now broken. + } + } + + public override bool CanRead => _strategy.CanRead; + + public override bool CanWrite => _strategy.CanWrite; + + public override bool CanSeek => _strategy.CanSeek; + + public override long Length + { + get + { + long len = _strategy.Length; + + // If we're writing near the end of the file, we must include our + // internal buffer in our Length calculation. Don't flush because + // we use the length of the file in AsyncWindowsFileStreamStrategy.WriteAsync + if (_writePos > 0 && _strategy.Position + _writePos > len) + { + len = _writePos + _strategy.Position; + } + + return len; + } + } + + public override long Position + { + get + { + Debug.Assert(!(_writePos > 0 && _readPos != _readLen), "Read and Write buffers cannot both have data in them at the same time."); + + return _strategy.Position + _readPos - _readLen + _writePos; + } + set + { + if (_writePos > 0) + { + FlushWrite(); + } + + _readPos = 0; + _readLen = 0; + + _strategy.Position = value; + } + } + + internal override bool IsAsync => _strategy.IsAsync; + + internal override bool IsClosed => _strategy.IsClosed; + + internal override string Name => _strategy.Name; + + internal override SafeFileHandle SafeFileHandle + { + get + { + // BufferedFileStreamStrategy must flush before the handle is exposed + // so whoever uses SafeFileHandle to access disk data can see + // the changes that were buffered in memory so far + Flush(); + + return _strategy.SafeFileHandle; + } + } + + public override async ValueTask DisposeAsync() + { + try + { + if (!_strategy.IsClosed) + { + try + { + await FlushAsync().ConfigureAwait(false); + } + finally + { + await _strategy.DisposeAsync().ConfigureAwait(false); + } + } + } + finally + { + // Don't set the buffer to null, to avoid a NullReferenceException + // when users have a race condition in their code (i.e. they call + // FileStream.Close when calling another method on FileStream like Read). + + _writePos = 0; // WriteByte hot path relies on this + } + } + + internal override void DisposeInternal(bool disposing) => Dispose(disposing); + + protected override void Dispose(bool disposing) + { + try + { + if (disposing && !_strategy.IsClosed) + { + try + { + Flush(); + } + finally + { + _strategy.Dispose(); + } + } + } + finally + { + // Don't set the buffer to null, to avoid a NullReferenceException + // when users have a race condition in their code (i.e. they call + // FileStream.Close when calling another method on FileStream like Read). + + // Call base.Dispose(bool) to cleanup async IO resources + base.Dispose(disposing); + + _writePos = 0; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + AssertBufferArguments(buffer, offset, count); + + return ReadSpan(new Span(buffer, offset, count), new ArraySegment(buffer, offset, count)); + } + + public override int Read(Span destination) + { + EnsureNotClosed(); + + return ReadSpan(destination, default); + } + + private int ReadSpan(Span destination, ArraySegment arraySegment) + { + Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), + "We're either reading or writing, but not both."); + + bool isBlocked = false; + int n = _readLen - _readPos; + // if the read buffer is empty, read into either user's array or our + // buffer, depending on number of bytes user asked for and buffer size. + if (n == 0) + { + EnsureCanRead(); + + if (_writePos > 0) + { + FlushWrite(); + } + + if (!_strategy.CanSeek || (destination.Length >= _bufferSize)) + { + // For async file stream strategies the call to Read(Span) is translated to Stream.Read(Span), + // which rents an array from the pool, copies the data, and then calls Read(Array). This is expensive! + // To avoid that (and code duplication), the Read(Array) method passes ArraySegment to this method + // which allows for calling Strategy.Read(Array) instead of Strategy.Read(Span). + n = arraySegment.Array != null + ? _strategy.Read(arraySegment.Array, arraySegment.Offset, arraySegment.Count) + : _strategy.Read(destination); + + // Throw away read buffer. + _readPos = 0; + _readLen = 0; + return n; + } + + EnsureBufferAllocated(); + n = _strategy.Read(_buffer!, 0, _bufferSize); + + if (n == 0) + { + return 0; + } + + isBlocked = n < _bufferSize; + _readPos = 0; + _readLen = n; + } + // Now copy min of count or numBytesAvailable (i.e. near EOF) to array. + if (n > destination.Length) + { + n = destination.Length; + } + new ReadOnlySpan(_buffer!, _readPos, n).CopyTo(destination); + _readPos += n; + + // We may have read less than the number of bytes the user asked + // for, but that is part of the Stream contract. Reading again for + // more data may cause us to block if we're using a device with + // no clear end of file, such as a serial port or pipe. If we + // blocked here & this code was used with redirected pipes for a + // process's standard output, this can lead to deadlocks involving + // two processes. But leave this here for files to avoid what would + // probably be a breaking change. -- + + // If we are reading from a device with no clear EOF like a + // serial port or a pipe, this will cause us to block incorrectly. + if (!_strategy.IsPipe) + { + // If we hit the end of the buffer and didn't have enough bytes, we must + // read some more from the underlying stream. However, if we got + // fewer bytes from the underlying stream than we asked for (i.e. we're + // probably blocked), don't ask for more bytes. + if (n < destination.Length && !isBlocked) + { + Debug.Assert(_readPos == _readLen, "Read buffer should be empty!"); + + int moreBytesRead = arraySegment.Array != null + ? _strategy.Read(arraySegment.Array, arraySegment.Offset + n, arraySegment.Count - n) + : _strategy.Read(destination.Slice(n)); + + n += moreBytesRead; + // We've just made our buffer inconsistent with our position + // pointer. We must throw away the read buffer. + _readPos = 0; + _readLen = 0; + } + } + + return n; + } + + public override int ReadByte() => _readPos != _readLen ? _buffer![_readPos++] : ReadByteSlow(); + + private int ReadByteSlow() + { + Debug.Assert(_readPos == _readLen); + + // We want to check for whether the underlying stream has been closed and whether + // it's readable, but we only need to do so if we don't have data in our buffer, + // as any data we have came from reading it from an open stream, and we don't + // care if the stream has been closed or become unreadable since. Further, if + // the stream is closed, its read buffer is flushed, so we'll take this slow path. + EnsureNotClosed(); + EnsureCanRead(); + + if (_writePos > 0) + { + FlushWrite(); + } + + EnsureBufferAllocated(); + _readLen = _strategy.Read(_buffer!, 0, _bufferSize); + _readPos = 0; + + if (_readLen == 0) + { + return -1; + } + + return _buffer![_readPos++]; + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + AssertBufferArguments(buffer, offset, count); + + ValueTask readResult = ReadAsync(new Memory(buffer, offset, count), cancellationToken); + + return readResult.IsCompletedSuccessfully + ? LastSyncCompletedReadTask(readResult.Result) + : readResult.AsTask(); + + Task LastSyncCompletedReadTask(int val) + { + Task? t = _lastSyncCompletedReadTask; + Debug.Assert(t == null || t.IsCompletedSuccessfully); + + if (t != null && t.Result == val) + return t; + + t = Task.FromResult(val); + _lastSyncCompletedReadTask = t; + return t; + } + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + EnsureCanRead(); + + Debug.Assert(!_strategy.IsClosed, "FileStream ensures that strategy is not closed"); + Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), + "We're either reading or writing, but not both."); + + if (_strategy.IsPipe) // pipes have a very limited support for buffering + { + return ReadFromPipeAsync(buffer, cancellationToken); + } + + SemaphoreSlim semaphore = EnsureAsyncActiveSemaphoreInitialized(); + Task semaphoreLockTask = semaphore.WaitAsync(cancellationToken); + + if (semaphoreLockTask.IsCompletedSuccessfully // lock has been acquired + && _writePos == 0) // there is nothing to flush + { + bool releaseTheLock = true; + try + { + if (_readLen - _readPos >= buffer.Length) + { + // hot path #1: there is enough data in the buffer + _buffer.AsSpan(_readPos, buffer.Length).CopyTo(buffer.Span); + _readPos += buffer.Length; + return new ValueTask(buffer.Length); + } + else if (_readLen == _readPos && buffer.Length >= _bufferSize) + { + // hot path #2: the read buffer is empty and buffering would not be beneficial + return _strategy.ReadAsync(buffer, cancellationToken); + } + + releaseTheLock = false; + } + finally + { + if (releaseTheLock) + { + semaphore.Release(); + } + // the code is going to call ReadAsyncSlowPath which is going to release the lock + } + } + + return ReadAsyncSlowPath(semaphoreLockTask, buffer, cancellationToken); + } + + private async ValueTask ReadFromPipeAsync(Memory destination, CancellationToken cancellationToken) + { + Debug.Assert(_strategy.IsPipe); + + // Employ async waiting based on the same synchronization used in BeginRead of the abstract Stream. + await EnsureAsyncActiveSemaphoreInitialized().WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + // Pipes are tricky, at least when you have 2 different pipes + // that you want to use simultaneously. When redirecting stdout + // & stderr with the Process class, it's easy to deadlock your + // parent & child processes when doing writes 4K at a time. The + // OS appears to use a 4K buffer internally. If you write to a + // pipe that is full, you will block until someone read from + // that pipe. If you try reading from an empty pipe and + // Win32FileStream's ReadAsync blocks waiting for data to fill it's + // internal buffer, you will be blocked. In a case where a child + // process writes to stdout & stderr while a parent process tries + // reading from both, you can easily get into a deadlock here. + // To avoid this deadlock, don't buffer when doing async IO on + // pipes. But don't completely ignore buffered data either. + if (_readPos < _readLen) + { + int n = Math.Min(_readLen - _readPos, destination.Length); + new Span(_buffer!, _readPos, n).CopyTo(destination.Span); + _readPos += n; + return n; + } + else + { + Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional."); + return await _strategy.ReadAsync(destination, cancellationToken).ConfigureAwait(false); + } + } + finally + { + _asyncActiveSemaphore.Release(); + } + } + + private async ValueTask ReadAsyncSlowPath(Task semaphoreLockTask, Memory buffer, CancellationToken cancellationToken) + { + Debug.Assert(_asyncActiveSemaphore != null); + Debug.Assert(!_strategy.IsPipe); + + // Employ async waiting based on the same synchronization used in BeginRead of the abstract Stream. + await semaphoreLockTask.ConfigureAwait(false); + try + { + int bytesFromBuffer = 0; + int bytesAlreadySatisfied = 0; + + if (_readLen - _readPos > 0) + { + // The buffer might have been changed by another async task while we were waiting on the semaphore. + // Check it now again. + bytesFromBuffer = Math.Min(buffer.Length, _readLen - _readPos); + + if (bytesFromBuffer > 0) // don't try to copy 0 bytes + { + _buffer.AsSpan(_readPos, bytesFromBuffer).CopyTo(buffer.Span); + _readPos += bytesFromBuffer; + } + + if (bytesFromBuffer == buffer.Length) + { + return bytesFromBuffer; + } + + if (bytesFromBuffer > 0) + { + buffer = buffer.Slice(bytesFromBuffer); + bytesAlreadySatisfied += bytesFromBuffer; + } + } + + Debug.Assert(_readLen == _readPos, "The read buffer must now be empty"); + _readPos = _readLen = 0; + + // If there was anything in the write buffer, clear it. + if (_writePos > 0) + { + await _strategy.WriteAsync(new ReadOnlyMemory(_buffer, 0, _writePos), cancellationToken).ConfigureAwait(false); + _writePos = 0; + } + + // If the requested read is larger than buffer size, avoid the buffer and still use a single read: + if (buffer.Length >= _bufferSize) + { + return bytesAlreadySatisfied + await _strategy.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + } + + // Ok. We can fill the buffer: + EnsureBufferAllocated(); + _readLen = await _strategy.ReadAsync(new Memory(_buffer, 0, _bufferSize), cancellationToken).ConfigureAwait(false); + + bytesFromBuffer = Math.Min(_readLen, buffer.Length); + _buffer.AsSpan(0, bytesFromBuffer).CopyTo(buffer.Span); + _readPos += bytesFromBuffer; + return bytesAlreadySatisfied + bytesFromBuffer; + } + finally + { + _asyncActiveSemaphore.Release(); + } + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => TaskToApm.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state); + + public override int EndRead(IAsyncResult asyncResult) + => TaskToApm.End(asyncResult); + + public override void Write(byte[] buffer, int offset, int count) + { + AssertBufferArguments(buffer, offset, count); + + WriteSpan(new ReadOnlySpan(buffer, offset, count), new ArraySegment(buffer, offset, count)); + } + + public override void Write(ReadOnlySpan buffer) + { + EnsureNotClosed(); + + WriteSpan(buffer, default); + } + + private void WriteSpan(ReadOnlySpan source, ArraySegment arraySegment) + { + if (_writePos == 0) + { + EnsureCanWrite(); + ClearReadBufferBeforeWrite(); + } + + // If our buffer has data in it, copy data from the user's array into + // the buffer, and if we can fit it all there, return. Otherwise, write + // the buffer to disk and copy any remaining data into our buffer. + // The assumption here is memcpy is cheaper than disk (or net) IO. + // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy) + // So the extra copying will reduce the total number of writes, in + // non-pathological cases (i.e. write 1 byte, then write for the buffer + // size repeatedly) + if (_writePos > 0) + { + int numBytes = _bufferSize - _writePos; // space left in buffer + if (numBytes > 0) + { + if (numBytes >= source.Length) + { + source.CopyTo(_buffer!.AsSpan(_writePos)); + _writePos += source.Length; + return; + } + else + { + source.Slice(0, numBytes).CopyTo(_buffer!.AsSpan(_writePos)); + _writePos += numBytes; + source = source.Slice(numBytes); + if (arraySegment.Array != null) + { + arraySegment = arraySegment.Slice(numBytes); + } + } + } + + FlushWrite(); + Debug.Assert(_writePos == 0, "FlushWrite must set _writePos to 0"); + } + + // If the buffer would slow _bufferSize down, avoid buffer completely. + if (source.Length >= _bufferSize) + { + Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); + + // For async file stream strategies the call to Write(Span) is translated to Stream.Write(Span), + // which rents an array from the pool, copies the data, and then calls Write(Array). This is expensive! + // To avoid that (and code duplication), the Write(Array) method passes ArraySegment to this method + // which allows for calling Strategy.Write(Array) instead of Strategy.Write(Span). + if (arraySegment.Array != null) + { + _strategy.Write(arraySegment.Array, arraySegment.Offset, arraySegment.Count); + } + else + { + _strategy.Write(source); + } + + return; + } + else if (source.Length == 0) + { + return; // Don't allocate a buffer then call memcpy for 0 bytes. + } + + // Copy remaining bytes into buffer, to write at a later date. + EnsureBufferAllocated(); + source.CopyTo(_buffer!.AsSpan(_writePos)); + _writePos = source.Length; + } + + public override void WriteByte(byte value) + { + if (_writePos > 0 && _writePos < _bufferSize - 1) + { + _buffer![_writePos++] = value; + } + else + { + WriteByteSlow(value); + } + } + + private void WriteByteSlow(byte value) + { + if (_writePos == 0) + { + EnsureNotClosed(); + EnsureCanWrite(); + ClearReadBufferBeforeWrite(); + EnsureBufferAllocated(); + } + else if (_writePos == _bufferSize - 1) + { + FlushWrite(); + } + + _buffer![_writePos++] = value; + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + AssertBufferArguments(buffer, offset, count); + + return WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + EnsureCanWrite(); + + Debug.Assert(!_strategy.IsClosed, "FileStream ensures that strategy is not closed"); + Debug.Assert((_readPos == 0 && _readLen == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLen), + "We're either reading or writing, but not both."); + Debug.Assert(!_strategy.IsPipe || (_readPos == 0 && _readLen == 0), + "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); + + if (_strategy.IsPipe) + { + // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadFromPipeAsync) + return WriteToPipeAsync(buffer, cancellationToken); + } + + SemaphoreSlim semaphore = EnsureAsyncActiveSemaphoreInitialized(); + Task semaphoreLockTask = semaphore.WaitAsync(cancellationToken); + + if (semaphoreLockTask.IsCompletedSuccessfully // lock has been acquired + && _readPos == _readLen) // there is nothing to flush + { + bool releaseTheLock = true; + try + { + // hot path #1 if the write completely fits into the buffer, we can complete synchronously: + if (_bufferSize - _writePos >= buffer.Length) + { + EnsureBufferAllocated(); + buffer.Span.CopyTo(_buffer.AsSpan(_writePos)); + _writePos += buffer.Length; + return default; + } + else if (_writePos == 0 && buffer.Length >= _bufferSize) + { + // hot path #2: the write buffer is empty and buffering would not be beneficial + return _strategy.WriteAsync(buffer, cancellationToken); + } + + releaseTheLock = false; + } + finally + { + if (releaseTheLock) + { + semaphore.Release(); + } + // the code is going to call ReadAsyncSlowPath which is going to release the lock + } + } + + return WriteAsyncSlowPath(semaphoreLockTask, buffer, cancellationToken); + } + + private async ValueTask WriteToPipeAsync(ReadOnlyMemory source, CancellationToken cancellationToken) + { + Debug.Assert(_strategy.IsPipe); + + await EnsureAsyncActiveSemaphoreInitialized().WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + await _strategy.WriteAsync(source, cancellationToken).ConfigureAwait(false); + } + finally + { + _asyncActiveSemaphore.Release(); + } + } + + private async ValueTask WriteAsyncSlowPath(Task semaphoreLockTask, ReadOnlyMemory source, CancellationToken cancellationToken) + { + Debug.Assert(_asyncActiveSemaphore != null); + Debug.Assert(!_strategy.IsPipe); + + await semaphoreLockTask.ConfigureAwait(false); + try + { + if (_writePos == 0) + { + ClearReadBufferBeforeWrite(); + } + + // If our buffer has data in it, copy data from the user's array into + // the buffer, and if we can fit it all there, return. Otherwise, write + // the buffer to disk and copy any remaining data into our buffer. + // The assumption here is memcpy is cheaper than disk (or net) IO. + // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy) + // So the extra copying will reduce the total number of writes, in + // non-pathological cases (i.e. write 1 byte, then write for the buffer + // size repeatedly) + if (_writePos > 0) + { + int spaceLeft = _bufferSize - _writePos; + if (spaceLeft > 0) + { + if (spaceLeft >= source.Length) + { + source.Span.CopyTo(_buffer!.AsSpan(_writePos)); + _writePos += source.Length; + return; + } + else + { + source.Span.Slice(0, spaceLeft).CopyTo(_buffer!.AsSpan(_writePos)); + _writePos += spaceLeft; + source = source.Slice(spaceLeft); + } + } + + await _strategy.WriteAsync(new ReadOnlyMemory(_buffer, 0, _writePos), cancellationToken).ConfigureAwait(false); + _writePos = 0; + } + + // If the buffer would slow _bufferSize down, avoid buffer completely. + if (source.Length >= _bufferSize) + { + Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted."); + await _strategy.WriteAsync(source, cancellationToken).ConfigureAwait(false); + return; + } + else if (source.Length == 0) + { + return; // Don't allocate a buffer then call memcpy for 0 bytes. + } + + // Copy remaining bytes into buffer, to write at a later date. + EnsureBufferAllocated(); + source.Span.CopyTo(_buffer!.AsSpan(_writePos)); + _writePos = source.Length; + } + finally + { + _asyncActiveSemaphore.Release(); + } + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) + => TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state); + + public override void EndWrite(IAsyncResult asyncResult) + => TaskToApm.End(asyncResult); + + public override void SetLength(long value) + { + Flush(); + + _strategy.SetLength(value); + } + + public override void Flush() => Flush(flushToDisk: false); + + internal override void Flush(bool flushToDisk) + { + EnsureNotClosed(); + + // Has write data in the buffer: + if (_writePos > 0) + { + // EnsureNotClosed does not guarantee that the Stream has not been closed + // an example could be a call to fileStream.SafeFileHandle.Dispose() + // so to avoid getting exception here, we just ensure that we can Write before doing it + if (_strategy.CanWrite) + { + FlushWrite(); + Debug.Assert(_writePos == 0 && _readPos == 0 && _readLen == 0); + return; + } + } + + // Has read data in the buffer: + if (_readPos < _readLen) + { + // If the underlying strategy is not seekable AND we have something in the read buffer, then FlushRead would throw. + // We can either throw away the buffer resulting in data loss (!) or ignore the Flush. + // (We cannot throw because it would be a breaking change.) We opt into ignoring the Flush in that situation. + if (_strategy.CanSeek) + { + FlushRead(); + } + + // If the Stream was seekable, then we should have called FlushRead which resets _readPos & _readLen. + Debug.Assert(_writePos == 0 && (!_strategy.CanSeek || (_readPos == 0 && _readLen == 0))); + return; + } + + // We had no data in the buffer, but we still need to tell the underlying strategy to flush. + _strategy.Flush(flushToDisk); + + _writePos = _readPos = _readLen = 0; + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + + EnsureNotClosed(); + + return FlushAsyncInternal(cancellationToken); + } + + private async Task FlushAsyncInternal(CancellationToken cancellationToken) + { + await EnsureAsyncActiveSemaphoreInitialized().WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + if (_writePos > 0) + { + await _strategy.WriteAsync(new ReadOnlyMemory(_buffer, 0, _writePos), cancellationToken).ConfigureAwait(false); + _writePos = 0; + Debug.Assert(_writePos == 0 && _readPos == 0 && _readLen == 0); + return; + } + + if (_readPos < _readLen) + { + // If the underlying strategy is not seekable AND we have something in the read buffer, then FlushRead would throw. + // We can either throw away the buffer resulting in date loss (!) or ignore the Flush. (We cannot throw because it + // would be a breaking change.) We opt into ignoring the Flush in that situation. + if (_strategy.CanSeek) + { + FlushRead(); // not async; it uses Seek, but there's no SeekAsync + } + + // If the Strategy was seekable, then we should have called FlushRead which resets _readPos & _readLen. + Debug.Assert(_writePos == 0 && (!_strategy.CanSeek || (_readPos == 0 && _readLen == 0))); + return; + } + + // There was nothing in the buffer: + Debug.Assert(_writePos == 0 && _readPos == _readLen); + } + finally + { + _asyncActiveSemaphore.Release(); + } + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + ValidateCopyToArguments(destination, bufferSize); + EnsureNotClosed(); + EnsureCanRead(); + + return cancellationToken.IsCancellationRequested ? + Task.FromCanceled(cancellationToken) : + CopyToAsyncCore(destination, bufferSize, cancellationToken); + } + + private async Task CopyToAsyncCore(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // Synchronize async operations as does Read/WriteAsync. + await EnsureAsyncActiveSemaphoreInitialized().WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + int readBytes = _readLen - _readPos; + Debug.Assert(readBytes >= 0, $"Expected a non-negative number of bytes in buffer, got {readBytes}"); + + if (readBytes > 0) + { + // If there's any read data in the buffer, write it all to the destination stream. + Debug.Assert(_writePos == 0, "Write buffer must be empty if there's data in the read buffer"); + await destination.WriteAsync(new ReadOnlyMemory(_buffer, _readPos, readBytes), cancellationToken).ConfigureAwait(false); + _readPos = _readLen = 0; + } + else if (_writePos > 0) + { + // If there's write data in the buffer, flush it back to the underlying stream, as does ReadAsync. + await _strategy.WriteAsync(new ReadOnlyMemory(_buffer, 0, _writePos), cancellationToken).ConfigureAwait(false); + _writePos = 0; + } + + // Our buffer is now clear. Copy data directly from the source stream to the destination stream. + await _strategy.CopyToAsync(destination, bufferSize, cancellationToken).ConfigureAwait(false); + } + finally + { + _asyncActiveSemaphore.Release(); + } + } + + public override void CopyTo(Stream destination, int bufferSize) + { + ValidateCopyToArguments(destination, bufferSize); + EnsureNotClosed(); + EnsureCanRead(); + + int readBytes = _readLen - _readPos; + Debug.Assert(readBytes >= 0, $"Expected a non-negative number of bytes in buffer, got {readBytes}"); + + if (readBytes > 0) + { + // If there's any read data in the buffer, write it all to the destination stream. + Debug.Assert(_writePos == 0, "Write buffer must be empty if there's data in the read buffer"); + destination.Write(_buffer!, _readPos, readBytes); + _readPos = _readLen = 0; + } + else if (_writePos > 0) + { + // If there's write data in the buffer, flush it back to the underlying stream, as does ReadAsync. + FlushWrite(); + } + + // Our buffer is now clear. Copy data directly from the source stream to the destination stream. + _strategy.CopyTo(destination, bufferSize); + } + + public override long Seek(long offset, SeekOrigin origin) + { + EnsureNotClosed(); + EnsureCanSeek(); + + // If we have bytes in the write buffer, flush them out, seek and be done. + if (_writePos > 0) + { + FlushWrite(); + return _strategy.Seek(offset, origin); + } + + // The buffer is either empty or we have a buffered read. + if (_readLen - _readPos > 0 && origin == SeekOrigin.Current) + { + // If we have bytes in the read buffer, adjust the seek offset to account for the resulting difference + // between this stream's position and the underlying stream's position. + offset -= (_readLen - _readPos); + } + + long oldPos = Position; + Debug.Assert(oldPos == _strategy.Position + (_readPos - _readLen)); + + long newPos = _strategy.Seek(offset, origin); + + // If the seek destination is still within the data currently in the buffer, we want to keep the buffer data and continue using it. + // Otherwise we will throw away the buffer. This can only happen on read, as we flushed write data above. + + // The offset of the new/updated seek pointer within _buffer: + _readPos = (int)(newPos - (oldPos - _readPos)); + + // If the offset of the updated seek pointer in the buffer is still legal, then we can keep using the buffer: + if (0 <= _readPos && _readPos < _readLen) + { + // Adjust the seek pointer of the underlying stream to reflect the amount of useful bytes in the read buffer: + _strategy.Seek(_readLen - _readPos, SeekOrigin.Current); + } + else + { // The offset of the updated seek pointer is not a legal offset. Loose the buffer. + _readPos = _readLen = 0; + } + + Debug.Assert(newPos == Position, "newPos (=" + newPos + ") == Position (=" + Position + ")"); + return newPos; + } + + internal override void Lock(long position, long length) => _strategy.Lock(position, length); + + internal override void Unlock(long position, long length) => _strategy.Unlock(position, length); + + // Reading is done in blocks, but someone could read 1 byte from the buffer then write. + // At that point, the underlying stream's pointer is out of sync with this stream's position. + // All write functions should call this function to ensure that the buffered data is not lost. + private void FlushRead() + { + Debug.Assert(_writePos == 0, "Write buffer must be empty in FlushRead!"); + + if (_readPos - _readLen != 0) + { + _strategy.Seek(_readPos - _readLen, SeekOrigin.Current); + } + + _readPos = 0; + _readLen = 0; + } + + private void FlushWrite() + { + Debug.Assert(_readPos == 0 && _readLen == 0, "Read buffer must be empty in FlushWrite!"); + Debug.Assert(_buffer != null && _bufferSize >= _writePos, "Write buffer must be allocated and write position must be in the bounds of the buffer in FlushWrite!"); + + _strategy.Write(_buffer, 0, _writePos); + _writePos = 0; + } + + /// + /// Called by Write methods to clear the Read Buffer + /// + private void ClearReadBufferBeforeWrite() + { + Debug.Assert(_readPos <= _readLen, "_readPos <= _readLen [" + _readPos + " <= " + _readLen + "]"); + + // No read data in the buffer: + if (_readPos == _readLen) + { + _readPos = _readLen = 0; + return; + } + + // Must have read data. + Debug.Assert(_readPos < _readLen); + FlushRead(); + } + + private void EnsureNotClosed() + { + if (_strategy.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); + } + } + + private void EnsureCanSeek() + { + if (!_strategy.CanSeek) + { + ThrowHelper.ThrowNotSupportedException_UnseekableStream(); + } + } + + private void EnsureCanRead() + { + if (!_strategy.CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + } + + private void EnsureCanWrite() + { + if (!_strategy.CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } + } + + private void EnsureBufferAllocated() + { + // BufferedFileStreamStrategy is not intended for multi-threaded use, so no worries about the get/set race on _buffer. + if (_buffer == null) + { + AllocateBuffer(); + } + + void AllocateBuffer() // logic kept in a separate method to get EnsureBufferAllocated() inlined + { + _strategy.OnBufferAllocated(_buffer = new byte[_bufferSize]); + } + } + + [Conditional("DEBUG")] + private void AssertBufferArguments(byte[] buffer, int offset, int count) + { + ValidateBufferArguments(buffer, offset, count); // FileStream is supposed to call this + Debug.Assert(!_strategy.IsClosed, "FileStream ensures that strategy is not closed"); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/DerivedFileStreamStrategy.cs similarity index 88% rename from src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/DerivedFileStreamStrategy.cs index 76d4441309f7b..e6c63246d0a19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/DerivedFileStreamStrategy.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -namespace System.IO +namespace System.IO.Strategies { // this type exists so we can avoid GetType() != typeof(FileStream) checks in FileStream // when FileStream was supposed to call base.Method() for such cases, we just call _fileStream.BaseMethod() @@ -15,8 +15,21 @@ namespace System.IO internal sealed class DerivedFileStreamStrategy : FileStreamStrategy { private readonly FileStreamStrategy _strategy; + private readonly FileStream _fileStream; - internal DerivedFileStreamStrategy(FileStream fileStream, FileStreamStrategy strategy) : base(fileStream) => _strategy = strategy; + internal DerivedFileStreamStrategy(FileStream fileStream, FileStreamStrategy strategy) + { + _fileStream = fileStream; + _strategy = strategy; + } + + ~DerivedFileStreamStrategy() + { + // Preserved for compatibility since FileStream has defined a + // finalizer in past releases and derived classes may depend + // on Dispose(false) call. + _fileStream.DisposeInternal(false); + } public override bool CanRead => _strategy.CanRead; @@ -36,7 +49,14 @@ public override long Position internal override string Name => _strategy.Name; - internal override SafeFileHandle SafeFileHandle => _strategy.SafeFileHandle; + internal override SafeFileHandle SafeFileHandle + { + get + { + _fileStream.Flush(false); + return _strategy.SafeFileHandle; + } + } internal override bool IsClosed => _strategy.IsClosed; @@ -136,6 +156,14 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio public override ValueTask DisposeAsync() => _fileStream.BaseDisposeAsync(); - internal override void DisposeInternal(bool disposing) => _strategy.DisposeInternal(disposing); + internal override void DisposeInternal(bool disposing) + { + _strategy.DisposeInternal(disposing); + + if (disposing) + { + GC.SuppressFinalize(this); + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs new file mode 100644 index 0000000000000..679d62422a4ae --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + // to avoid code duplicaiton of FileStreamCompletionSource for Net5CompatFileStreamStrategy and AsyncWindowsFileStreamStrategy + // we have created the following interface that is a common contract for both of them + internal interface IFileStreamCompletionSourceStrategy + { + SafeFileHandle FileHandle { get; } + + FileStreamCompletionSource? CurrentOverlappedOwner { get; } + + FileStreamCompletionSource? CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource); + } + + // This is an internal object extending TaskCompletionSource with fields + // for all of the relevant data necessary to complete the IO operation. + // This is used by IOCallback and all of the async methods. + internal unsafe class FileStreamCompletionSource : TaskCompletionSource + { + private const long NoResult = 0; + private const long ResultSuccess = (long)1 << 32; + private const long ResultError = (long)2 << 32; + private const long RegisteringCancellation = (long)4 << 32; + private const long CompletedCallback = (long)8 << 32; + private const ulong ResultMask = ((ulong)uint.MaxValue) << 32; + private const int ERROR_BROKEN_PIPE = 109; + private const int ERROR_NO_DATA = 232; + + internal static readonly unsafe IOCompletionCallback s_ioCallback = IOCallback; + + private static Action? s_cancelCallback; + + private readonly IFileStreamCompletionSourceStrategy _strategy; + private readonly int _numBufferedBytes; + private CancellationTokenRegistration _cancellationRegistration; +#if DEBUG + private bool _cancellationHasBeenRegistered; +#endif + private NativeOverlapped* _overlapped; // Overlapped class responsible for operations in progress when an appdomain unload occurs + private long _result; // Using long since this needs to be used in Interlocked APIs + + // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations) + internal FileStreamCompletionSource(IFileStreamCompletionSourceStrategy strategy, PreAllocatedOverlapped? preallocatedOverlapped, + int numBufferedBytes, byte[]? bytes) : base(TaskCreationOptions.RunContinuationsAsynchronously) + { + _numBufferedBytes = numBufferedBytes; + _strategy = strategy; + _result = NoResult; + + // The _preallocatedOverlapped is null if the internal buffer was never created, so we check for + // a non-null bytes before using the stream's _preallocatedOverlapped + _overlapped = bytes != null && strategy.CompareExchangeCurrentOverlappedOwner(this, null) == null ? + strategy.FileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(preallocatedOverlapped!) : // allocated when buffer was created, and buffer is non-null + strategy.FileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(s_ioCallback, this, bytes); + Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null"); + } + + internal NativeOverlapped* Overlapped => _overlapped; + + public void SetCompletedSynchronously(int numBytes) + { + ReleaseNativeResource(); + TrySetResult(numBytes + _numBufferedBytes); + } + + public void RegisterForCancellation(CancellationToken cancellationToken) + { +#if DEBUG + Debug.Assert(cancellationToken.CanBeCanceled); + Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice"); + _cancellationHasBeenRegistered = true; +#endif + + // Quick check to make sure the IO hasn't completed + if (_overlapped != null) + { + Action? cancelCallback = s_cancelCallback ??= Cancel; + + // Register the cancellation only if the IO hasn't completed + long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult); + if (packedResult == NoResult) + { + _cancellationRegistration = cancellationToken.UnsafeRegister(cancelCallback, this); + + // Switch the result, just in case IO completed while we were setting the registration + packedResult = Interlocked.Exchange(ref _result, NoResult); + } + else if (packedResult != CompletedCallback) + { + // Failed to set the result, IO is in the process of completing + // Attempt to take the packed result + packedResult = Interlocked.Exchange(ref _result, NoResult); + } + + // If we have a callback that needs to be completed + if ((packedResult != NoResult) && (packedResult != CompletedCallback) && (packedResult != RegisteringCancellation)) + { + CompleteCallback((ulong)packedResult); + } + } + } + + internal virtual void ReleaseNativeResource() + { + // Ensure that cancellation has been completed and cleaned up. + _cancellationRegistration.Dispose(); + + // Free the overlapped. + // NOTE: The cancellation must *NOT* be running at this point, or it may observe freed memory + // (this is why we disposed the registration above). + if (_overlapped != null) + { + _strategy.FileHandle.ThreadPoolBinding!.FreeNativeOverlapped(_overlapped); + _overlapped = null; + } + + // Ensure we're no longer set as the current completion source (we may not have been to begin with). + // Only one operation at a time is eligible to use the preallocated overlapped, + _strategy.CompareExchangeCurrentOverlappedOwner(null, this); + } + + // When doing IO asynchronously (i.e. _isAsync==true), this callback is + // called by a free thread in the threadpool when the IO operation + // completes. + internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped) + { + // Extract the completion source from the overlapped. The state in the overlapped + // will either be a FileStreamStrategy (in the case where the preallocated overlapped was used), + // in which case the operation being completed is its _currentOverlappedOwner, or it'll + // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated + // overlapped was already in use by another operation). + object? state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); + Debug.Assert(state is IFileStreamCompletionSourceStrategy || state is FileStreamCompletionSource); + FileStreamCompletionSource completionSource = state switch + { + IFileStreamCompletionSourceStrategy strategy => strategy.CurrentOverlappedOwner!, // must be owned + _ => (FileStreamCompletionSource)state + }; + Debug.Assert(completionSource != null); + Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match"); + + // Handle reading from & writing to closed pipes. While I'm not sure + // this is entirely necessary anymore, maybe it's possible for + // an async read on a pipe to be issued and then the pipe is closed, + // returning this error. This may very well be necessary. + ulong packedResult; + if (errorCode != 0 && errorCode != ERROR_BROKEN_PIPE && errorCode != ERROR_NO_DATA) + { + packedResult = ((ulong)ResultError | errorCode); + } + else + { + packedResult = ((ulong)ResultSuccess | numBytes); + } + + // Stow the result so that other threads can observe it + // And, if no other thread is registering cancellation, continue + if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult)) + { + // Successfully set the state, attempt to take back the callback + if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult) + { + // Successfully got the callback, finish the callback + completionSource.CompleteCallback(packedResult); + } + // else: Some other thread stole the result, so now it is responsible to finish the callback + } + // else: Some other thread is registering a cancellation, so it *must* finish the callback + } + + private void CompleteCallback(ulong packedResult) + { + // Free up the native resource and cancellation registration + CancellationToken cancellationToken = _cancellationRegistration.Token; // access before disposing registration + ReleaseNativeResource(); + + // Unpack the result and send it to the user + long result = (long)(packedResult & ResultMask); + if (result == ResultError) + { + int errorCode = unchecked((int)(packedResult & uint.MaxValue)); + if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED) + { + TrySetCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + } + else + { + Exception e = Win32Marshal.GetExceptionForWin32Error(errorCode); + e.SetCurrentStackTrace(); + TrySetException(e); + } + } + else + { + Debug.Assert(result == ResultSuccess, "Unknown result"); + TrySetResult((int)(packedResult & uint.MaxValue) + _numBufferedBytes); + } + } + + private static void Cancel(object? state) + { + // WARNING: This may potentially be called under a lock (during cancellation registration) + + Debug.Assert(state is FileStreamCompletionSource, "Unknown state passed to cancellation"); + FileStreamCompletionSource completionSource = (FileStreamCompletionSource)state; + Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet"); + + // If the handle is still valid, attempt to cancel the IO + if (!completionSource._strategy.FileHandle.IsInvalid && + !Interop.Kernel32.CancelIoEx(completionSource._strategy.FileHandle, completionSource._overlapped)) + { + int errorCode = Marshal.GetLastWin32Error(); + + // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel. + // This probably means that the IO operation has completed. + if (errorCode != Interop.Errors.ERROR_NOT_FOUND) + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + } + } + + public static FileStreamCompletionSource Create(IFileStreamCompletionSourceStrategy strategy, PreAllocatedOverlapped? preallocatedOverlapped, + int numBufferedBytesRead, ReadOnlyMemory memory) + { + // If the memory passed in is the strategy's internal buffer, we can use the base FileStreamCompletionSource, + // which has a PreAllocatedOverlapped with the memory already pinned. Otherwise, we use the derived + // MemoryFileStreamCompletionSource, which Retains the memory, which will result in less pinning in the case + // where the underlying memory is backed by pre-pinned buffers. + return preallocatedOverlapped != null && MemoryMarshal.TryGetArray(memory, out ArraySegment buffer) + && preallocatedOverlapped.IsUserObject(buffer.Array) // preallocatedOverlapped is allocated when BufferedStream|Net5CompatFileStreamStrategy allocates the buffer + ? new FileStreamCompletionSource(strategy, preallocatedOverlapped, numBufferedBytesRead, buffer.Array) + : new MemoryFileStreamCompletionSource(strategy, numBufferedBytesRead, memory); + } + } + + /// + /// Extends with to support disposing of a + /// when the operation has completed. This should only be used + /// when memory doesn't wrap a byte[]. + /// + internal sealed class MemoryFileStreamCompletionSource : FileStreamCompletionSource + { + private MemoryHandle _handle; // mutable struct; do not make this readonly + + internal MemoryFileStreamCompletionSource(IFileStreamCompletionSourceStrategy strategy, int numBufferedBytes, ReadOnlyMemory memory) + : base(strategy, null, numBufferedBytes, null) // this type handles the pinning, so null is passed for bytes + { + _handle = memory.Pin(); + } + + internal override void ReleaseNativeResource() + { + _handle.Dispose(); + base.ReleaseNativeResource(); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs similarity index 89% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs index 471005b833f74..0d2c5df52be3a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs @@ -8,17 +8,17 @@ using System.Threading; using System.Threading.Tasks; -namespace System.IO +namespace System.IO.Strategies { // this type defines a set of stateless FileStream/FileStreamStrategy helper methods - internal static class FileStreamHelpers + internal static partial class FileStreamHelpers { // in the future we are most probably going to introduce more strategies (io_uring etc) - internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) - => new LegacyFileStreamStrategy(fileStream, handle, access, bufferSize, isAsync); + private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) + => new Net5CompatFileStreamStrategy(handle, access, bufferSize, isAsync); - internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) - => new LegacyFileStreamStrategy(fileStream, path, mode, access, share, bufferSize, options); + private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + => new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options); internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs new file mode 100644 index 0000000000000..fd3218f6eb4c3 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -0,0 +1,639 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + // this type defines a set of stateless FileStream/FileStreamStrategy helper methods + internal static partial class FileStreamHelpers + { + internal const int ERROR_BROKEN_PIPE = 109; + internal const int ERROR_NO_DATA = 232; + private const int ERROR_HANDLE_EOF = 38; + private const int ERROR_IO_PENDING = 997; + + private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) + { + if (UseNet5CompatStrategy) + { + return new Net5CompatFileStreamStrategy(handle, access, bufferSize, isAsync); + } + + WindowsFileStreamStrategy strategy = isAsync + ? new AsyncWindowsFileStreamStrategy(handle, access, share) + : new SyncWindowsFileStreamStrategy(handle, access, share); + + return EnableBufferingIfNeeded(strategy, bufferSize); + } + + private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + { + if (UseNet5CompatStrategy) + { + return new Net5CompatFileStreamStrategy(path, mode, access, share, bufferSize, options); + } + + WindowsFileStreamStrategy strategy = (options & FileOptions.Asynchronous) != 0 + ? new AsyncWindowsFileStreamStrategy(path, mode, access, share, options) + : new SyncWindowsFileStreamStrategy(path, mode, access, share, options); + + return EnableBufferingIfNeeded(strategy, bufferSize); + } + + // TODO: we might want to consider strategy.IsPipe here and never enable buffering for async pipes + internal static FileStreamStrategy EnableBufferingIfNeeded(WindowsFileStreamStrategy strategy, int bufferSize) + => bufferSize == 1 ? strategy : new BufferedFileStreamStrategy(strategy, bufferSize); + + internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + => CreateFileOpenHandle(path, mode, access, share, options); + + private static unsafe SafeFileHandle CreateFileOpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + { + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share); + + int fAccess = + ((access & FileAccess.Read) == FileAccess.Read ? Interop.Kernel32.GenericOperations.GENERIC_READ : 0) | + ((access & FileAccess.Write) == FileAccess.Write ? Interop.Kernel32.GenericOperations.GENERIC_WRITE : 0); + + // Our Inheritable bit was stolen from Windows, but should be set in + // the security attributes class. Don't leave this bit set. + share &= ~FileShare.Inheritable; + + // Must use a valid Win32 constant here... + if (mode == FileMode.Append) + mode = FileMode.OpenOrCreate; + + int flagsAndAttributes = (int)options; + + // For mitigating local elevation of privilege attack through named pipes + // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the + // named pipe server can't impersonate a high privileged client security context + // (note that this is the effective default on CreateFile2) + flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS); + + using (DisableMediaInsertionPrompt.Create()) + { + Debug.Assert(path != null); + return ValidateFileHandle( + Interop.Kernel32.CreateFile(path, fAccess, share, &secAttrs, mode, flagsAndAttributes, IntPtr.Zero), + path, + (options & FileOptions.Asynchronous) != 0); + } + } + + internal static bool GetDefaultIsAsync(SafeFileHandle handle, bool defaultIsAsync) + { + return handle.IsAsync ?? !IsHandleSynchronous(handle, ignoreInvalid: true) ?? defaultIsAsync; + } + + internal static unsafe bool? IsHandleSynchronous(SafeFileHandle fileHandle, bool ignoreInvalid) + { + if (fileHandle.IsInvalid) + return null; + + uint fileMode; + + int status = Interop.NtDll.NtQueryInformationFile( + FileHandle: fileHandle, + IoStatusBlock: out _, + FileInformation: &fileMode, + Length: sizeof(uint), + FileInformationClass: Interop.NtDll.FileModeInformation); + + switch (status) + { + case 0: + // We were successful + break; + case Interop.NtDll.STATUS_INVALID_HANDLE: + if (!ignoreInvalid) + { + throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_INVALID_HANDLE); + } + else + { + return null; + } + default: + // Something else is preventing access + Debug.Fail("Unable to get the file mode information, status was" + status.ToString()); + return null; + } + + // If either of these two flags are set, the file handle is synchronous (not overlapped) + return (fileMode & (Interop.NtDll.FILE_SYNCHRONOUS_IO_ALERT | Interop.NtDll.FILE_SYNCHRONOUS_IO_NONALERT)) > 0; + } + + internal static void VerifyHandleIsSync(SafeFileHandle handle) + { + // As we can accurately check the handle type when we have access to NtQueryInformationFile we don't need to skip for + // any particular file handle type. + + // If the handle was passed in without an explicit async setting, we already looked it up in GetDefaultIsAsync + if (!handle.IsAsync.HasValue) + return; + + // If we can't check the handle, just assume it is ok. + if (!(IsHandleSynchronous(handle, ignoreInvalid: false) ?? true)) + ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle)); + } + + private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share) + { + Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default; + if ((share & FileShare.Inheritable) != 0) + { + secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES + { + nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES), + bInheritHandle = Interop.BOOL.TRUE + }; + } + return secAttrs; + } + + private static SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle, string path, bool useAsyncIO) + { + if (fileHandle.IsInvalid) + { + // Return a meaningful exception with the full path. + + // NT5 oddity - when trying to open "C:\" as a Win32FileStream, + // we usually get ERROR_PATH_NOT_FOUND from the OS. We should + // probably be consistent w/ every other directory. + int errorCode = Marshal.GetLastWin32Error(); + + if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && path!.Length == PathInternal.GetRootLength(path)) + errorCode = Interop.Errors.ERROR_ACCESS_DENIED; + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + + fileHandle.IsAsync = useAsyncIO; + return fileHandle; + } + + internal static unsafe long GetFileLength(SafeFileHandle handle, string? path) + { + Interop.Kernel32.FILE_STANDARD_INFO info; + + if (!Interop.Kernel32.GetFileInformationByHandleEx(handle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) + { + throw Win32Marshal.GetExceptionForLastWin32Error(path); + } + + return info.EndOfFile; + } + + internal static void FlushToDisk(SafeFileHandle handle, string? path) + { + if (!Interop.Kernel32.FlushFileBuffers(handle)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(path); + } + } + + internal static long Seek(SafeFileHandle handle, string? path, long offset, SeekOrigin origin, bool closeInvalidHandle = false) + { + Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); + + if (!Interop.Kernel32.SetFilePointerEx(handle, offset, out long ret, (uint)origin)) + { + if (closeInvalidHandle) + { + throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(handle), path); + } + else + { + throw Win32Marshal.GetExceptionForLastWin32Error(path); + } + } + + return ret; + } + + private static int GetLastWin32ErrorAndDisposeHandleIfInvalid(SafeFileHandle handle) + { + int errorCode = Marshal.GetLastWin32Error(); + + // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set + // the handle as invalid; the handle must also be closed. + // + // Marking the handle as invalid but not closing the handle + // resulted in exceptions during finalization and locked column + // values (due to invalid but unclosed handle) in SQL Win32FileStream + // scenarios. + // + // A more mainstream scenario involves accessing a file on a + // network share. ERROR_INVALID_HANDLE may occur because the network + // connection was dropped and the server closed the handle. However, + // the client side handle is still open and even valid for certain + // operations. + // + // Note that _parent.Dispose doesn't throw so we don't need to special case. + // SetHandleAsInvalid only sets _closed field to true (without + // actually closing handle) so we don't need to call that as well. + if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) + { + handle.Dispose(); + } + + return errorCode; + } + + internal static void Lock(SafeFileHandle handle, string? path, long position, long length) + { + int positionLow = unchecked((int)(position)); + int positionHigh = unchecked((int)(position >> 32)); + int lengthLow = unchecked((int)(length)); + int lengthHigh = unchecked((int)(length >> 32)); + + if (!Interop.Kernel32.LockFile(handle, positionLow, positionHigh, lengthLow, lengthHigh)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(path); + } + } + + internal static void Unlock(SafeFileHandle handle, string? path, long position, long length) + { + int positionLow = unchecked((int)(position)); + int positionHigh = unchecked((int)(position >> 32)); + int lengthLow = unchecked((int)(length)); + int lengthHigh = unchecked((int)(length >> 32)); + + if (!Interop.Kernel32.UnlockFile(handle, positionLow, positionHigh, lengthLow, lengthHigh)) + { + throw Win32Marshal.GetExceptionForLastWin32Error(path); + } + } + + internal static void ValidateFileTypeForNonExtendedPaths(SafeFileHandle handle, string originalPath) + { + if (!PathInternal.IsExtended(originalPath)) + { + // To help avoid stumbling into opening COM/LPT ports by accident, we will block on non file handles unless + // we were explicitly passed a path that has \\?\. GetFullPath() will turn paths like C:\foo\con.txt into + // \\.\CON, so we'll only allow the \\?\ syntax. + + int fileType = Interop.Kernel32.GetFileType(handle); + if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) + { + int errorCode = fileType == Interop.Kernel32.FileTypes.FILE_TYPE_UNKNOWN + ? Marshal.GetLastWin32Error() + : Interop.Errors.ERROR_SUCCESS; + + handle.Dispose(); + + if (errorCode != Interop.Errors.ERROR_SUCCESS) + { + throw Win32Marshal.GetExceptionForWin32Error(errorCode); + } + throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); + } + } + } + + internal static void GetFileTypeSpecificInformation(SafeFileHandle handle, out bool canSeek, out bool isPipe) + { + int handleType = Interop.Kernel32.GetFileType(handle); + Debug.Assert(handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK + || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE + || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR, + "FileStream was passed an unknown file type!"); + + canSeek = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; + isPipe = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE; + } + + internal static unsafe void SetFileLength(SafeFileHandle handle, string? path, long length) + { + var eofInfo = new Interop.Kernel32.FILE_END_OF_FILE_INFO + { + EndOfFile = length + }; + + if (!Interop.Kernel32.SetFileInformationByHandle( + handle, + Interop.Kernel32.FileEndOfFileInfo, + &eofInfo, + (uint)sizeof(Interop.Kernel32.FILE_END_OF_FILE_INFO))) + { + int errorCode = Marshal.GetLastWin32Error(); + if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) + throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_FileLengthTooBig); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + } + + // __ConsoleStream also uses this code. + internal static unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, bool syncUsingOverlapped, NativeOverlapped* overlapped, out int errorCode) + { + Debug.Assert(handle != null, "handle != null"); + + int r; + int numBytesRead = 0; + + fixed (byte* p = &MemoryMarshal.GetReference(bytes)) + { + r = overlapped != null ? + (syncUsingOverlapped + ? Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, overlapped) + : Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped)) + : Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero); + } + + if (r == 0) + { + errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + + if (syncUsingOverlapped && errorCode == Interop.Errors.ERROR_HANDLE_EOF) + { + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile#synchronization-and-file-position : + // "If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, + // ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF" + return numBytesRead; + } + + return -1; + } + else + { + errorCode = 0; + return numBytesRead; + } + } + + internal static unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, bool syncUsingOverlapped, NativeOverlapped* overlapped, out int errorCode) + { + Debug.Assert(handle != null, "handle != null"); + + int numBytesWritten = 0; + int r; + + fixed (byte* p = &MemoryMarshal.GetReference(buffer)) + { + r = overlapped != null ? + (syncUsingOverlapped + ? Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, overlapped) + : Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped)) + : Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero); + } + + if (r == 0) + { + errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(handle); + return -1; + } + else + { + errorCode = 0; + return numBytesWritten; + } + } + + internal static async Task AsyncModeCopyToAsync(SafeFileHandle handle, string? path, bool canSeek, long filePosition, Stream destination, int bufferSize, CancellationToken cancellationToken) + { + // For efficiency, we avoid creating a new task and associated state for each asynchronous read. + // Instead, we create a single reusable awaitable object that will be triggered when an await completes + // and reset before going again. + var readAwaitable = new AsyncCopyToAwaitable(handle); + + // Make sure we are reading from the position that we think we are. + // Only set the position in the awaitable if we can seek (e.g. not for pipes). + if (canSeek) + { + readAwaitable._position = filePosition; + } + + // Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use + // _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may + // actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically + // CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized. + // Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that + // we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool. + byte[] copyBuffer = ArrayPool.Shared.Rent(bufferSize); + + // Allocate an Overlapped we can use repeatedly for all operations + var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer); + var cancellationReg = default(CancellationTokenRegistration); + try + { + // Register for cancellation. We do this once for the whole copy operation, and just try to cancel + // whatever read operation may currently be in progress, if there is one. It's possible the cancellation + // request could come in between operations, in which case we flag that with explicit calls to ThrowIfCancellationRequested + // in the read/write copy loop. + if (cancellationToken.CanBeCanceled) + { + cancellationReg = cancellationToken.UnsafeRegister(static s => + { + Debug.Assert(s is AsyncCopyToAwaitable); + var innerAwaitable = (AsyncCopyToAwaitable)s; + unsafe + { + lock (innerAwaitable.CancellationLock) // synchronize with cleanup of the overlapped + { + if (innerAwaitable._nativeOverlapped != null) + { + // Try to cancel the I/O. We ignore the return value, as cancellation is opportunistic and we + // don't want to fail the operation because we couldn't cancel it. + Interop.Kernel32.CancelIoEx(innerAwaitable._fileHandle, innerAwaitable._nativeOverlapped); + } + } + } + }, readAwaitable); + } + + // Repeatedly read from this FileStream and write the results to the destination stream. + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + readAwaitable.ResetForNextOperation(); + + try + { + bool synchronousSuccess; + int errorCode; + unsafe + { + // Allocate a native overlapped for our reusable overlapped, and set position to read based on the next + // desired address stored in the awaitable. (This position may be 0, if either we're at the beginning or + // if the stream isn't seekable.) + readAwaitable._nativeOverlapped = handle.ThreadPoolBinding!.AllocateNativeOverlapped(awaitableOverlapped); + if (canSeek) + { + readAwaitable._nativeOverlapped->OffsetLow = unchecked((int)readAwaitable._position); + readAwaitable._nativeOverlapped->OffsetHigh = (int)(readAwaitable._position >> 32); + } + + // Kick off the read. + synchronousSuccess = ReadFileNative(handle, copyBuffer, false, readAwaitable._nativeOverlapped, out errorCode) >= 0; + } + + // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation. + if (!synchronousSuccess) + { + switch (errorCode) + { + case ERROR_IO_PENDING: + // Async operation in progress. + break; + case ERROR_BROKEN_PIPE: + case ERROR_HANDLE_EOF: + // We're at or past the end of the file, and the overlapped callback + // won't be raised in these cases. Mark it as completed so that the await + // below will see it as such. + readAwaitable.MarkCompleted(); + break; + default: + // Everything else is an error (and there won't be a callback). + throw Win32Marshal.GetExceptionForWin32Error(errorCode, path); + } + } + + // Wait for the async operation (which may or may not have already completed), then throw if it failed. + await readAwaitable; + switch (readAwaitable._errorCode) + { + case 0: // success + break; + case ERROR_BROKEN_PIPE: // logically success with 0 bytes read (write end of pipe closed) + case ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) + Debug.Assert(readAwaitable._numBytes == 0, $"Expected 0 bytes read, got {readAwaitable._numBytes}"); + break; + case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled + throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); + default: // error + throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode, path); + } + + // Successful operation. If we got zero bytes, we're done: exit the read/write loop. + int numBytesRead = (int)readAwaitable._numBytes; + if (numBytesRead == 0) + { + break; + } + + // Otherwise, update the read position for next time accordingly. + if (canSeek) + { + readAwaitable._position += numBytesRead; + } + } + finally + { + // Free the resources for this read operation + unsafe + { + NativeOverlapped* overlapped; + lock (readAwaitable.CancellationLock) // just an Exchange, but we need this to be synchronized with cancellation, so using the same lock + { + overlapped = readAwaitable._nativeOverlapped; + readAwaitable._nativeOverlapped = null; + } + if (overlapped != null) + { + handle.ThreadPoolBinding!.FreeNativeOverlapped(overlapped); + } + } + } + + // Write out the read data. + await destination.WriteAsync(new ReadOnlyMemory(copyBuffer, 0, (int)readAwaitable._numBytes), cancellationToken).ConfigureAwait(false); + } + } + finally + { + // Cleanup from the whole copy operation + cancellationReg.Dispose(); + awaitableOverlapped.Dispose(); + + ArrayPool.Shared.Return(copyBuffer); + } + } + + /// Used by AsyncWindowsFileStreamStrategy and Net5CompatFileStreamStrategy CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. + private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion + { + /// Sentinel object used to indicate that the I/O operation has completed before being awaited. + private static readonly Action s_sentinel = () => { }; + /// Cached delegate to IOCallback. + internal static readonly IOCompletionCallback s_callback = IOCallback; + + internal readonly SafeFileHandle _fileHandle; + + /// Tracked position representing the next location from which to read. + internal long _position; + /// The current native overlapped pointer. This changes for each operation. + internal NativeOverlapped* _nativeOverlapped; + /// + /// null if the operation is still in progress, + /// s_sentinel if the I/O operation completed before the await, + /// s_callback if it completed after the await yielded. + /// + internal Action? _continuation; + /// Last error code from completed operation. + internal uint _errorCode; + /// Last number of read bytes from completed operation. + internal uint _numBytes; + + /// Lock object used to protect cancellation-related access to _nativeOverlapped. + internal object CancellationLock => this; + + /// Initialize the awaitable. + internal AsyncCopyToAwaitable(SafeFileHandle fileHandle) => _fileHandle = fileHandle; + + /// Reset state to prepare for the next read operation. + internal void ResetForNextOperation() + { + Debug.Assert(_position >= 0, $"Expected non-negative position, got {_position}"); + _continuation = null; + _errorCode = 0; + _numBytes = 0; + } + + /// Overlapped callback: store the results, then invoke the continuation delegate. + internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) + { + var awaitable = (AsyncCopyToAwaitable?)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP); + Debug.Assert(awaitable != null); + + Debug.Assert(!ReferenceEquals(awaitable._continuation, s_sentinel), "Sentinel must not have already been set as the continuation"); + awaitable._errorCode = errorCode; + awaitable._numBytes = numBytes; + + (awaitable._continuation ?? Interlocked.CompareExchange(ref awaitable._continuation, s_sentinel, null))?.Invoke(); + } + + /// + /// Called when it's known that the I/O callback for an operation will not be invoked but we'll + /// still be awaiting the awaitable. + /// + internal void MarkCompleted() + { + Debug.Assert(_continuation == null, "Expected null continuation"); + _continuation = s_sentinel; + } + + public AsyncCopyToAwaitable GetAwaiter() => this; + public bool IsCompleted => ReferenceEquals(_continuation, s_sentinel); + public void GetResult() { } + public void OnCompleted(Action continuation) => UnsafeOnCompleted(continuation); + public void UnsafeOnCompleted(Action continuation) + { + if (ReferenceEquals(_continuation, s_sentinel) || + Interlocked.CompareExchange(ref _continuation, continuation, null) != null) + { + Debug.Assert(ReferenceEquals(_continuation, s_sentinel), $"Expected continuation set to s_sentinel, got ${_continuation}"); + Task.Run(continuation); + } + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs new file mode 100644 index 0000000000000..d64677b48efe8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + internal static partial class FileStreamHelpers + { + // It's enabled by default. We are going to change that once we fix #16354, #25905 and #24847. + internal static bool UseNet5CompatStrategy { get; } = GetNet5CompatFileStreamSetting(); + + private static bool GetNet5CompatFileStreamSetting() + { + if (AppContext.TryGetSwitch("System.IO.UseNet5CompatFileStream", out bool fileConfig)) + { + return fileConfig; + } + + string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM"); + return envVar is null + ? true // Net5Compat is currently enabled by default; + : bool.IsTrueStringIgnoreCase(envVar) || envVar.Equals("1"); + } + + internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, FileShare share, int bufferSize, bool isAsync) + => WrapIfDerivedType(fileStream, ChooseStrategyCore(handle, access, share, bufferSize, isAsync)); + + internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) + => WrapIfDerivedType(fileStream, ChooseStrategyCore(path, mode, access, share, bufferSize, options)); + + private static FileStreamStrategy WrapIfDerivedType(FileStream fileStream, FileStreamStrategy strategy) + => fileStream.GetType() == typeof(FileStream) + ? strategy + : new DerivedFileStreamStrategy(fileStream, strategy); + + internal static bool IsIoRelatedException(Exception e) => + // These all derive from IOException + // DirectoryNotFoundException + // DriveNotFoundException + // EndOfStreamException + // FileLoadException + // FileNotFoundException + // PathTooLongException + // PipeException + e is IOException || + // Note that SecurityException is only thrown on runtimes that support CAS + // e is SecurityException || + e is UnauthorizedAccessException || + e is NotSupportedException || + (e is ArgumentException && !(e is ArgumentNullException)); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs similarity index 83% rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs index 97b5969355383..fdeba8f0df233 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs @@ -3,14 +3,10 @@ using Microsoft.Win32.SafeHandles; -namespace System.IO +namespace System.IO.Strategies { internal abstract class FileStreamStrategy : Stream { - protected readonly FileStream _fileStream; - - protected FileStreamStrategy(FileStream fileStream) => _fileStream = fileStream; - internal abstract bool IsAsync { get; } internal abstract string Name { get; } @@ -21,6 +17,8 @@ internal abstract class FileStreamStrategy : Stream internal abstract bool IsClosed { get; } + internal virtual bool IsPipe => false; + internal abstract void Lock(long position, long length); internal abstract void Unlock(long position, long length); @@ -28,5 +26,7 @@ internal abstract class FileStreamStrategy : Stream internal abstract void Flush(bool flushToDisk); internal abstract void DisposeInternal(bool disposing); + + internal virtual void OnBufferAllocated(byte[] buffer) { } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs similarity index 81% rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs index 599b51694d179..d3ac0b7cf7f16 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.OSX.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace System.IO +namespace System.IO.Strategies { - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { internal override void Lock(long position, long length) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs similarity index 90% rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs index 5233dcdb7087f..20e065d6317fd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Lock.Unix.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace System.IO +namespace System.IO.Strategies { - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { /// Prevents other processes from reading from or writing to the FileStream. /// The beginning of the range to lock. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs similarity index 97% rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs index 550b2acab2505..eb1cebc37a7f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Unix.cs @@ -8,10 +8,10 @@ using System.Threading; using System.Threading.Tasks; -namespace System.IO +namespace System.IO.Strategies { /// Provides an implementation of a file stream for Unix files. - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { /// File mode. private FileMode _mode; @@ -172,7 +172,7 @@ protected override void Dispose(bool disposing) { FlushWriteBuffer(); } - catch (Exception e) when (IsIoRelatedException(e) && !disposing) + catch (Exception e) when (!disposing && FileStreamHelpers.IsIoRelatedException(e)) { // On finalization, ignore failures from trying to flush the write buffer, // e.g. if this stream is wrapping a pipe and the pipe is now broken. @@ -224,7 +224,7 @@ public override ValueTask DisposeAsync() // override may already exist on a derived type. if (_useAsyncIO && _writePos > 0) { - return new ValueTask(Task.Factory.StartNew(static s => ((LegacyFileStreamStrategy)s!).Dispose(), this, + return new ValueTask(Task.Factory.StartNew(static s => ((Net5CompatFileStreamStrategy)s!).Dispose(), this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default)); } @@ -395,7 +395,7 @@ private unsafe int ReadNative(Span buffer) if (!CanRead) // match Windows behavior; this gets thrown synchronously { - throw Error.GetReadNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } // Serialize operations using the semaphore. @@ -449,7 +449,7 @@ private unsafe int ReadNative(Span buffer) // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (LegacyFileStreamStrategy)s!; + var thisRef = (Net5CompatFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -559,11 +559,11 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo return ValueTask.FromCanceled(cancellationToken); if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); if (!CanWrite) // match Windows behavior; this gets thrown synchronously { - throw Error.GetWriteNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } // Serialize operations using the semaphore. @@ -610,7 +610,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo // whereas on Windows it may happen before the write has completed. Debug.Assert(t.Status == TaskStatus.RanToCompletion); - var thisRef = (LegacyFileStreamStrategy)s!; + var thisRef = (Net5CompatFileStreamStrategy)s!; Debug.Assert(thisRef._asyncState != null); try { @@ -637,11 +637,11 @@ public override long Seek(long offset, SeekOrigin origin) } if (_fileHandle.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } if (!CanSeek) { - throw Error.GetSeekNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnseekableStream(); } VerifyOSHandlePosition(); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs similarity index 70% rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs index 4eaf3d69d0e2f..5ed9f5937aa21 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.Windows.cs @@ -1,13 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.Diagnostics; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -using System.Runtime.CompilerServices; /* * Win32FileStream supports different modes of accessing the disk - async mode @@ -37,44 +34,21 @@ * */ -namespace System.IO +namespace System.IO.Strategies { - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy, IFileStreamCompletionSourceStrategy { private bool _canSeek; private bool _isPipe; // Whether to disable async buffering code. private long _appendStart; // When appending, prevent overwriting file. - private static readonly unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback; - private Task _activeBufferOperation = Task.CompletedTask; // tracks in-progress async ops using the buffer private PreAllocatedOverlapped? _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations private FileStreamCompletionSource? _currentOverlappedOwner; // async op currently using the preallocated overlapped private void Init(FileMode mode, FileShare share, string originalPath, FileOptions options) { - if (!PathInternal.IsExtended(originalPath)) - { - // To help avoid stumbling into opening COM/LPT ports by accident, we will block on non file handles unless - // we were explicitly passed a path that has \\?\. GetFullPath() will turn paths like C:\foo\con.txt into - // \\.\CON, so we'll only allow the \\?\ syntax. - - int fileType = Interop.Kernel32.GetFileType(_fileHandle); - if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK) - { - int errorCode = fileType == Interop.Kernel32.FileTypes.FILE_TYPE_UNKNOWN - ? Marshal.GetLastWin32Error() - : Interop.Errors.ERROR_SUCCESS; - - _fileHandle.Dispose(); - - if (errorCode != Interop.Errors.ERROR_SUCCESS) - { - throw Win32Marshal.GetExceptionForWin32Error(errorCode); - } - throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles); - } - } + FileStreamHelpers.ValidateFileTypeForNonExtendedPaths(_fileHandle, originalPath); // This is necessary for async IO using IO Completion ports via our // managed Threadpool API's. This (theoretically) calls the OS's @@ -139,11 +113,7 @@ private void InitFromHandle(SafeFileHandle handle, FileAccess access, bool useAs private void InitFromHandleImpl(SafeFileHandle handle, bool useAsyncIO) { - int handleType = Interop.Kernel32.GetFileType(handle); - Debug.Assert(handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!"); - - _canSeek = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK; - _isPipe = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE; + FileStreamHelpers.GetFileTypeSpecificInformation(handle, out _canSeek, out _isPipe); // This is necessary for async IO using IO Completion ports via our // managed Threadpool API's. This calls the OS's @@ -189,11 +159,7 @@ public unsafe override long Length { get { - Interop.Kernel32.FILE_STANDARD_INFO info; - - if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))) - throw Win32Marshal.GetExceptionForLastWin32Error(_path); - long len = info.EndOfFile; + long len = FileStreamHelpers.GetFileLength(_fileHandle, _path); // If we're writing near the end of the file, we must include our // internal buffer in our Length calculation. Don't flush because @@ -226,7 +192,7 @@ protected override void Dispose(bool disposing) { FlushWriteBuffer(!disposing); } - catch (Exception e) when (IsIoRelatedException(e) && !disposing) + catch (Exception e) when (!disposing && FileStreamHelpers.IsIoRelatedException(e)) { // On finalization, ignore failures from trying to flush the write buffer, // e.g. if this stream is wrapping a pipe and the pipe is now broken. @@ -275,13 +241,7 @@ public override async ValueTask DisposeAsync() } } - private void FlushOSBuffer() - { - if (!Interop.Kernel32.FlushFileBuffers(_fileHandle)) - { - throw Win32Marshal.GetExceptionForLastWin32Error(_path); - } - } + private void FlushOSBuffer() => FileStreamHelpers.FlushToDisk(_fileHandle, _path); // Returns a task that flushes the internal write buffer private Task FlushWriteAsync(CancellationToken cancellationToken) @@ -368,22 +328,7 @@ private unsafe void SetLengthCore(long value) Debug.Assert(value >= 0, "value >= 0"); VerifyOSHandlePosition(); - var eofInfo = new Interop.Kernel32.FILE_END_OF_FILE_INFO - { - EndOfFile = value - }; - - if (!Interop.Kernel32.SetFileInformationByHandle( - _fileHandle, - Interop.Kernel32.FileEndOfFileInfo, - &eofInfo, - (uint)sizeof(Interop.Kernel32.FILE_END_OF_FILE_INFO))) - { - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig); - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); - } + FileStreamHelpers.SetFileLength(_fileHandle, _path, value); if (_filePosition > value) { @@ -391,11 +336,6 @@ private unsafe void SetLengthCore(long value) } } - // Instance method to help code external to this MarshalByRefObject avoid - // accessing its fields by ref. This avoids a compiler warning. - private FileStreamCompletionSource? CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource) => - Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); - private int ReadSpan(Span destination) { Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); @@ -408,7 +348,7 @@ private int ReadSpan(Span destination) // buffer, depending on number of bytes user asked for and buffer size. if (n == 0) { - if (!CanRead) throw Error.GetReadNotSupported(); + if (!CanRead) ThrowHelper.ThrowNotSupportedException_UnreadableStream(); if (_writePos > 0) FlushWriteBuffer(); if (!CanSeek || (destination.Length >= _bufferLength)) { @@ -494,7 +434,7 @@ private unsafe int ReadNative(Span buffer) else { if (errorCode == ERROR_INVALID_PARAMETER) - throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle"); + ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle)); throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); } @@ -509,8 +449,8 @@ public override long Seek(long offset, SeekOrigin origin) { if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin)); - if (_fileHandle.IsClosed) throw Error.GetFileNotOpen(); - if (!CanSeek) throw Error.GetSeekNotSupported(); + if (_fileHandle.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed(); + if (!CanSeek) ThrowHelper.ThrowNotSupportedException_UnseekableStream(); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -594,22 +534,8 @@ public override long Seek(long offset, SeekOrigin origin) private long SeekCore(SafeFileHandle fileHandle, long offset, SeekOrigin origin, bool closeInvalidHandle = false) { Debug.Assert(!fileHandle.IsClosed && _canSeek, "!fileHandle.IsClosed && _canSeek"); - Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin >= SeekOrigin.Begin && origin <= SeekOrigin.End"); - - if (!Interop.Kernel32.SetFilePointerEx(fileHandle, offset, out long ret, (uint)origin)) - { - if (closeInvalidHandle) - { - throw Win32Marshal.GetExceptionForWin32Error(GetLastWin32ErrorAndDisposeHandleIfInvalid(), _path); - } - else - { - throw Win32Marshal.GetExceptionForLastWin32Error(_path); - } - } - _filePosition = ret; - return ret; + return _filePosition = FileStreamHelpers.Seek(fileHandle, _path, offset, origin, closeInvalidHandle); } partial void OnBufferAllocated() @@ -618,9 +544,16 @@ partial void OnBufferAllocated() Debug.Assert(_preallocatedOverlapped == null); if (_useAsyncIO) - _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer); + _preallocatedOverlapped = new PreAllocatedOverlapped(FileStreamCompletionSource.s_ioCallback, this, _buffer); } + SafeFileHandle IFileStreamCompletionSourceStrategy.FileHandle => _fileHandle; + + FileStreamCompletionSource? IFileStreamCompletionSourceStrategy.CurrentOverlappedOwner => _currentOverlappedOwner; + + FileStreamCompletionSource? IFileStreamCompletionSourceStrategy.CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource? newSource, FileStreamCompletionSource? existingSource) + => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource); + private void WriteSpan(ReadOnlySpan source) { Debug.Assert(!_useAsyncIO, "Must only be used when in synchronous mode"); @@ -628,7 +561,7 @@ private void WriteSpan(ReadOnlySpan source) if (_writePos == 0) { // Ensure we can write to the stream, and ready buffer for writing. - if (!CanWrite) throw Error.GetWriteNotSupported(); + if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); if (_readPos < _readLength) FlushReadBuffer(); _readPos = 0; _readLength = 0; @@ -722,7 +655,7 @@ private unsafe void WriteCore(ReadOnlySpan source) private Task? ReadAsyncInternal(Memory destination, CancellationToken cancellationToken, out int synchronousResult) { Debug.Assert(_useAsyncIO); - if (!CanRead) throw Error.GetReadNotSupported(); + if (!CanRead) ThrowHelper.ThrowNotSupportedException_UnreadableStream(); Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); @@ -828,7 +761,7 @@ private unsafe Task ReadNativeAsync(Memory destination, int numBuffer Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!"); // Create and store async stream class library specific data in the async result - FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, numBufferedBytesRead, destination); + FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, numBufferedBytesRead, destination); NativeOverlapped* intOverlapped = completionSource.Overlapped; // Calculate position in the file we should be at after the read is done @@ -905,7 +838,7 @@ private unsafe Task ReadNativeAsync(Memory destination, int numBuffer if (errorCode == ERROR_HANDLE_EOF) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } else { @@ -938,7 +871,7 @@ private ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationTo Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both."); Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional."); - if (!CanWrite) throw Error.GetWriteNotSupported(); + if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); bool writeDataStoredInBuffer = false; if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore) @@ -1046,7 +979,7 @@ private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, Cancella Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!"); // Create and store async stream class library specific data in the async result - FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, 0, source); + FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, source); NativeOverlapped* intOverlapped = completionSource.Overlapped; if (CanSeek) @@ -1107,7 +1040,7 @@ private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, Cancella if (errorCode == ERROR_HANDLE_EOF) { - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); } else { @@ -1144,85 +1077,16 @@ private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory source, Cancella // __ConsoleStream also uses this code. private unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, NativeOverlapped* overlapped, out int errorCode) { - Debug.Assert(handle != null, "handle != null"); Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative."); - int r; - int numBytesRead = 0; - - fixed (byte* p = &MemoryMarshal.GetReference(bytes)) - { - r = _useAsyncIO ? - Interop.Kernel32.ReadFile(handle, p, bytes.Length, IntPtr.Zero, overlapped) : - Interop.Kernel32.ReadFile(handle, p, bytes.Length, out numBytesRead, IntPtr.Zero); - } - - if (r == 0) - { - errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(); - return -1; - } - else - { - errorCode = 0; - return numBytesRead; - } + return FileStreamHelpers.ReadFileNative(handle, bytes, false, overlapped, out errorCode); } private unsafe int WriteFileNative(SafeFileHandle handle, ReadOnlySpan buffer, NativeOverlapped* overlapped, out int errorCode) { - Debug.Assert(handle != null, "handle != null"); Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative."); - int numBytesWritten = 0; - int r; - - fixed (byte* p = &MemoryMarshal.GetReference(buffer)) - { - r = _useAsyncIO ? - Interop.Kernel32.WriteFile(handle, p, buffer.Length, IntPtr.Zero, overlapped) : - Interop.Kernel32.WriteFile(handle, p, buffer.Length, out numBytesWritten, IntPtr.Zero); - } - - if (r == 0) - { - errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(); - return -1; - } - else - { - errorCode = 0; - return numBytesWritten; - } - } - - private int GetLastWin32ErrorAndDisposeHandleIfInvalid() - { - int errorCode = Marshal.GetLastWin32Error(); - - // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set - // the handle as invalid; the handle must also be closed. - // - // Marking the handle as invalid but not closing the handle - // resulted in exceptions during finalization and locked column - // values (due to invalid but unclosed handle) in SQL Win32FileStream - // scenarios. - // - // A more mainstream scenario involves accessing a file on a - // network share. ERROR_INVALID_HANDLE may occur because the network - // connection was dropped and the server closed the handle. However, - // the client side handle is still open and even valid for certain - // operations. - // - // Note that _parent.Dispose doesn't throw so we don't need to special case. - // SetHandleAsInvalid only sets _closed field to true (without - // actually closing handle) so we don't need to call that as well. - if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE) - { - _fileHandle.Dispose(); - } - - return errorCode; + return FileStreamHelpers.WriteFileNative(handle, buffer, false, overlapped, out errorCode); } public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) @@ -1239,11 +1103,11 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio // Fail if the file was closed if (_fileHandle.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } if (!CanRead) { - throw Error.GetReadNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } // Bail early for cancellation if cancellation has been requested @@ -1281,164 +1145,20 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc } } - // For efficiency, we avoid creating a new task and associated state for each asynchronous read. - // Instead, we create a single reusable awaitable object that will be triggered when an await completes - // and reset before going again. - var readAwaitable = new AsyncCopyToAwaitable(this); - - // Make sure we are reading from the position that we think we are. - // Only set the position in the awaitable if we can seek (e.g. not for pipes). bool canSeek = CanSeek; if (canSeek) { VerifyOSHandlePosition(); - readAwaitable._position = _filePosition; } - // Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use - // _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may - // actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically - // CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized. - // Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that - // we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool. - byte[] copyBuffer = ArrayPool.Shared.Rent(bufferSize); - - // Allocate an Overlapped we can use repeatedly for all operations - var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer); - var cancellationReg = default(CancellationTokenRegistration); try { - // Register for cancellation. We do this once for the whole copy operation, and just try to cancel - // whatever read operation may currently be in progress, if there is one. It's possible the cancellation - // request could come in between operations, in which case we flag that with explicit calls to ThrowIfCancellationRequested - // in the read/write copy loop. - if (cancellationToken.CanBeCanceled) - { - cancellationReg = cancellationToken.UnsafeRegister(static s => - { - Debug.Assert(s is AsyncCopyToAwaitable); - var innerAwaitable = (AsyncCopyToAwaitable)s; - unsafe - { - lock (innerAwaitable.CancellationLock) // synchronize with cleanup of the overlapped - { - if (innerAwaitable._nativeOverlapped != null) - { - // Try to cancel the I/O. We ignore the return value, as cancellation is opportunistic and we - // don't want to fail the operation because we couldn't cancel it. - Interop.Kernel32.CancelIoEx(innerAwaitable._fileStream._fileHandle, innerAwaitable._nativeOverlapped); - } - } - } - }, readAwaitable); - } - - // Repeatedly read from this FileStream and write the results to the destination stream. - while (true) - { - cancellationToken.ThrowIfCancellationRequested(); - readAwaitable.ResetForNextOperation(); - - try - { - bool synchronousSuccess; - int errorCode; - unsafe - { - // Allocate a native overlapped for our reusable overlapped, and set position to read based on the next - // desired address stored in the awaitable. (This position may be 0, if either we're at the beginning or - // if the stream isn't seekable.) - readAwaitable._nativeOverlapped = _fileHandle.ThreadPoolBinding!.AllocateNativeOverlapped(awaitableOverlapped); - if (canSeek) - { - readAwaitable._nativeOverlapped->OffsetLow = unchecked((int)readAwaitable._position); - readAwaitable._nativeOverlapped->OffsetHigh = (int)(readAwaitable._position >> 32); - } - - // Kick off the read. - synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, readAwaitable._nativeOverlapped, out errorCode) >= 0; - } - - // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation. - if (!synchronousSuccess) - { - switch (errorCode) - { - case ERROR_IO_PENDING: - // Async operation in progress. - break; - case ERROR_BROKEN_PIPE: - case ERROR_HANDLE_EOF: - // We're at or past the end of the file, and the overlapped callback - // won't be raised in these cases. Mark it as completed so that the await - // below will see it as such. - readAwaitable.MarkCompleted(); - break; - default: - // Everything else is an error (and there won't be a callback). - throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); - } - } - - // Wait for the async operation (which may or may not have already completed), then throw if it failed. - await readAwaitable; - switch (readAwaitable._errorCode) - { - case 0: // success - break; - case ERROR_BROKEN_PIPE: // logically success with 0 bytes read (write end of pipe closed) - case ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file) - Debug.Assert(readAwaitable._numBytes == 0, $"Expected 0 bytes read, got {readAwaitable._numBytes}"); - break; - case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled - throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); - default: // error - throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode, _path); - } - - // Successful operation. If we got zero bytes, we're done: exit the read/write loop. - int numBytesRead = (int)readAwaitable._numBytes; - if (numBytesRead == 0) - { - break; - } - - // Otherwise, update the read position for next time accordingly. - if (canSeek) - { - readAwaitable._position += numBytesRead; - } - } - finally - { - // Free the resources for this read operation - unsafe - { - NativeOverlapped* overlapped; - lock (readAwaitable.CancellationLock) // just an Exchange, but we need this to be synchronized with cancellation, so using the same lock - { - overlapped = readAwaitable._nativeOverlapped; - readAwaitable._nativeOverlapped = null; - } - if (overlapped != null) - { - _fileHandle.ThreadPoolBinding!.FreeNativeOverlapped(overlapped); - } - } - } - - // Write out the read data. - await destination.WriteAsync(new ReadOnlyMemory(copyBuffer, 0, (int)readAwaitable._numBytes), cancellationToken).ConfigureAwait(false); - } + await FileStreamHelpers + .AsyncModeCopyToAsync(_fileHandle, _path, canSeek, _filePosition, destination, bufferSize, cancellationToken) + .ConfigureAwait(false); } finally { - // Cleanup from the whole copy operation - cancellationReg.Dispose(); - awaitableOverlapped.Dispose(); - - ArrayPool.Shared.Return(copyBuffer); - // Make sure the stream's current position reflects where we ended up if (!_fileHandle.IsClosed && CanSeek) { @@ -1447,112 +1167,8 @@ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, Canc } } - /// Used by CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead. - private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion - { - /// Sentinel object used to indicate that the I/O operation has completed before being awaited. - private static readonly Action s_sentinel = () => { }; - /// Cached delegate to IOCallback. - internal static readonly IOCompletionCallback s_callback = IOCallback; - - /// The FileStream that owns this instance. - internal readonly LegacyFileStreamStrategy _fileStream; - - /// Tracked position representing the next location from which to read. - internal long _position; - /// The current native overlapped pointer. This changes for each operation. - internal NativeOverlapped* _nativeOverlapped; - /// - /// null if the operation is still in progress, - /// s_sentinel if the I/O operation completed before the await, - /// s_callback if it completed after the await yielded. - /// - internal Action? _continuation; - /// Last error code from completed operation. - internal uint _errorCode; - /// Last number of read bytes from completed operation. - internal uint _numBytes; - - /// Lock object used to protect cancellation-related access to _nativeOverlapped. - internal object CancellationLock => this; - - /// Initialize the awaitable. - internal AsyncCopyToAwaitable(LegacyFileStreamStrategy fileStream) - { - _fileStream = fileStream; - } + internal override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, _path, position, length); - /// Reset state to prepare for the next read operation. - internal void ResetForNextOperation() - { - Debug.Assert(_position >= 0, $"Expected non-negative position, got {_position}"); - _continuation = null; - _errorCode = 0; - _numBytes = 0; - } - - /// Overlapped callback: store the results, then invoke the continuation delegate. - internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP) - { - var awaitable = (AsyncCopyToAwaitable?)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP); - Debug.Assert(awaitable != null); - - Debug.Assert(!ReferenceEquals(awaitable._continuation, s_sentinel), "Sentinel must not have already been set as the continuation"); - awaitable._errorCode = errorCode; - awaitable._numBytes = numBytes; - - (awaitable._continuation ?? Interlocked.CompareExchange(ref awaitable._continuation, s_sentinel, null))?.Invoke(); - } - - /// - /// Called when it's known that the I/O callback for an operation will not be invoked but we'll - /// still be awaiting the awaitable. - /// - internal void MarkCompleted() - { - Debug.Assert(_continuation == null, "Expected null continuation"); - _continuation = s_sentinel; - } - - public AsyncCopyToAwaitable GetAwaiter() => this; - public bool IsCompleted => ReferenceEquals(_continuation, s_sentinel); - public void GetResult() { } - public void OnCompleted(Action continuation) => UnsafeOnCompleted(continuation); - public void UnsafeOnCompleted(Action continuation) - { - if (ReferenceEquals(_continuation, s_sentinel) || - Interlocked.CompareExchange(ref _continuation, continuation, null) != null) - { - Debug.Assert(ReferenceEquals(_continuation, s_sentinel), $"Expected continuation set to s_sentinel, got ${_continuation}"); - Task.Run(continuation); - } - } - } - - internal override void Lock(long position, long length) - { - int positionLow = unchecked((int)(position)); - int positionHigh = unchecked((int)(position >> 32)); - int lengthLow = unchecked((int)(length)); - int lengthHigh = unchecked((int)(length >> 32)); - - if (!Interop.Kernel32.LockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh)) - { - throw Win32Marshal.GetExceptionForLastWin32Error(_path); - } - } - - internal override void Unlock(long position, long length) - { - int positionLow = unchecked((int)(position)); - int positionHigh = unchecked((int)(position >> 32)); - int lengthLow = unchecked((int)(length)); - int lengthHigh = unchecked((int)(length >> 32)); - - if (!Interop.Kernel32.UnlockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh)) - { - throw Win32Marshal.GetExceptionForLastWin32Error(_path); - } - } + internal override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, _path, position, length); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs similarity index 92% rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs index 01e5db4f0827c..03ca3533e717b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/Net5CompatFileStreamStrategy.cs @@ -7,10 +7,10 @@ using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -namespace System.IO +namespace System.IO.Strategies { - // This type is partial so we can avoid code duplication between Windows and Unix Legacy implementations - internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy + // This type is partial so we can avoid code duplication between Windows and Unix Net5Compat implementations + internal sealed partial class Net5CompatFileStreamStrategy : FileStreamStrategy { private byte[]? _buffer; private readonly int _bufferLength; @@ -61,7 +61,7 @@ internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy /// Whether the file stream's handle has been exposed. private bool _exposedHandle; - internal LegacyFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) : base(fileStream) + internal Net5CompatFileStreamStrategy(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { _exposedHandle = true; _bufferLength = bufferSize; @@ -78,7 +78,7 @@ internal LegacyFileStreamStrategy(FileStream fileStream, SafeFileHandle handle, _fileHandle = handle; } - internal LegacyFileStreamStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) : base(fileStream) + internal Net5CompatFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) { string fullPath = Path.GetFullPath(path); @@ -105,12 +105,7 @@ internal LegacyFileStreamStrategy(FileStream fileStream, string path, FileMode m } } - ~LegacyFileStreamStrategy() - { - // it looks like having this finalizer is mandatory, - // as we can not guarantee that the Strategy won't be null in FileStream finalizer - Dispose(false); - } + ~Net5CompatFileStreamStrategy() => Dispose(false); // mandatory to Flush the write buffer internal override void DisposeInternal(bool disposing) => Dispose(disposing); @@ -149,7 +144,7 @@ public override int Read(Span buffer) { if (_fileHandle.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } return ReadSpan(buffer); @@ -229,7 +224,7 @@ public override void Write(ReadOnlySpan buffer) { if (_fileHandle.IsClosed) { - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); } WriteSpan(buffer); @@ -272,8 +267,7 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo return WriteAsyncInternal(buffer, cancellationToken); } - // this method might call Derived type implenentation of Flush(flushToDisk) - public override void Flush() => _fileStream.Flush(); + public override void Flush() => Flush(flushToDisk: false); internal override void Flush(bool flushToDisk) { @@ -351,9 +345,9 @@ private void AssertBufferInvariants() private void PrepareForReading() { if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); if (_readLength == 0 && !CanRead) - throw Error.GetReadNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); AssertBufferInvariants(); } @@ -383,22 +377,6 @@ public override long Position internal override bool IsClosed => _fileHandle.IsClosed; - private static bool IsIoRelatedException(Exception e) => - // These all derive from IOException - // DirectoryNotFoundException - // DriveNotFoundException - // EndOfStreamException - // FileLoadException - // FileNotFoundException - // PathTooLongException - // PipeException - e is IOException || - // Note that SecurityException is only thrown on runtimes that support CAS - // e is SecurityException || - e is UnauthorizedAccessException || - e is NotSupportedException || - (e is ArgumentException && !(e is ArgumentNullException)); - /// /// Gets the array used for buffering reading and writing. /// If the array hasn't been allocated, this will lazily allocate it. @@ -502,14 +480,14 @@ public override void WriteByte(byte value) private void PrepareForWriting() { if (_fileHandle.IsClosed) - throw Error.GetFileNotOpen(); + ThrowHelper.ThrowObjectDisposedException_FileClosed(); // Make sure we're good to write. We only need to do this if there's nothing already // in our write buffer, since if there is something in the buffer, we've already done // this checking and flushing. if (_writePos == 0) { - if (!CanWrite) throw Error.GetWriteNotSupported(); + if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); FlushReadBuffer(); Debug.Assert(_bufferLength > 0, "_bufferSize > 0"); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs new file mode 100644 index 0000000000000..3639b4b5fb4da --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs @@ -0,0 +1,175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + internal sealed class SyncWindowsFileStreamStrategy : WindowsFileStreamStrategy + { + internal SyncWindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) : base(handle, access, share) + { + } + + internal SyncWindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + : base(path, mode, access, share, options) + { + } + + internal override bool IsAsync => false; + + protected override void OnInitFromHandle(SafeFileHandle handle) + { + // As we can accurately check the handle type when we have access to NtQueryInformationFile we don't need to skip for + // any particular file handle type. + + // If the handle was passed in without an explicit async setting, we already looked it up in GetDefaultIsAsync + if (!handle.IsAsync.HasValue) + return; + + // If we can't check the handle, just assume it is ok. + if (!(FileStreamHelpers.IsHandleSynchronous(handle, ignoreInvalid: false) ?? true)) + ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle)); + } + + public override int Read(byte[] buffer, int offset, int count) => ReadSpan(new Span(buffer, offset, count)); + + public override int Read(Span buffer) => ReadSpan(buffer); + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Read is invoked asynchronously. But we can do so using the base Stream's internal helper + // that bypasses delegating to BeginRead, since we already know this is FileStream rather + // than something derived from it and what our BeginRead implementation is going to do. + return (Task)BeginReadInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Read is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's + // internal helper that bypasses delegating to BeginRead, since we already know this is FileStream + // rather than something derived from it and what our BeginRead implementation is going to do. + return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? + new ValueTask((Task)BeginReadInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + base.ReadAsync(buffer, cancellationToken); + } + + public override void Write(byte[] buffer, int offset, int count) + => WriteSpan(new ReadOnlySpan(buffer, offset, count)); + + public override void Write(ReadOnlySpan buffer) + { + if (_fileHandle.IsClosed) + { + ThrowHelper.ThrowObjectDisposedException_FileClosed(); + } + + WriteSpan(buffer); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Write is invoked asynchronously. But we can do so using the base Stream's internal helper + // that bypasses delegating to BeginWrite, since we already know this is FileStream rather + // than something derived from it and what our BeginWrite implementation is going to do. + return (Task)BeginWriteInternal(buffer, offset, count, null, null, serializeAsynchronously: true, apm: false); + } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + // If we weren't opened for asynchronous I/O, we still call to the base implementation so that + // Write is invoked asynchronously. But if we have a byte[], we can do so using the base Stream's + // internal helper that bypasses delegating to BeginWrite, since we already know this is FileStream + // rather than something derived from it and what our BeginWrite implementation is going to do. + return MemoryMarshal.TryGetArray(buffer, out ArraySegment segment) ? + new ValueTask((Task)BeginWriteInternal(segment.Array!, segment.Offset, segment.Count, null, null, serializeAsynchronously: true, apm: false)) : + base.WriteAsync(buffer, cancellationToken); + } + + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; // no buffering = nothing to flush + + private unsafe int ReadSpan(Span destination) + { + if (!CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + + NativeOverlapped nativeOverlapped = GetNativeOverlappedForCurrentPosition(); + int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination, true, &nativeOverlapped, out int errorCode); + + if (r == -1) + { + // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe. + if (errorCode == ERROR_BROKEN_PIPE) + { + r = 0; + } + else + { + if (errorCode == ERROR_INVALID_PARAMETER) + ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle)); + + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + } + Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken."); + _filePosition += r; + + return r; + } + + private unsafe void WriteSpan(ReadOnlySpan source) + { + if (!CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } + + Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed"); + + NativeOverlapped nativeOverlapped = GetNativeOverlappedForCurrentPosition(); + int r = FileStreamHelpers.WriteFileNative(_fileHandle, source, true, &nativeOverlapped, out int errorCode); + + if (r == -1) + { + // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing. + if (errorCode == ERROR_NO_DATA) + { + r = 0; + } + else + { + // ERROR_INVALID_PARAMETER may be returned for writes + // where the position is too large or for synchronous writes + // to a handle opened asynchronously. + if (errorCode == ERROR_INVALID_PARAMETER) + throw new IOException(SR.IO_FileTooLongOrHandleNotSync); + throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path); + } + } + Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken."); + _filePosition += r; + UpdateLengthOnChangePosition(); + } + + private NativeOverlapped GetNativeOverlappedForCurrentPosition() + { + NativeOverlapped nativeOverlapped = default; + // For pipes the offsets are ignored by the OS + nativeOverlapped.OffsetLow = unchecked((int)_filePosition); + nativeOverlapped.OffsetHigh = (int)(_filePosition >> 32); + + return nativeOverlapped; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs new file mode 100644 index 0000000000000..d42494c19086d --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace System.IO.Strategies +{ + // this type serves some basic functionality that is common for Async and Sync Windows File Stream Strategies + internal abstract class WindowsFileStreamStrategy : FileStreamStrategy + { + // Error codes (not HRESULTS), from winerror.h + internal const int ERROR_BROKEN_PIPE = 109; + internal const int ERROR_NO_DATA = 232; + protected const int ERROR_HANDLE_EOF = 38; + protected const int ERROR_INVALID_PARAMETER = 87; + protected const int ERROR_IO_PENDING = 997; + + protected readonly SafeFileHandle _fileHandle; // only ever null if ctor throws + protected readonly string? _path; // The path to the opened file. + private readonly FileAccess _access; // What file was opened for. + private readonly FileShare _share; + private readonly bool _canSeek; // Whether can seek (file) or not (pipe). + private readonly bool _isPipe; // Whether to disable async buffering code. + + protected long _filePosition; + private long _appendStart; // When appending, prevent overwriting file. + private long _length = -1; // When the file is locked for writes (_share <= FileShare.Read) cache file length in-memory, negative means that hasn't been fetched. + private bool _exposedHandle; // created from handle, or SafeFileHandle was used and the handle got exposed + + internal WindowsFileStreamStrategy(SafeFileHandle handle, FileAccess access, FileShare share) + { + InitFromHandle(handle, access, out _canSeek, out _isPipe); + + // Note: Cleaner to set the following fields in ValidateAndInitFromHandle, + // but we can't as they're readonly. + _access = access; + _share = share; + _exposedHandle = true; + + // As the handle was passed in, we must set the handle field at the very end to + // avoid the finalizer closing the handle when we throw errors. + _fileHandle = handle; + } + + internal WindowsFileStreamStrategy(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options) + { + string fullPath = Path.GetFullPath(path); + + _path = fullPath; + _access = access; + _share = share; + + _fileHandle = FileStreamHelpers.OpenHandle(fullPath, mode, access, share, options); + + try + { + _canSeek = true; + + Init(mode, path); + } + catch + { + // If anything goes wrong while setting up the stream, make sure we deterministically dispose + // of the opened handle. + _fileHandle.Dispose(); + _fileHandle = null!; + throw; + } + } + + public sealed override bool CanSeek => _canSeek; + + public sealed override bool CanRead => !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; + + public sealed override bool CanWrite => !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; + + // When the file is locked for writes we can cache file length in memory + // and avoid subsequent native calls which are expensive. + public unsafe sealed override long Length + { + get + { + if (_share > FileShare.Read || _exposedHandle) + { + return FileStreamHelpers.GetFileLength(_fileHandle, _path); + } + + if (_length < 0) + { + _length = FileStreamHelpers.GetFileLength(_fileHandle, _path); + } + + return _length; + } + } + + protected void UpdateLengthOnChangePosition() + { + // Do not update the cached length if the file is not locked + // or if the length hasn't been fetched. + if (_share > FileShare.Read || _length < 0 || _exposedHandle) + { + Debug.Assert(_length < 0); + return; + } + + if (_filePosition > _length) + { + _length = _filePosition; + } + } + + /// Gets or sets the position within the current stream + public override long Position + { + get => _filePosition; + set => _filePosition = value; + } + + internal sealed override string Name => _path ?? SR.IO_UnknownFileName; + + internal sealed override bool IsClosed => _fileHandle.IsClosed; + + internal sealed override bool IsPipe => _isPipe; + // Flushing is the responsibility of BufferedFileStreamStrategy + internal sealed override SafeFileHandle SafeFileHandle + { + get + { + if (CanSeek) + { + // Update the file offset before exposing it since it's possible that + // in memory position is out-of-sync with the actual file position. + FileStreamHelpers.Seek(_fileHandle, _path, _filePosition, SeekOrigin.Begin); + } + + _exposedHandle = true; + _length = -1; // invalidate cached length + + return _fileHandle; + } + } + + // ReadByte and WriteByte methods are used only when the user has disabled buffering on purpose + // their performance is going to be horrible + // TODO: should we consider adding a new event provider and log an event so it can be detected? + public override int ReadByte() + { + Span buffer = stackalloc byte[1]; + int bytesRead = Read(buffer); + return bytesRead == 1 ? buffer[0] : -1; + } + + public override void WriteByte(byte value) + { + Span buffer = stackalloc byte[1]; + buffer[0] = value; + Write(buffer); + } + + // this method just disposes everything (no buffer, no need to flush) + public override ValueTask DisposeAsync() + { + if (_fileHandle != null && !_fileHandle.IsClosed) + { + _fileHandle.ThreadPoolBinding?.Dispose(); + _fileHandle.Dispose(); + } + + return ValueTask.CompletedTask; + } + + internal sealed override void DisposeInternal(bool disposing) => Dispose(disposing); + + // this method just disposes everything (no buffer, no need to flush) + protected override void Dispose(bool disposing) + { + if (_fileHandle != null && !_fileHandle.IsClosed) + { + _fileHandle.ThreadPoolBinding?.Dispose(); + _fileHandle.Dispose(); + } + } + + public sealed override void Flush() => Flush(flushToDisk: false); // we have nothing to flush as there is no buffer here + + internal sealed override void Flush(bool flushToDisk) + { + if (flushToDisk && CanWrite) + { + FileStreamHelpers.FlushToDisk(_fileHandle, _path); + } + } + + public sealed override long Seek(long offset, SeekOrigin origin) + { + if (origin < SeekOrigin.Begin || origin > SeekOrigin.End) + throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin)); + if (_fileHandle.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed(); + if (!CanSeek) ThrowHelper.ThrowNotSupportedException_UnseekableStream(); + + long oldPos = _filePosition; + long pos = origin switch + { + SeekOrigin.Begin => offset, + SeekOrigin.End => Length + offset, + _ => _filePosition + offset // SeekOrigin.Current + }; + + if (pos >= 0) + { + _filePosition = pos; + } + else + { + // keep throwing the same exception we did when seek was causing actual offset change + throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_INVALID_PARAMETER); + } + + // Prevent users from overwriting data in a file that was opened in append mode. + if (_appendStart != -1 && pos < _appendStart) + { + _filePosition = oldPos; + throw new IOException(SR.IO_SeekAppendOverwrite); + } + + return pos; + } + + internal sealed override void Lock(long position, long length) => FileStreamHelpers.Lock(_fileHandle, _path, position, length); + + internal sealed override void Unlock(long position, long length) => FileStreamHelpers.Unlock(_fileHandle, _path, position, length); + + protected abstract void OnInitFromHandle(SafeFileHandle handle); + + protected virtual void OnInit() { } + + private void Init(FileMode mode, string originalPath) + { + FileStreamHelpers.ValidateFileTypeForNonExtendedPaths(_fileHandle, originalPath); + + OnInit(); + + // For Append mode... + if (mode == FileMode.Append) + { + _appendStart = _filePosition = Length; + } + else + { + _appendStart = -1; + } + } + + private void InitFromHandle(SafeFileHandle handle, FileAccess access, out bool canSeek, out bool isPipe) + { +#if DEBUG + bool hadBinding = handle.ThreadPoolBinding != null; + + try + { +#endif + InitFromHandleImpl(handle, out canSeek, out isPipe); +#if DEBUG + } + catch + { + Debug.Assert(hadBinding || handle.ThreadPoolBinding == null, "We should never error out with a ThreadPoolBinding we've added"); + throw; + } +#endif + } + + private void InitFromHandleImpl(SafeFileHandle handle, out bool canSeek, out bool isPipe) + { + FileStreamHelpers.GetFileTypeSpecificInformation(handle, out canSeek, out isPipe); + + OnInitFromHandle(handle); + + if (_canSeek) + { + // given strategy was created out of existing handle, so we have to perform + // a syscall to get the current handle offset + _filePosition = FileStreamHelpers.Seek(handle, _path, 0, SeekOrigin.Current); + } + else + { + _filePosition = 0; + } + } + + public sealed override void SetLength(long value) + { + if (_appendStart != -1 && value < _appendStart) + throw new IOException(SR.IO_SetLengthAppendTruncate); + + SetLengthCore(value); + } + + protected unsafe void SetLengthCore(long value) + { + Debug.Assert(value >= 0, "value >= 0"); + + FileStreamHelpers.SetFileLength(_fileHandle, _path, value); + _length = value; + + if (_filePosition > value) + { + _filePosition = value; + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs index 6a364614ef99a..71ad1dbeddc48 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs @@ -55,9 +55,12 @@ public virtual void CopyTo(Stream destination, int bufferSize) ValidateCopyToArguments(destination, bufferSize); if (!CanRead) { - throw CanWrite ? (Exception) - new NotSupportedException(SR.NotSupported_UnreadableStream) : - new ObjectDisposedException(GetType().Name, SR.ObjectDisposed_StreamClosed); + if (CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + ThrowHelper.ThrowObjectDisposedException_StreamClosed(GetType().Name); } byte[] buffer = ArrayPool.Shared.Rent(bufferSize); @@ -86,9 +89,12 @@ public virtual Task CopyToAsync(Stream destination, int bufferSize, Cancellation ValidateCopyToArguments(destination, bufferSize); if (!CanRead) { - throw CanWrite ? (Exception) - new NotSupportedException(SR.NotSupported_UnreadableStream) : - new ObjectDisposedException(GetType().Name, SR.ObjectDisposed_StreamClosed); + if (CanWrite) + { + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); + } + + ThrowHelper.ThrowObjectDisposedException_StreamClosed(GetType().Name); } return Core(this, destination, bufferSize, cancellationToken); @@ -202,7 +208,7 @@ internal IAsyncResult BeginReadInternal( ValidateBufferArguments(buffer, offset, count); if (!CanRead) { - throw Error.GetReadNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } // To avoid a race with a stream's position pointer & generating race conditions @@ -359,7 +365,7 @@ internal IAsyncResult BeginWriteInternal( ValidateBufferArguments(buffer, offset, count); if (!CanWrite) { - throw Error.GetWriteNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } // To avoid a race condition with a stream's position pointer & generating conditions @@ -759,9 +765,12 @@ protected static void ValidateCopyToArguments(Stream destination, int bufferSize if (!destination.CanWrite) { - throw destination.CanRead ? (Exception) - new NotSupportedException(SR.NotSupported_UnwritableStream) : - new ObjectDisposedException(destination.GetType().Name, SR.ObjectDisposed_StreamClosed); + if (destination.CanRead) + { + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); + } + + ThrowHelper.ThrowObjectDisposedException_StreamClosed(destination.GetType().Name); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs index 848da5bc1e62b..2e2821a2b17d6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @@ -138,7 +138,7 @@ public StreamReader(Stream stream, Encoding? encoding = null, bool detectEncodin { if (stream == null) { - throw new ArgumentNullException(nameof(stream)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stream); } if (encoding == null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index 7b1004b0800c9..ce84fbfe85b7e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -95,7 +95,7 @@ public StreamWriter(Stream stream, Encoding? encoding = null, int bufferSize = - { if (stream == null) { - throw new ArgumentNullException(nameof(stream)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stream); } if (encoding == null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs index 52379002896b2..0e53df433e78c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryAccessor.cs @@ -12,6 +12,7 @@ ** ===========================================================*/ +using System.Diagnostics; using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; @@ -489,24 +490,22 @@ public void Write(long position, decimal value) { EnsureSafeToWrite(position, sizeof(decimal)); + Span bits = stackalloc int[4]; + decimal.TryGetBits(value, bits, out int intsWritten); + Debug.Assert(intsWritten == 4); + unsafe { - int* valuePtr = (int*)(&value); - int flags = *valuePtr; - int hi = *(valuePtr + 1); - int lo = *(valuePtr + 2); - int mid = *(valuePtr + 3); - byte* pointer = null; try { _buffer.AcquirePointer(ref pointer); pointer += (_offset + position); - Unsafe.WriteUnaligned(pointer, lo); - Unsafe.WriteUnaligned(pointer + 4, mid); - Unsafe.WriteUnaligned(pointer + 8, hi); - Unsafe.WriteUnaligned(pointer + 12, flags); + Unsafe.WriteUnaligned(pointer, bits[0]); + Unsafe.WriteUnaligned(pointer + 4, bits[1]); + Unsafe.WriteUnaligned(pointer + 8, bits[2]); + Unsafe.WriteUnaligned(pointer + 12, bits[3]); } finally { diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs index f0d48773eebf5..c116c17e69ad8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStream.cs @@ -215,19 +215,19 @@ protected override void Dispose(bool disposing) private void EnsureNotClosed() { if (!_isOpen) - throw Error.GetStreamIsClosed(); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); } private void EnsureReadable() { if (!CanRead) - throw Error.GetReadNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } private void EnsureWriteable() { if (!CanWrite) - throw Error.GetWriteNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } /// @@ -290,13 +290,13 @@ public override long Position { get { - if (!CanSeek) throw Error.GetStreamIsClosed(); + if (!CanSeek) ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); return Interlocked.Read(ref _position); } set { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum); - if (!CanSeek) throw Error.GetStreamIsClosed(); + if (!CanSeek) ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); Interlocked.Exchange(ref _position, value); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStreamWrapper.cs b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStreamWrapper.cs index 4851eb6098d2e..b1adbb1c022c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStreamWrapper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/UnmanagedMemoryStreamWrapper.cs @@ -148,17 +148,16 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum); if (!CanRead && !CanWrite) - throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(null); if (!destination.CanRead && !destination.CanWrite) - throw new ObjectDisposedException(nameof(destination), SR.ObjectDisposed_StreamClosed); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(nameof(destination)); if (!CanRead) - throw new NotSupportedException(SR.NotSupported_UnreadableStream); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); if (!destination.CanWrite) - throw new NotSupportedException(SR.NotSupported_UnwritableStream); - + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); return _unmanagedStream.CopyToAsync(destination, bufferSize, cancellationToken); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Lazy.cs b/src/libraries/System.Private.CoreLib/src/System/Lazy.cs index b0778f5b71c21..b60daedce2422 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Lazy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Lazy.cs @@ -40,7 +40,7 @@ internal enum LazyState /// - allows for instantiation for ExecutionAndPublication so as to create an object for locking on /// - holds exception information. /// - internal class LazyHelper + internal sealed class LazyHelper { internal static readonly LazyHelper NoneViaConstructor = new LazyHelper(LazyState.NoneViaConstructor); internal static readonly LazyHelper NoneViaFactory = new LazyHelper(LazyState.NoneViaFactory); diff --git a/src/libraries/System.Private.CoreLib/src/System/Marvin.OrdinalIgnoreCase.cs b/src/libraries/System.Private.CoreLib/src/System/Marvin.OrdinalIgnoreCase.cs index c2e47dd17e9be..3b0c9fad76f00 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Marvin.OrdinalIgnoreCase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Marvin.OrdinalIgnoreCase.cs @@ -48,10 +48,25 @@ public static int ComputeHash32OrdinalIgnoreCase(ref char data, int count, uint goto NotAscii; } - // addition is written with -0x80u to allow fall-through to next statement rather than jmp past it - p0 += Utf16Utility.ConvertAllAsciiCharsInUInt32ToUppercase(tempValue) + (0x800000u - 0x80u); + if (BitConverter.IsLittleEndian) + { + // addition is written with -0x80u to allow fall-through to next statement rather than jmp past it + p0 += Utf16Utility.ConvertAllAsciiCharsInUInt32ToUppercase(tempValue) + (0x800000u - 0x80u); + } + else + { + // as above, addition is modified to allow fall-through to next statement rather than jmp past it + p0 += (Utf16Utility.ConvertAllAsciiCharsInUInt32ToUppercase(tempValue) << 16) + 0x8000u - 0x80000000u; + } + } + if (BitConverter.IsLittleEndian) + { + p0 += 0x80u; + } + else + { + p0 += 0x80000000u; } - p0 += 0x80u; Block(ref p0, ref p1); Block(ref p0, ref p1); diff --git a/src/libraries/System.Private.CoreLib/src/System/Memory.cs b/src/libraries/System.Private.CoreLib/src/System/Memory.cs index f170027580762..55d636fe57bf8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Memory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Memory.cs @@ -216,7 +216,7 @@ public override string ToString() { return (_object is string str) ? str.Substring(_index, _length) : Span.ToString(); } - return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length); + return $"System.Memory<{typeof(T).Name}>[{_length}]"; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs index 054b7b0322ec6..826a129be7f85 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -70,6 +71,30 @@ public static class BitOperations [CLSCompliant(false)] public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value != 0; + /// Round the given integral value up to a power of 2. + /// The value. + internal static uint RoundUpToPowerOf2(uint value) + { + // TODO: https://github.com/dotnet/runtime/issues/43135 + // When this is exposed publicly, decide on the behavior for the boundary cases... + // the accelerated and fallback paths differ. + Debug.Assert(value > 0 && value <= (uint.MaxValue / 2) + 1); + + if (Lzcnt.IsSupported || ArmBase.IsSupported || X86Base.IsSupported) + { + return 1u << (32 - LeadingZeroCount(value - 1)); + } + + // Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --value; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + return value + 1; + } + /// /// Count the number of leading zero bits in a mask. /// Similar in behavior to the x86 instruction LZCNT. diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs index 160793595f40b..7bf7d5c6d1feb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs @@ -5,8 +5,6 @@ namespace System.Numerics.Hashing { internal static class HashHelpers { - public static readonly int RandomSeed = new Random().Next(int.MinValue, int.MaxValue); - public static int Combine(int h1, int h2) { // RyuJIT optimizes this to use the ROL instruction diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs index bddf63dd49e21..5e290fbef7c36 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs @@ -652,12 +652,7 @@ public override readonly int GetHashCode() /// Returns a string that represents this matrix. /// The string representation of this matrix. /// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as { {M11:1.1 M12:1.2} {M21:2.1 M22:2.2} {M31:3.1 M32:3.2} }. - public override readonly string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}", - M11, M12, - M21, M22, - M31, M32); - } + public override readonly string ToString() => + $"{{ {{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}} }}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs index 413695ae53b07..a7fcca9d24a5f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs @@ -2256,14 +2256,8 @@ public override readonly int GetHashCode() /// Returns a string that represents this matrix. /// The string representation of this matrix. /// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as { {M11:1.1 M12:1.2 M13:1.3 M14:1.4} {M21:2.1 M22:2.2 M23:2.3 M24:2.4} {M31:3.1 M32:3.2 M33:3.3 M34:3.4} {M41:4.1 M42:4.2 M43:4.3 M44:4.4} }. - public override readonly string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} }}", - M11, M12, M13, M14, - M21, M22, M23, M24, - M31, M32, M33, M34, - M41, M42, M43, M44); - } + public override readonly string ToString() => + $"{{ {{M11:{M11} M12:{M12} M13:{M13} M14:{M14}}} {{M21:{M21} M22:{M22} M23:{M23} M24:{M24}}} {{M31:{M31} M32:{M32} M33:{M33} M34:{M34}}} {{M41:{M41} M42:{M42} M43:{M43} M44:{M44}}} }}"; private struct CanonicalBasis { diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs index fa792eec909fc..aaab1903dc476 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs @@ -321,10 +321,7 @@ public override readonly int GetHashCode() /// Returns the string representation of this plane object. /// A string that represents this object. /// The string representation of a object use the formatting conventions of the current culture to format the numeric values in the returned string. For example, a object whose string representation is formatted by using the conventions of the en-US culture might appear as {Normal:<1.1, 2.2, 3.3> D:4.4}. - public override readonly string ToString() - { - CultureInfo ci = CultureInfo.CurrentCulture; - return string.Format(ci, "{{Normal:{0} D:{1}}}", Normal.ToString(), D.ToString(ci)); - } + public override readonly string ToString() => + $"{{Normal:{Normal.ToString()} D:{D.ToString()}}}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs index 8a8d431c6b8c7..ed863caca43be 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs @@ -635,9 +635,7 @@ public readonly float LengthSquared() /// Returns a string that represents this quaternion. /// The string representation of this quaternion. /// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as {X:1.1 Y:2.2 Z:3.3 W:4.4}. - public override readonly string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "{{X:{0} Y:{1} Z:{2} W:{3}}}", X, Y, Z, W); - } + public override readonly string ToString() => + $"{{X:{X} Y:{Y} Z:{Z} W:{W}}}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs index b20b73ae05a68..ae0e87bded858 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs @@ -4,7 +4,9 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; +using Internal.Runtime.CompilerServices; namespace System.Numerics { @@ -39,6 +41,18 @@ public Vector2(float x, float y) Y = y; } + /// Constructs a vector from the given . The span must contain at least 2 elements. + /// The span of elements to assign to the vector. + public Vector2(ReadOnlySpan values) + { + if (values.Length < 2) + { + Vector.ThrowInsufficientNumberOfElementsException(2); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + /// Returns a vector whose 2 elements are equal to zero. /// A vector whose two elements are equal to zero (that is, it returns the vector (0,0). public static Vector2 Zero @@ -530,6 +544,34 @@ public readonly void CopyTo(float[] array, int index) array[index + 1] = Y; } + /// Copies the vector to the given .The length of the destination span must be at least 2. + /// The destination span which the values are copied into. + /// If number of elements in source vector is greater than those available in destination span. + public readonly void CopyTo(Span destination) + { + if (destination.Length < 2) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); + } + + /// Attempts to copy the vector to the given . The length of the destination span must be at least 2. + /// The destination span which the values are copied into. + /// if the source vector was successfully copied to . if is not large enough to hold the source vector. + public readonly bool TryCopyTo(Span destination) + { + if (destination.Length < 2) + { + return false; + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); + + return true; + } + /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs index bb34093c7133a..85657b1322539 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs @@ -4,7 +4,9 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; +using Internal.Runtime.CompilerServices; namespace System.Numerics { @@ -52,6 +54,18 @@ public Vector3(float x, float y, float z) Z = z; } + /// Constructs a vector from the given . The span must contain at least 3 elements. + /// The span of elements to assign to the vector. + public Vector3(ReadOnlySpan values) + { + if (values.Length < 3) + { + Vector.ThrowInsufficientNumberOfElementsException(3); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + /// Gets a vector whose 3 elements are equal to zero. /// A vector whose three elements are equal to zero (that is, it returns the vector (0,0,0). public static Vector3 Zero @@ -553,6 +567,34 @@ public readonly void CopyTo(float[] array, int index) array[index + 2] = Z; } + /// Copies the vector to the given . The length of the destination span must be at least 3. + /// The destination span which the values are copied into. + /// If number of elements in source vector is greater than those available in destination span. + public readonly void CopyTo(Span destination) + { + if (destination.Length < 3) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); + } + + /// Attempts to copy the vector to the given . The length of the destination span must be at least 3. + /// The destination span which the values are copied into. + /// if the source vector was successfully copied to . if is not large enough to hold the source vector. + public readonly bool TryCopyTo(Span destination) + { + if (destination.Length < 3) + { + return false; + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); + + return true; + } + /// Returns a value that indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// if the current instance and are equal; otherwise, . If is , the method returns . diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs index c376d53f25de7..2c920df2587d5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs @@ -4,7 +4,9 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; +using Internal.Runtime.CompilerServices; namespace System.Numerics { @@ -66,6 +68,18 @@ public Vector4(float x, float y, float z, float w) W = w; } + /// Constructs a vector from the given . The span must contain at least 4 elements. + /// The span of elements to assign to the vector. + public Vector4(ReadOnlySpan values) + { + if (values.Length < 4) + { + Vector.ThrowInsufficientNumberOfElementsException(4); + } + + this = Unsafe.ReadUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(values))); + } + /// Gets a vector whose 4 elements are equal to zero. /// A vector whose four elements are equal to zero (that is, it returns the vector (0,0,0,0). public static Vector4 Zero @@ -640,6 +654,34 @@ public readonly void CopyTo(float[] array, int index) array[index + 3] = W; } + /// Copies the vector to the given . The length of the destination span must be at least 4. + /// The destination span which the values are copied into. + /// If number of elements in source vector is greater than those available in destination span. + public readonly void CopyTo(Span destination) + { + if (destination.Length < 4) + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); + } + + /// Attempts to copy the vector to the given . The length of the destination span must be at least 4. + /// The destination span which the values are copied into. + /// if the source vector was successfully copied to . if is not large enough to hold the source vector. + public readonly bool TryCopyTo(Span destination) + { + if (destination.Length < 4) + { + return false; + } + + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(destination)), this); + + return true; + } + /// Returns a value that indicates whether this instance and another vector are equal. /// The other vector. /// if the two vectors are equal; otherwise, . diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs index 6d8c7f3276541..d9056586a49af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs @@ -409,7 +409,7 @@ public readonly bool TryCopyTo(Span destination) /// The source vector. /// The scalar value. /// The scaled vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Intrinsic] public static Vector operator *(Vector value, T factor) { Vector result = default; @@ -422,11 +422,11 @@ public readonly bool TryCopyTo(Span destination) return result; } - /// Multiplies a vector by the given scalar. /// The scalar value. /// The source vector. /// The scaled vector. + [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector operator *(T factor, Vector value) => value * factor; @@ -450,6 +450,7 @@ public readonly bool TryCopyTo(Span destination) /// Negates a given vector. /// The source vector. /// The negated vector. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector operator -(Vector value) => Zero - value; /// Returns a new vector by performing a bitwise-and operation on each of the elements in the given vectors. diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs b/src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs similarity index 97% rename from src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs rename to src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs index eea6e4c9f3ff9..250c84cb971af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.LegacyImpl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.Net5CompatImpl.cs @@ -16,7 +16,7 @@ public partial class Random /// of Knuth's subtractive random number generator algorithm. See https://github.com/dotnet/runtime/issues/23198 /// for a discussion of some of the modifications / discrepancies. /// - private sealed class LegacyImpl : ImplBase + private sealed class Net5CompatImpl : ImplBase { /// Thread-static instance used to seed any legacy implementations created with the default ctor. [ThreadStatic] @@ -29,11 +29,11 @@ private sealed class LegacyImpl : ImplBase private int _inext; private int _inextp; - public LegacyImpl(Random parent) : this(parent, (t_seedGenerator ??= new()).Next()) + public Net5CompatImpl(Random parent) : this(parent, (t_seedGenerator ??= new()).Next()) { } - public LegacyImpl(Random parent, int Seed) + public Net5CompatImpl(Random parent, int Seed) { _parent = parent; diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index daaa02e93a533..64b9eb97c0971 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Runtime.CompilerServices; + namespace System { /// @@ -24,7 +27,7 @@ public Random() => // With no seed specified, if this is the base type, we can implement this however we like. // If it's a derived type, for compat we respect the previous implementation, so that overrides // are called as they were previously. - _impl = GetType() == typeof(Random) ? new XoshiroImpl() : new LegacyImpl(this); + _impl = GetType() == typeof(Random) ? new XoshiroImpl() : new Net5CompatImpl(this); /// Initializes a new instance of the Random class, using the specified seed value. /// @@ -34,7 +37,18 @@ public Random() => public Random(int Seed) => // With a custom seed, for compat we respect the previous implementation so that the same sequence // previously output continues to be output. - _impl = new LegacyImpl(this, Seed); + _impl = new Net5CompatImpl(this, Seed); + + /// Constructor used by . + /// Must be true. + protected private Random(bool isThreadSafeRandom) + { + Debug.Assert(isThreadSafeRandom); + _impl = null!; // base implementation isn't used at all + } + + /// Provides a thread-safe instance that may be used concurrently from any thread. + public static Random Shared { get; } = new ThreadSafeRandom(); /// Returns a non-negative random integer. /// A 32-bit signed integer that is greater than or equal to 0 and less than . @@ -148,5 +162,94 @@ private static void ThrowMaxValueMustBeNonNegative() => private static void ThrowMinMaxValueSwapped() => throw new ArgumentOutOfRangeException("minValue", SR.Format(SR.Argument_MinMaxValue, "minValue", "maxValue")); + + /// Random implementation that delegates all calls to a ThreadStatic Impl instance. + private sealed class ThreadSafeRandom : Random + { + // We need Random.Shared to return an instance that is thread-safe, as it may be used from multiple threads. + // It's also possible that one thread could retrieve Shared and pass it to another thread, so Shared can't + // just access a ThreadStatic to return a Random instance stored there. As such, we need the instance + // returned from Shared itself to be thread-safe, which can be accomplished either by locking around all + // accesses or by itself accessing a ThreadStatic on every access: we've chosen the latter, as it is more + // scalable. With that, we have two basic approaches: + // 1. the instance returned can be a base Random instance constructed with an _impl that is a ThreadSafeImpl. + // 2. the instance returned can be a special Random-derived instance, where _impl is ignored and the derived + // type overrides all methods on the base. + // (1) is cleaner, as (2) requires duplicating a bit more code, but (2) enables all virtual dispatch to be + // devirtualized and potentially inlined, so that Random.Shared.NextXx(...) ends up being faster. + // This implements (2). + + [ThreadStatic] + private static XoshiroImpl? t_random; + + public ThreadSafeRandom() : base(isThreadSafeRandom: true) { } + + private static XoshiroImpl LocalRandom => t_random ?? Create(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static XoshiroImpl Create() => t_random = new(); + + public override int Next() => LocalRandom.Next(); + + public override int Next(int maxValue) + { + if (maxValue < 0) + { + ThrowMaxValueMustBeNonNegative(); + } + + return LocalRandom.Next(maxValue); + } + + public override int Next(int minValue, int maxValue) + { + if (minValue > maxValue) + { + ThrowMinMaxValueSwapped(); + } + + return LocalRandom.Next(minValue, maxValue); + } + + public override long NextInt64() => LocalRandom.NextInt64(); + + public override long NextInt64(long maxValue) + { + if (maxValue < 0) + { + ThrowMaxValueMustBeNonNegative(); + } + + return LocalRandom.NextInt64(maxValue); + } + + public override long NextInt64(long minValue, long maxValue) + { + if (minValue > maxValue) + { + ThrowMinMaxValueSwapped(); + } + + return LocalRandom.NextInt64(minValue, maxValue); + } + + public override float NextSingle() => LocalRandom.NextSingle(); + + public override double NextDouble() => LocalRandom.NextDouble(); + + public override void NextBytes(byte[] buffer) + { + if (buffer is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.buffer); + } + + LocalRandom.NextBytes(buffer); + } + + public override void NextBytes(Span buffer) => LocalRandom.NextBytes(buffer); + + protected override double Sample() => throw new NotSupportedException(); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs index 124377cbea14a..fef6862586e48 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs @@ -145,7 +145,7 @@ public override string ToString() { return (_object is string str) ? str.Substring(_index, _length) : Span.ToString(); } - return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length); + return $"System.ReadOnlyMemory<{typeof(T).Name}>[{_length}]"; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs index 38fea0090ca9a..bd2a26583d731 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlySpan.cs @@ -319,7 +319,7 @@ public override string ToString() { return new string(new ReadOnlySpan(ref Unsafe.As(ref _pointer.Value), _length)); } - return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length); + return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]"; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs index bd6dd45fb0b45..9f627596efed9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs @@ -260,7 +260,7 @@ public static Assembly LoadFile(string path) if (s_loadfile.TryGetValue(normalizedPath, out result)) return result; - AssemblyLoadContext alc = new IndividualAssemblyLoadContext(string.Format("Assembly.LoadFile({0})", normalizedPath)); + AssemblyLoadContext alc = new IndividualAssemblyLoadContext($"Assembly.LoadFile({normalizedPath})"); result = alc.LoadFromAssemblyPath(normalizedPath); s_loadfile.Add(normalizedPath, result); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs index fafdb5b1d4988..dd31ae3559976 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeNamedArgument.cs @@ -45,7 +45,7 @@ public override string ToString() if (m_memberInfo == null) return base.ToString()!; - return string.Format("{0} = {1}", MemberInfo.Name, TypedValue.ToString(ArgumentType != typeof(object))); + return $"{MemberInfo.Name} = {TypedValue.ToString(ArgumentType != typeof(object))}"; } public override int GetHashCode() diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs index e848262a6e28d..46fb5dd5c68a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/CustomAttributeTypedArgument.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Text; namespace System.Reflection { @@ -43,33 +44,47 @@ internal string ToString(bool typed) return base.ToString()!; if (ArgumentType.IsEnum) - return string.Format(typed ? "{0}" : "({1}){0}", Value, ArgumentType.FullName); - else if (Value == null) - return string.Format(typed ? "null" : "({0})null", ArgumentType.Name); - else if (ArgumentType == typeof(string)) - return string.Format("\"{0}\"", Value); - else if (ArgumentType == typeof(char)) - return string.Format("'{0}'", Value); - else if (ArgumentType == typeof(Type)) - return string.Format("typeof({0})", ((Type)Value!).FullName); - else if (ArgumentType.IsArray) + return typed ? $"{Value}" : $"({ArgumentType.FullName}){Value}"; + + if (Value == null) + return typed ? "null" : $"({ArgumentType.Name})null"; + + if (ArgumentType == typeof(string)) + return $"\"{Value}\""; + + if (ArgumentType == typeof(char)) + return $"'{Value}'"; + + if (ArgumentType == typeof(Type)) + return $"typeof({((Type)Value!).FullName})"; + + if (ArgumentType.IsArray) { IList array = (IList)Value!; - Type elementType = ArgumentType.GetElementType()!; - string result = string.Format("new {0}[{1}] {{ ", elementType.IsEnum ? elementType.FullName : elementType.Name, array.Count); + + var result = new ValueStringBuilder(stackalloc char[256]); + result.Append("new "); + result.Append(elementType.IsEnum ? elementType.FullName : elementType.Name); + result.Append('['); + result.Append(array.Count.ToString()); + result.Append(']'); for (int i = 0; i < array.Count; i++) { - result += string.Format(i == 0 ? "{0}" : ", {0}", array[i].ToString(elementType != typeof(object))); + if (i != 0) + { + result.Append(", "); + } + result.Append(array[i].ToString(elementType != typeof(object))); } - result += " }"; + result.Append(" }"); - return result; + return result.ToString(); } - return string.Format(typed ? "{0}" : "({1}){0}", Value, ArgumentType.Name); + return typed ? $"{Value}" : $"({ArgumentType.Name}){Value}"; } public override int GetHashCode() => base.GetHashCode(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EmptyCAHolder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EmptyCAHolder.cs index 1d30d3c2ebeb8..0a8d72ca21c6f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EmptyCAHolder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/EmptyCAHolder.cs @@ -3,7 +3,7 @@ namespace System.Reflection.Emit { - internal class EmptyCAHolder : ICustomAttributeProvider + internal sealed class EmptyCAHolder : ICustomAttributeProvider { internal EmptyCAHolder() { } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeNameBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeNameBuilder.cs index 569bd36754d39..b402b32f7f1ab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeNameBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/TypeNameBuilder.cs @@ -13,7 +13,7 @@ namespace System.Reflection.Emit { - internal class TypeNameBuilder + internal sealed class TypeNameBuilder { private StringBuilder _str = new StringBuilder(); private int _instNesting; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ExceptionHandlingClause.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ExceptionHandlingClause.cs index 33d2e30b828c5..7c218a021464e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ExceptionHandlingClause.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ExceptionHandlingClause.cs @@ -16,11 +16,7 @@ protected ExceptionHandlingClause() { } public virtual int FilterOffset => throw new InvalidOperationException(SR.Arg_EHClauseNotFilter); public virtual Type? CatchType => null; - public override string ToString() - { - return string.Format(CultureInfo.CurrentUICulture, - "Flags={0}, TryOffset={1}, TryLength={2}, HandlerOffset={3}, HandlerLength={4}, CatchType={5}", - Flags, TryOffset, TryLength, HandlerOffset, HandlerLength, CatchType); - } + public override string ToString() => + $"Flags={Flags}, TryOffset={TryOffset}, TryLength={TryLength}, HandlerOffset={HandlerOffset}, HandlerLength={HandlerLength}, CatchType={CatchType}"; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs index af70d9fb06b68..2bbdd3db332b0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/FileBasedResourceGroveler.cs @@ -19,7 +19,7 @@ namespace System.Resources { - internal class FileBasedResourceGroveler : IResourceGroveler + internal sealed class FileBasedResourceGroveler : IResourceGroveler { private readonly ResourceManager.ResourceManagerMediator _mediator; diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs index b24d6dc29223c..18b714a42ce38 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ManifestBasedResourceGroveler.cs @@ -31,7 +31,7 @@ namespace System.Resources // belonging to that type may not be initialized. FrameworkEventSource.Log // is one such example. // - internal partial class ManifestBasedResourceGroveler : IResourceGroveler + internal sealed partial class ManifestBasedResourceGroveler : IResourceGroveler { private readonly ResourceManager.ResourceManagerMediator _mediator; diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceFallbackManager.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceFallbackManager.cs index 66434a24e1cdb..84b3a70d6aac2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceFallbackManager.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceFallbackManager.cs @@ -19,7 +19,7 @@ namespace System.Resources { - internal class ResourceFallbackManager : IEnumerable + internal sealed class ResourceFallbackManager : IEnumerable { private readonly CultureInfo m_startingCulture; private readonly CultureInfo? m_neutralResourcesCulture; diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.cs index 16c585d4fe634..d7b8bae667911 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceManager.cs @@ -92,7 +92,7 @@ namespace System.Resources public partial class ResourceManager { - internal class CultureNameResourceSetPair + internal sealed class CultureNameResourceSetPair { public string? lastCultureName; public ResourceSet? lastResourceSet; @@ -743,7 +743,7 @@ internal static bool IsDefaultType(string asmTypeName, return ums; } - internal class ResourceManagerMediator + internal sealed class ResourceManagerMediator { private readonly ResourceManager _rm; diff --git a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs index 270df3a8b9cb1..78ead82418ca1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Resources/ResourceReader.cs @@ -383,7 +383,19 @@ private unsafe string AllocateStringForNameIndex(int index, out int dataOffset) string? s = null; char* charPtr = (char*)_ums.PositionPointer; - s = new string(charPtr, 0, byteLen / 2); + if (BitConverter.IsLittleEndian) + { + s = new string(charPtr, 0, byteLen / 2); + } + else + { + char[] arr = new char[byteLen / 2]; + for (int i = 0; i < arr.Length; i++) + { + arr[i] = (char)BinaryPrimitives.ReverseEndianness((short)charPtr[i]); + } + s = new string(arr); + } _ums.Position += byteLen; dataOffset = _store.ReadInt32(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs index 8c65d2f04173d..026e43c24a45f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncMethodBuilderAttribute.cs @@ -5,9 +5,10 @@ namespace System.Runtime.CompilerServices { /// /// Indicates the type of the async method builder that should be used by a language compiler to - /// build the attributed type when used as the return type of an async method. + /// build the attributed async method or to build the attributed type when used as the return type + /// of an async method. /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public sealed class AsyncMethodBuilderAttribute : Attribute { /// Initializes the . diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs index d725b51ca051b..bab1cdf3376e2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilder.cs @@ -3,9 +3,6 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; -using Internal.Runtime.CompilerServices; - -using StateMachineBox = System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.StateMachineBox; namespace System.Runtime.CompilerServices { @@ -13,23 +10,14 @@ namespace System.Runtime.CompilerServices [StructLayout(LayoutKind.Auto)] public struct AsyncValueTaskMethodBuilder { - /// true if we should use reusable boxes for async completions of ValueTask methods; false if we should use tasks. - /// - /// We rely on tiered compilation turning this into a const and doing dead code elimination to make checks on this efficient. - /// It's also required for safety that this value never changes once observed, as Unsafe.As casts are employed based on its value. - /// - internal static readonly bool s_valueTaskPoolingEnabled = GetPoolAsyncValueTasksSwitch(); - /// Maximum number of boxes that are allowed to be cached per state machine type. - internal static readonly int s_valueTaskPoolingCacheSize = GetPoolAsyncValueTasksLimitValue(); - /// Sentinel object used to indicate that the builder completed synchronously and successfully. - private static readonly object s_syncSuccessSentinel = AsyncValueTaskMethodBuilder.s_syncSuccessSentinel; + private static readonly Task s_syncSuccessSentinel = AsyncValueTaskMethodBuilder.s_syncSuccessSentinel; - /// The wrapped state machine box or task, based on the value of . + /// The wrapped task. /// /// If the operation completed synchronously and successfully, this will be . /// - private object? m_task; // Debugger depends on the exact name of this field. + private Task? m_task; // Debugger depends on the exact name of this field. /// Creates an instance of the struct. /// The initialized instance. @@ -39,8 +27,7 @@ public struct AsyncValueTaskMethodBuilder /// The type of the state machine. /// The state machine instance, passed by reference. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Start(ref TStateMachine stateMachine) - where TStateMachine : IAsyncStateMachine => + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => AsyncMethodBuilderCore.Start(ref stateMachine); /// Associates the builder with the specified state machine. @@ -55,29 +42,16 @@ public void SetResult() { m_task = s_syncSuccessSentinel; } - else if (s_valueTaskPoolingEnabled) - { - Unsafe.As(m_task).SetResult(default); - } else { - AsyncTaskMethodBuilder.SetExistingTaskResult(Unsafe.As>(m_task), default); + AsyncTaskMethodBuilder.SetExistingTaskResult(m_task, default); } } /// Marks the task as failed and binds the specified exception to the task. /// The exception to bind to the task. - public void SetException(Exception exception) - { - if (s_valueTaskPoolingEnabled) - { - AsyncValueTaskMethodBuilder.SetException(exception, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.SetException(exception, ref Unsafe.As?>(ref m_task)); - } - } + public void SetException(Exception exception) => + AsyncTaskMethodBuilder.SetException(exception, ref m_task); /// Gets the task for this builder. public ValueTask Task @@ -94,27 +68,11 @@ public ValueTask Task // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly // initialized m_task to a state machine object. However, if the type is used manually (not via // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then - // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around - // the interface instead. + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a normal + // task object instead. - if (s_valueTaskPoolingEnabled) - { - var box = Unsafe.As(m_task); - if (box is null) - { - m_task = box = AsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); - } - return new ValueTask(box, box.Version); - } - else - { - var task = Unsafe.As?>(m_task); - if (task is null) - { - m_task = task = new Task(); // base task used rather than box to minimize size when used as manual promise - } - return new ValueTask(task); - } + Task? task = m_task ??= new Task(); // base task used rather than box to minimize size when used as manual promise + return new ValueTask(task); } } @@ -125,17 +83,8 @@ public ValueTask Task /// The state machine. public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (s_valueTaskPoolingEnabled) - { - AsyncValueTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// Schedules the state machine to proceed to the next action when the specified awaiter completes. /// The type of the awaiter. @@ -145,17 +94,8 @@ public void AwaitOnCompleted(ref TAwaiter awaiter, ref [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (s_valueTaskPoolingEnabled) - { - AsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// /// Gets an object that may be used to uniquely identify this builder to the debugger. @@ -165,28 +105,6 @@ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. /// - internal object ObjectIdForDebugger - { - get - { - if (m_task is null) - { - m_task = s_valueTaskPoolingEnabled ? (object) - AsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox() : - AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); - } - - return m_task; - } - } - - private static bool GetPoolAsyncValueTasksSwitch() => - Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKS") is string value && - (bool.IsTrueStringIgnoreCase(value) || value == "1"); - - private static int GetPoolAsyncValueTasksLimitValue() => - int.TryParse(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLASYNCVALUETASKSLIMIT"), out int result) && result > 0 ? - result : - Environment.ProcessorCount * 4; // arbitrary default value + internal object ObjectIdForDebugger => m_task ??= AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs index 856feb8d95a9a..62499d54e761b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncValueTaskMethodBuilderT.cs @@ -1,13 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; -using System.Threading.Tasks.Sources; -using Internal.Runtime.CompilerServices; namespace System.Runtime.CompilerServices { @@ -22,12 +17,10 @@ public struct AsyncValueTaskMethodBuilder /// is valid for the mode in which we're operating. As such, it's cached on the generic builder per TResult /// rather than having one sentinel instance for all types. /// - internal static readonly object s_syncSuccessSentinel = AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled ? (object) - new SyncSuccessSentinelStateMachineBox() : - new Task(default(TResult)!); + internal static readonly Task s_syncSuccessSentinel = new Task(default(TResult)!); - /// The wrapped state machine or task. If the operation completed synchronously and successfully, this will be a sentinel object compared by reference identity. - private object? m_task; // Debugger depends on the exact name of this field. + /// The wrapped task. If the operation completed synchronously and successfully, this will be a sentinel object compared by reference identity. + private Task? m_task; // Debugger depends on the exact name of this field. /// The result for this builder if it's completed synchronously, in which case will be . private TResult _result; @@ -56,39 +49,16 @@ public void SetResult(TResult result) _result = result; m_task = s_syncSuccessSentinel; } - else if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - Unsafe.As(m_task).SetResult(result); - } else { - AsyncTaskMethodBuilder.SetExistingTaskResult(Unsafe.As>(m_task), result); + AsyncTaskMethodBuilder.SetExistingTaskResult(m_task, result); } } /// Marks the value task as failed and binds the specified exception to the value task. /// The exception to bind to the value task. - public void SetException(Exception exception) - { - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - SetException(exception, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.SetException(exception, ref Unsafe.As?>(ref m_task)); - } - } - - internal static void SetException(Exception exception, [NotNull] ref StateMachineBox? boxFieldRef) - { - if (exception is null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); - } - - (boxFieldRef ??= CreateWeaklyTypedStateMachineBox()).SetException(exception); - } + public void SetException(Exception exception) => + AsyncTaskMethodBuilder.SetException(exception, ref m_task); /// Gets the value task for this builder. public ValueTask Task @@ -105,27 +75,11 @@ public ValueTask Task // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly // initialized m_task to a state machine object. However, if the type is used manually (not via // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then - // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around - // the interface instead. + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a + // normal task object instead. - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - var box = Unsafe.As(m_task); - if (box is null) - { - m_task = box = CreateWeaklyTypedStateMachineBox(); - } - return new ValueTask(box, box.Version); - } - else - { - var task = Unsafe.As?>(m_task); - if (task is null) - { - m_task = task = new Task(); // base task used rather than box to minimize size when used as manual promise - } - return new ValueTask(task); - } + Task? task = m_task ??= new Task(); // base task used rather than box to minimize size when used as manual promise + return new ValueTask(task); } } @@ -136,32 +90,8 @@ public ValueTask Task /// The state machine. public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } - - internal static void AwaitOnCompleted( - ref TAwaiter awaiter, ref TStateMachine stateMachine, ref StateMachineBox? box) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - { - try - { - awaiter.OnCompleted(GetStateMachineBox(ref stateMachine, ref box).MoveNextAction); - } - catch (Exception e) - { - System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null); - } - } + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// Schedules the state machine to proceed to the next action when the specified awaiter completes. /// The type of the awaiter. @@ -171,106 +101,8 @@ internal static void AwaitOnCompleted( [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - { - if (AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled) - { - AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As(ref m_task)); - } - else - { - AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref Unsafe.As?>(ref m_task)); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void AwaitUnsafeOnCompleted( - ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref StateMachineBox? boxRef) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - { - IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine, ref boxRef); - AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, box); - } - - /// Gets the "boxed" state machine object. - /// Specifies the type of the async state machine. - /// The state machine. - /// A reference to the field containing the initialized state machine box. - /// The "boxed" state machine. - private static IAsyncStateMachineBox GetStateMachineBox( - ref TStateMachine stateMachine, - [NotNull] ref StateMachineBox? boxFieldRef) - where TStateMachine : IAsyncStateMachine - { - ExecutionContext? currentContext = ExecutionContext.Capture(); - - // Check first for the most common case: not the first yield in an async method. - // In this case, the first yield will have already "boxed" the state machine in - // a strongly-typed manner into an AsyncStateMachineBox. It will already contain - // the state machine as well as a MoveNextDelegate and a context. The only thing - // we might need to do is update the context if that's changed since it was stored. - if (boxFieldRef is StateMachineBox stronglyTypedBox) - { - if (stronglyTypedBox.Context != currentContext) - { - stronglyTypedBox.Context = currentContext; - } - - return stronglyTypedBox; - } - - // The least common case: we have a weakly-typed boxed. This results if the debugger - // or some other use of reflection accesses a property like ObjectIdForDebugger. In - // such situations, we need to get an object to represent the builder, but we don't yet - // know the type of the state machine, and thus can't use TStateMachine. Instead, we - // use the IAsyncStateMachine interface, which all TStateMachines implement. This will - // result in a boxing allocation when storing the TStateMachine if it's a struct, but - // this only happens in active debugging scenarios where such performance impact doesn't - // matter. - if (boxFieldRef is StateMachineBox weaklyTypedBox) - { - // If this is the first await, we won't yet have a state machine, so store it. - if (weaklyTypedBox.StateMachine is null) - { - Debugger.NotifyOfCrossThreadDependency(); // same explanation as with usage below - weaklyTypedBox.StateMachine = stateMachine; - } - - // Update the context. This only happens with a debugger, so no need to spend - // extra IL checking for equality before doing the assignment. - weaklyTypedBox.Context = currentContext; - return weaklyTypedBox; - } - - // Alert a listening debugger that we can't make forward progress unless it slips threads. - // If we don't do this, and a method that uses "await foo;" is invoked through funceval, - // we could end up hooking up a callback to push forward the async method's state machine, - // the debugger would then abort the funceval after it takes too long, and then continuing - // execution could result in another callback being hooked up. At that point we have - // multiple callbacks registered to push the state machine, which could result in bad behavior. - Debugger.NotifyOfCrossThreadDependency(); - - // At this point, m_task should really be null, in which case we want to create the box. - // However, in a variety of debugger-related (erroneous) situations, it might be non-null, - // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-intialized - // as a Task rather than as an ValueTaskStateMachineBox. The worst that happens in such - // cases is we lose the ability to properly step in the debugger, as the debugger uses that - // object's identity to track this specific builder/state machine. As such, we proceed to - // overwrite whatever's there anyway, even if it's non-null. - var box = StateMachineBox.GetOrCreateBox(); - boxFieldRef = box; // important: this must be done before storing stateMachine into box.StateMachine! - box.StateMachine = stateMachine; - box.Context = currentContext; - - return box; - } - - /// - /// Creates a box object for use when a non-standard access pattern is employed, e.g. when Task - /// is evaluated in the debugger prior to the async method yielding for the first time. - /// - internal static StateMachineBox CreateWeaklyTypedStateMachineBox() => new StateMachineBox(); + where TStateMachine : IAsyncStateMachine => + AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); /// /// Gets an object that may be used to uniquely identify this builder to the debugger. @@ -280,241 +112,6 @@ private static IAsyncStateMachineBox GetStateMachineBox( /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. /// - internal object ObjectIdForDebugger - { - get - { - if (m_task is null) - { - m_task = AsyncValueTaskMethodBuilder.s_valueTaskPoolingEnabled ? (object) - CreateWeaklyTypedStateMachineBox() : - AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); - } - - return m_task; - } - } - - /// The base type for all value task box reusable box objects, regardless of state machine type. - internal abstract class StateMachineBox : - IValueTaskSource, IValueTaskSource - { - /// A delegate to the MoveNext method. - protected Action? _moveNextAction; - /// Captured ExecutionContext with which to invoke MoveNext. - public ExecutionContext? Context; - /// Implementation for IValueTaskSource interfaces. - protected ManualResetValueTaskSourceCore _valueTaskSource; - - /// Completes the box with a result. - /// The result. - public void SetResult(TResult result) => - _valueTaskSource.SetResult(result); - - /// Completes the box with an error. - /// The exception. - public void SetException(Exception error) => - _valueTaskSource.SetException(error); - - /// Gets the status of the box. - public ValueTaskSourceStatus GetStatus(short token) => _valueTaskSource.GetStatus(token); - - /// Schedules the continuation action for this box. - public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => - _valueTaskSource.OnCompleted(continuation, state, token, flags); - - /// Gets the current version number of the box. - public short Version => _valueTaskSource.Version; - - /// Implemented by derived type. - TResult IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; - - /// Implemented by derived type. - void IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; - } - - private sealed class SyncSuccessSentinelStateMachineBox : StateMachineBox - { - public SyncSuccessSentinelStateMachineBox() => SetResult(default!); - } - - /// Provides a strongly-typed box object based on the specific state machine type in use. - private sealed class StateMachineBox : - StateMachineBox, - IValueTaskSource, IValueTaskSource, IAsyncStateMachineBox, IThreadPoolWorkItem - where TStateMachine : IAsyncStateMachine - { - /// Delegate used to invoke on an ExecutionContext when passed an instance of this box type. - private static readonly ContextCallback s_callback = ExecutionContextCallback; - /// Lock used to protected the shared cache of boxes. - /// The code that uses this assumes a runtime without thread aborts. - private static int s_cacheLock; - /// Singly-linked list cache of boxes. - private static StateMachineBox? s_cache; - /// The number of items stored in . - private static int s_cacheSize; - - // TODO: - // AsyncTaskMethodBuilder logs about the state machine box lifecycle; AsyncValueTaskMethodBuilder currently - // does not when it employs these pooled boxes. That logging is based on Task IDs, which we lack here. - // We could use the box's Version, but that is very likely to conflict with the IDs of other tasks in the system. - // For now, we don't log, but should we choose to we'll probably want to store an int ID on the state machine box, - // and initialize it an ID from Task's generator. - - /// If this box is stored in the cache, the next box in the cache. - private StateMachineBox? _next; - /// The state machine itself. - public TStateMachine? StateMachine; - - /// Gets a box object to use for an operation. This may be a reused, pooled object, or it may be new. - [MethodImpl(MethodImplOptions.AggressiveInlining)] // only one caller - internal static StateMachineBox GetOrCreateBox() - { - // Try to acquire the lock to access the cache. If there's any contention, don't use the cache. - if (Interlocked.CompareExchange(ref s_cacheLock, 1, 0) == 0) - { - // If there are any instances cached, take one from the cache stack and use it. - StateMachineBox? box = s_cache; - if (!(box is null)) - { - s_cache = box._next; - box._next = null; - s_cacheSize--; - Debug.Assert(s_cacheSize >= 0, "Expected the cache size to be non-negative."); - - // Release the lock and return the box. - Volatile.Write(ref s_cacheLock, 0); - return box; - } - - // No objects were cached. We'll just create a new instance. - Debug.Assert(s_cacheSize == 0, "Expected cache size to be 0."); - - // Release the lock. - Volatile.Write(ref s_cacheLock, 0); - } - - // Couldn't quickly get a cached instance, so create a new instance. - return new StateMachineBox(); - } - - private void ReturnOrDropBox() - { - Debug.Assert(_next is null, "Expected box to not be part of cached list."); - - // Clear out the state machine and associated context to avoid keeping arbitrary state referenced by - // lifted locals. We want to do this regardless of whether we end up caching the box or not, in case - // the caller keeps the box alive for an arbitrary period of time. - ClearStateUponCompletion(); - - // Reset the MRVTSC. We can either do this here, in which case we may be paying the (small) overhead - // to reset the box even if we're going to drop it, or we could do it while holding the lock, in which - // case we'll only reset it if necessary but causing the lock to be held for longer, thereby causing - // more contention. For now at least, we do it outside of the lock. (This must not be done after - // the lock is released, since at that point the instance could already be in use elsewhere.) - // We also want to increment the version number even if we're going to drop it, to maximize the chances - // that incorrectly double-awaiting a ValueTask will produce an error. - _valueTaskSource.Reset(); - - // If reusing the object would result in potentially wrapping around its version number, just throw it away. - // This provides a modicum of additional safety when ValueTasks are misused (helping to avoid the case where - // a ValueTask is illegally re-awaited and happens to do so at exactly 2^16 uses later on this exact same instance), - // at the expense of potentially incurring an additional allocation every 65K uses. - if ((ushort)_valueTaskSource.Version == ushort.MaxValue) - { - return; - } - - // Try to acquire the cache lock. If there's any contention, or if the cache is full, we just throw away the object. - if (Interlocked.CompareExchange(ref s_cacheLock, 1, 0) == 0) - { - if (s_cacheSize < AsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize) - { - // Push the box onto the cache stack for subsequent reuse. - _next = s_cache; - s_cache = this; - s_cacheSize++; - Debug.Assert(s_cacheSize > 0 && s_cacheSize <= AsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize, "Expected cache size to be within bounds."); - } - - // Release the lock. - Volatile.Write(ref s_cacheLock, 0); - } - } - - /// - /// Clear out the state machine and associated context to avoid keeping arbitrary state referenced by lifted locals. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearStateUponCompletion() - { - StateMachine = default; - Context = default; - } - - /// - /// Used to initialize s_callback above. We don't use a lambda for this on purpose: a lambda would - /// introduce a new generic type behind the scenes that comes with a hefty size penalty in AOT builds. - /// - private static void ExecutionContextCallback(object? s) - { - // Only used privately to pass directly to EC.Run - Debug.Assert(s is StateMachineBox); - Unsafe.As>(s).StateMachine!.MoveNext(); - } - - /// A delegate to the method. - public Action MoveNextAction => _moveNextAction ??= new Action(MoveNext); - - /// Invoked to run MoveNext when this instance is executed from the thread pool. - void IThreadPoolWorkItem.Execute() => MoveNext(); - - /// Calls MoveNext on - public void MoveNext() - { - ExecutionContext? context = Context; - - if (context is null) - { - Debug.Assert(!(StateMachine is null)); - StateMachine.MoveNext(); - } - else - { - ExecutionContext.RunInternal(context, s_callback, this); - } - } - - /// Get the result of the operation. - TResult IValueTaskSource.GetResult(short token) - { - try - { - return _valueTaskSource.GetResult(token); - } - finally - { - // Reuse this instance if possible, otherwise clear and drop it. - ReturnOrDropBox(); - } - } - - /// Get the result of the operation. - void IValueTaskSource.GetResult(short token) - { - try - { - _valueTaskSource.GetResult(token); - } - finally - { - // Reuse this instance if possible, otherwise clear and drop it. - ReturnOrDropBox(); - } - } - - /// Gets the state machine as a boxed object. This should only be used for debugging purposes. - IAsyncStateMachine IAsyncStateMachineBox.GetStateMachineObject() => StateMachine!; // likely boxes, only use for debugging - } + internal object ObjectIdForDebugger => m_task ??= AsyncTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs index 56b0a53930f0a..a501994645a9f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs @@ -30,8 +30,20 @@ public class CallConvSuppressGCTransition /// public CallConvSuppressGCTransition() { } } + public class CallConvThiscall { public CallConvThiscall() { } } + + /// + /// Indicates that the calling convention used is the member function variant. + /// + public class CallConvMemberFunction + { + /// + /// Initializes a new instance of the class. + /// + public CallConvMemberFunction() { } + } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs index c12b66f1bf7e6..7a34c739e2929 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs @@ -3,6 +3,7 @@ namespace System.Runtime.CompilerServices { + [Obsolete(Obsoletions.DisablePrivateReflectionAttributeMessage, DiagnosticId = Obsoletions.DisablePrivateReflectionAttributeDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] public sealed class DisablePrivateReflectionAttribute : Attribute { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs new file mode 100644 index 0000000000000..84996a0e062fd --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilder.cs @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using StateMachineBox = System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.StateMachineBox; + +namespace System.Runtime.CompilerServices +{ + /// Represents a builder for asynchronous methods that return a . + [StructLayout(LayoutKind.Auto)] + public struct PoolingAsyncValueTaskMethodBuilder + { + /// Maximum number of boxes that are allowed to be cached per state machine type. + internal static readonly int s_valueTaskPoolingCacheSize = + int.TryParse(Environment.GetEnvironmentVariable("DOTNET_SYSTEM_THREADING_POOLINGASYNCVALUETASKSCACHESIZE"), NumberStyles.Integer, CultureInfo.InvariantCulture, out int result) && result > 0 ? + result : + Environment.ProcessorCount * 4; // arbitrary default value + + /// Sentinel object used to indicate that the builder completed synchronously and successfully. + private static readonly StateMachineBox s_syncSuccessSentinel = PoolingAsyncValueTaskMethodBuilder.s_syncSuccessSentinel; + + /// The wrapped state machine box. + /// + /// If the operation completed synchronously and successfully, this will be . + /// + private StateMachineBox? m_task; // Debugger depends on the exact name of this field. + + /// Creates an instance of the struct. + /// The initialized instance. + public static PoolingAsyncValueTaskMethodBuilder Create() => default; + + /// Begins running the builder with the associated state machine. + /// The type of the state machine. + /// The state machine instance, passed by reference. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine => + AsyncMethodBuilderCore.Start(ref stateMachine); + + /// Associates the builder with the specified state machine. + /// The state machine instance to associate with the builder. + public void SetStateMachine(IAsyncStateMachine stateMachine) => + AsyncMethodBuilderCore.SetStateMachine(stateMachine, task: null); + + /// Marks the task as successfully completed. + public void SetResult() + { + if (m_task is null) + { + m_task = s_syncSuccessSentinel; + } + else + { + m_task.SetResult(default); + } + } + + /// Marks the task as failed and binds the specified exception to the task. + /// The exception to bind to the task. + public void SetException(Exception exception) => + PoolingAsyncValueTaskMethodBuilder.SetException(exception, ref m_task); + + /// Gets the task for this builder. + public ValueTask Task + { + get + { + if (m_task == s_syncSuccessSentinel) + { + return default; + } + + // With normal access paterns, m_task should always be non-null here: the async method should have + // either completed synchronously, in which case SetResult would have set m_task to a non-null object, + // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly + // initialized m_task to a state machine object. However, if the type is used manually (not via + // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around + // the interface instead. + + StateMachineBox? box = m_task ??= PoolingAsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); + return new ValueTask(box, box.Version); + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// The awaiter. + /// The state machine. + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine => + PoolingAsyncValueTaskMethodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// The awaiter. + /// The state machine. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine => + PoolingAsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + /// + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// + /// + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner + /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. + /// + internal object ObjectIdForDebugger => + m_task ??= PoolingAsyncValueTaskMethodBuilder.CreateWeaklyTypedStateMachineBox(); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs new file mode 100644 index 0000000000000..02d15bb92c3b9 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/PoolingAsyncValueTaskMethodBuilderT.cs @@ -0,0 +1,473 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Sources; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.CompilerServices +{ + /// Represents a builder for asynchronous methods that returns a . + /// The type of the result. + [StructLayout(LayoutKind.Auto)] + public struct PoolingAsyncValueTaskMethodBuilder + { + /// Sentinel object used to indicate that the builder completed synchronously and successfully. + /// + /// To avoid memory safety issues even in the face of invalid race conditions, we ensure that the type of this object + /// is valid for the mode in which we're operating. As such, it's cached on the generic builder per TResult + /// rather than having one sentinel instance for all types. + /// + internal static readonly StateMachineBox s_syncSuccessSentinel = new SyncSuccessSentinelStateMachineBox(); + + /// The wrapped state machine or task. If the operation completed synchronously and successfully, this will be a sentinel object compared by reference identity. + private StateMachineBox? m_task; // Debugger depends on the exact name of this field. + /// The result for this builder if it's completed synchronously, in which case will be . + private TResult _result; + + /// Creates an instance of the struct. + /// The initialized instance. + public static PoolingAsyncValueTaskMethodBuilder Create() => default; + + /// Begins running the builder with the associated state machine. + /// The type of the state machine. + /// The state machine instance, passed by reference. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => + AsyncMethodBuilderCore.Start(ref stateMachine); + + /// Associates the builder with the specified state machine. + /// The state machine instance to associate with the builder. + public void SetStateMachine(IAsyncStateMachine stateMachine) => + AsyncMethodBuilderCore.SetStateMachine(stateMachine, task: null); + + /// Marks the value task as successfully completed. + /// The result to use to complete the value task. + public void SetResult(TResult result) + { + if (m_task is null) + { + _result = result; + m_task = s_syncSuccessSentinel; + } + else + { + m_task.SetResult(result); + } + } + + /// Marks the value task as failed and binds the specified exception to the value task. + /// The exception to bind to the value task. + public void SetException(Exception exception) => + SetException(exception, ref m_task); + + internal static void SetException(Exception exception, [NotNull] ref StateMachineBox? boxFieldRef) + { + if (exception is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); + } + + (boxFieldRef ??= CreateWeaklyTypedStateMachineBox()).SetException(exception); + } + + /// Gets the value task for this builder. + public ValueTask Task + { + get + { + if (m_task == s_syncSuccessSentinel) + { + return new ValueTask(_result); + } + + // With normal access paterns, m_task should always be non-null here: the async method should have + // either completed synchronously, in which case SetResult would have set m_task to a non-null object, + // or it should be completing asynchronously, in which case AwaitUnsafeOnCompleted would have similarly + // initialized m_task to a state machine object. However, if the type is used manually (not via + // compiler-generated code) and accesses Task directly, we force it to be initialized. Things will then + // "work" but in a degraded mode, as we don't know the TStateMachine type here, and thus we use a box around + // the interface instead. + + PoolingAsyncValueTaskMethodBuilder.StateMachineBox? box = m_task ??= CreateWeaklyTypedStateMachineBox(); + return new ValueTask(box, box.Version); + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine => + AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + internal static void AwaitOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine, ref StateMachineBox? box) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + try + { + awaiter.OnCompleted(GetStateMachineBox(ref stateMachine, ref box).MoveNextAction); + } + catch (Exception e) + { + System.Threading.Tasks.Task.ThrowAsync(e, targetContext: null); + } + } + + /// Schedules the state machine to proceed to the next action when the specified awaiter completes. + /// The type of the awaiter. + /// The type of the state machine. + /// the awaiter + /// The state machine. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine => + AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine, [NotNull] ref StateMachineBox? boxRef) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + IAsyncStateMachineBox box = GetStateMachineBox(ref stateMachine, ref boxRef); + AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted(ref awaiter, box); + } + + /// Gets the "boxed" state machine object. + /// Specifies the type of the async state machine. + /// The state machine. + /// A reference to the field containing the initialized state machine box. + /// The "boxed" state machine. + private static IAsyncStateMachineBox GetStateMachineBox( + ref TStateMachine stateMachine, + [NotNull] ref StateMachineBox? boxFieldRef) + where TStateMachine : IAsyncStateMachine + { + ExecutionContext? currentContext = ExecutionContext.Capture(); + + // Check first for the most common case: not the first yield in an async method. + // In this case, the first yield will have already "boxed" the state machine in + // a strongly-typed manner into an AsyncStateMachineBox. It will already contain + // the state machine as well as a MoveNextDelegate and a context. The only thing + // we might need to do is update the context if that's changed since it was stored. + if (boxFieldRef is StateMachineBox stronglyTypedBox) + { + if (stronglyTypedBox.Context != currentContext) + { + stronglyTypedBox.Context = currentContext; + } + + return stronglyTypedBox; + } + + // The least common case: we have a weakly-typed boxed. This results if the debugger + // or some other use of reflection accesses a property like ObjectIdForDebugger. In + // such situations, we need to get an object to represent the builder, but we don't yet + // know the type of the state machine, and thus can't use TStateMachine. Instead, we + // use the IAsyncStateMachine interface, which all TStateMachines implement. This will + // result in a boxing allocation when storing the TStateMachine if it's a struct, but + // this only happens in active debugging scenarios where such performance impact doesn't + // matter. + if (boxFieldRef is StateMachineBox weaklyTypedBox) + { + // If this is the first await, we won't yet have a state machine, so store it. + if (weaklyTypedBox.StateMachine is null) + { + Debugger.NotifyOfCrossThreadDependency(); // same explanation as with usage below + weaklyTypedBox.StateMachine = stateMachine; + } + + // Update the context. This only happens with a debugger, so no need to spend + // extra IL checking for equality before doing the assignment. + weaklyTypedBox.Context = currentContext; + return weaklyTypedBox; + } + + // Alert a listening debugger that we can't make forward progress unless it slips threads. + // If we don't do this, and a method that uses "await foo;" is invoked through funceval, + // we could end up hooking up a callback to push forward the async method's state machine, + // the debugger would then abort the funceval after it takes too long, and then continuing + // execution could result in another callback being hooked up. At that point we have + // multiple callbacks registered to push the state machine, which could result in bad behavior. + Debugger.NotifyOfCrossThreadDependency(); + + // At this point, m_task should really be null, in which case we want to create the box. + // However, in a variety of debugger-related (erroneous) situations, it might be non-null, + // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-intialized + // as a Task rather than as an ValueTaskStateMachineBox. The worst that happens in such + // cases is we lose the ability to properly step in the debugger, as the debugger uses that + // object's identity to track this specific builder/state machine. As such, we proceed to + // overwrite whatever's there anyway, even if it's non-null. + StateMachineBox box = StateMachineBox.GetOrCreateBox(); + boxFieldRef = box; // important: this must be done before storing stateMachine into box.StateMachine! + box.StateMachine = stateMachine; + box.Context = currentContext; + + return box; + } + + /// + /// Creates a box object for use when a non-standard access pattern is employed, e.g. when Task + /// is evaluated in the debugger prior to the async method yielding for the first time. + /// + internal static StateMachineBox CreateWeaklyTypedStateMachineBox() => new StateMachineBox(); + + /// + /// Gets an object that may be used to uniquely identify this builder to the debugger. + /// + /// + /// This property lazily instantiates the ID in a non-thread-safe manner. + /// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner + /// when no other threads are in the middle of accessing this or other members that lazily initialize the box. + /// + internal object ObjectIdForDebugger => m_task ??= CreateWeaklyTypedStateMachineBox(); + + /// The base type for all value task box reusable box objects, regardless of state machine type. + internal abstract class StateMachineBox : IValueTaskSource, IValueTaskSource + { + /// A delegate to the MoveNext method. + protected Action? _moveNextAction; + /// Captured ExecutionContext with which to invoke MoveNext. + public ExecutionContext? Context; + /// Implementation for IValueTaskSource interfaces. + protected ManualResetValueTaskSourceCore _valueTaskSource; + + /// Completes the box with a result. + /// The result. + public void SetResult(TResult result) => + _valueTaskSource.SetResult(result); + + /// Completes the box with an error. + /// The exception. + public void SetException(Exception error) => + _valueTaskSource.SetException(error); + + /// Gets the status of the box. + public ValueTaskSourceStatus GetStatus(short token) => _valueTaskSource.GetStatus(token); + + /// Schedules the continuation action for this box. + public void OnCompleted(Action continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags) => + _valueTaskSource.OnCompleted(continuation, state, token, flags); + + /// Gets the current version number of the box. + public short Version => _valueTaskSource.Version; + + /// Implemented by derived type. + TResult IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; + + /// Implemented by derived type. + void IValueTaskSource.GetResult(short token) => throw NotImplemented.ByDesign; + } + + /// Type used as a singleton to indicate synchronous success for an async method. + private sealed class SyncSuccessSentinelStateMachineBox : StateMachineBox + { + public SyncSuccessSentinelStateMachineBox() => SetResult(default!); + } + + /// Provides a strongly-typed box object based on the specific state machine type in use. + private sealed class StateMachineBox : + StateMachineBox, + IValueTaskSource, IValueTaskSource, IAsyncStateMachineBox, IThreadPoolWorkItem + where TStateMachine : IAsyncStateMachine + { + /// Delegate used to invoke on an ExecutionContext when passed an instance of this box type. + private static readonly ContextCallback s_callback = ExecutionContextCallback; + /// Thread-local cache of boxes. This currently only ever stores one. + [ThreadStatic] + private static StateMachineBox? t_tlsCache; + /// Lock used to protected the shared cache of boxes. 1 == held, 0 == not held. + /// The code that uses this assumes a runtime without thread aborts. + private static int s_cacheLock; + /// Singly-linked list cache of boxes. + private static StateMachineBox? s_cache; + /// The number of items stored in . + private static int s_cacheSize; + + /// If this box is stored in the cache, the next box in the cache. + private StateMachineBox? _next; + /// The state machine itself. + public TStateMachine? StateMachine; + + /// Gets a box object to use for an operation. This may be a reused, pooled object, or it may be new. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // only one caller + internal static StateMachineBox GetOrCreateBox() + { + StateMachineBox? box; + + // First see if the thread-static cache of at most one box has one. + box = t_tlsCache; + if (box is not null) + { + t_tlsCache = null; + return box; + } + + // Try to acquire the lock to access the cache. If there's any contention, don't use the cache. + if (s_cache is not null && // hot read just to see if there's any point paying for the interlocked + Interlocked.Exchange(ref s_cacheLock, 1) == 0) + { + // If there are any instances cached, take one from the cache stack and use it. + box = s_cache; + if (box is not null) + { + s_cache = box._next; + box._next = null; + s_cacheSize--; + Debug.Assert(s_cacheSize >= 0, "Expected the cache size to be non-negative."); + + // Release the lock and return the box. + Volatile.Write(ref s_cacheLock, 0); + return box; + } + + // No objects were cached. We'll just create a new instance. + Debug.Assert(s_cacheSize == 0, "Expected cache size to be 0."); + + // Release the lock. + Volatile.Write(ref s_cacheLock, 0); + } + + // Couldn't quickly get a cached instance, so create a new instance. + return new StateMachineBox(); + } + + /// Returns this instance to the cache, or drops it if the cache is full or this instance shouldn't be cached. + private void ReturnOrDropBox() + { + Debug.Assert(_next is null, "Expected box to not be part of cached list."); + + // Clear out the state machine and associated context to avoid keeping arbitrary state referenced by + // lifted locals. We want to do this regardless of whether we end up caching the box or not, in case + // the caller keeps the box alive for an arbitrary period of time. + ClearStateUponCompletion(); + + // Reset the MRVTSC. We can either do this here, in which case we may be paying the (small) overhead + // to reset the box even if we're going to drop it, or we could do it while holding the lock, in which + // case we'll only reset it if necessary but causing the lock to be held for longer, thereby causing + // more contention. For now at least, we do it outside of the lock. (This must not be done after + // the lock is released, since at that point the instance could already be in use elsewhere.) + // We also want to increment the version number even if we're going to drop it, to maximize the chances + // that incorrectly double-awaiting a ValueTask will produce an error. + _valueTaskSource.Reset(); + + // If reusing the object would result in potentially wrapping around its version number, just throw it away. + // This provides a modicum of additional safety when ValueTasks are misused (helping to avoid the case where + // a ValueTask is illegally re-awaited and happens to do so at exactly 2^16 uses later on this exact same instance), + // at the expense of potentially incurring an additional allocation every 65K uses. + if ((ushort)_valueTaskSource.Version == ushort.MaxValue) + { + return; + } + + // If the thread static cache is empty, store this into it and bail. + if (t_tlsCache is null) + { + t_tlsCache = this; + return; + } + + // Try to acquire the cache lock. If there's any contention, or if the cache is full, we just throw away the object. + if (Interlocked.Exchange(ref s_cacheLock, 1) == 0) + { + if (s_cacheSize < PoolingAsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize) + { + // Push the box onto the cache stack for subsequent reuse. + _next = s_cache; + s_cache = this; + s_cacheSize++; + Debug.Assert(s_cacheSize > 0 && s_cacheSize <= PoolingAsyncValueTaskMethodBuilder.s_valueTaskPoolingCacheSize, "Expected cache size to be within bounds."); + } + + // Release the lock. + Volatile.Write(ref s_cacheLock, 0); + } + } + + /// + /// Clear out the state machine and associated context to avoid keeping arbitrary state referenced by lifted locals. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ClearStateUponCompletion() + { + StateMachine = default; + Context = default; + } + + /// + /// Used to initialize s_callback above. We don't use a lambda for this on purpose: a lambda would + /// introduce a new generic type behind the scenes that comes with a hefty size penalty in AOT builds. + /// + private static void ExecutionContextCallback(object? s) + { + // Only used privately to pass directly to EC.Run + Debug.Assert(s is StateMachineBox); + Unsafe.As>(s).StateMachine!.MoveNext(); + } + + /// A delegate to the method. + public Action MoveNextAction => _moveNextAction ??= new Action(MoveNext); + + /// Invoked to run MoveNext when this instance is executed from the thread pool. + void IThreadPoolWorkItem.Execute() => MoveNext(); + + /// Calls MoveNext on + public void MoveNext() + { + ExecutionContext? context = Context; + + if (context is null) + { + Debug.Assert(!(StateMachine is null)); + StateMachine.MoveNext(); + } + else + { + ExecutionContext.RunInternal(context, s_callback, this); + } + } + + /// Get the result of the operation. + TResult IValueTaskSource.GetResult(short token) + { + try + { + return _valueTaskSource.GetResult(token); + } + finally + { + // Reuse this instance if possible, otherwise clear and drop it. + ReturnOrDropBox(); + } + } + + /// Get the result of the operation. + void IValueTaskSource.GetResult(short token) + { + try + { + _valueTaskSource.GetResult(token); + } + finally + { + // Reuse this instance if possible, otherwise clear and drop it. + ReturnOrDropBox(); + } + } + + /// Gets the state machine as a boxed object. This should only be used for debugging purposes. + IAsyncStateMachine IAsyncStateMachineBox.GetStateMachineObject() => StateMachine!; // likely boxes, only use for debugging + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs index 18c72613056ba..d89ef99c9ac7b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionDispatchInfo.cs @@ -65,7 +65,7 @@ public void Throw() /// Stores the current stack trace into the specified instance. /// The unthrown instance. /// The argument was null. - /// The argument was previously thrown or previously had a stack trace stored into it.. + /// The argument was previously thrown or previously had a stack trace stored into it. /// The exception instance. [StackTraceHidden] public static Exception SetCurrentStackTrace(Exception source) @@ -79,5 +79,36 @@ public static Exception SetCurrentStackTrace(Exception source) return source; } + + /// + /// Stores the provided stack trace into the specified instance. + /// + /// The unthrown instance. + /// The stack trace string to persist within . This is normally acquired + /// from the property from the remote exception instance. + /// The or argument was null. + /// The argument was previously thrown or previously had a stack trace stored into it. + /// The exception instance. + /// + /// This method populates the property from an arbitrary string value. + /// The typical use case is the transmission of objects across processes with high fidelity, + /// allowing preservation of the exception object's stack trace information. .NET does not attempt to parse the + /// provided string value. The caller is responsible for normalizing line endings if required. + /// + public static Exception SetRemoteStackTrace(Exception source, string stackTrace) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + if (stackTrace is null) + { + throw new ArgumentNullException(nameof(stackTrace)); + } + + source.SetRemoteStackTrace(stackTrace); + + return source; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs index 6a717ef486ba5..a64f44683323e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/CollectionsMarshal.cs @@ -14,7 +14,20 @@ public static class CollectionsMarshal /// Get a view over a 's data. /// Items should not be added or removed from the while the is in use. /// + /// The list to get the data view over. public static Span AsSpan(List? list) => list is null ? default : new Span(list._items, 0, list._size); + + /// + /// Gets either a ref to a in the or a ref null if it does not exist in the . + /// + /// The dictionary to get the ref to from. + /// The key used for lookup. + /// + /// Items should not be added or removed from the while the ref is in use. + /// The ref null can be detected using System.Runtime.CompilerServices.Unsafe.IsNullRef + /// + public static ref TValue GetValueRefOrNullRef(Dictionary dictionary, TKey key) where TKey : notnull + => ref dictionary.FindValue(key); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 3c247a1c91f07..81c87972f7842 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -592,6 +592,238 @@ public static void PtrToStructure(IntPtr ptr, [DisallowNull] T structure) return GetExceptionForHRInternal(errorCode, errorInfo); } +#if !CORECLR + private static Exception? GetExceptionForHRInternal(int errorCode, IntPtr errorInfo) + { + switch (errorCode) + { + case HResults.COR_E_AMBIGUOUSMATCH: + return new System.Reflection.AmbiguousMatchException(); + case HResults.COR_E_APPLICATION: + return new System.ApplicationException(); + case HResults.COR_E_ARGUMENT: + return new System.ArgumentException(); + case HResults.COR_E_ARGUMENTOUTOFRANGE: + return new System.ArgumentOutOfRangeException(); + case HResults.COR_E_ARITHMETIC: + return new System.ArithmeticException(); + case HResults.COR_E_ARRAYTYPEMISMATCH: + return new System.ArrayTypeMismatchException(); + case HResults.COR_E_BADEXEFORMAT: + return new System.BadImageFormatException(); + case HResults.COR_E_BADIMAGEFORMAT: + return new System.BadImageFormatException(); + //case HResults.COR_E_CODECONTRACTFAILED: + //return new System.Diagnostics.Contracts.ContractException (); + //case HResults.COR_E_COMEMULATE: + case HResults.COR_E_CUSTOMATTRIBUTEFORMAT: + return new System.Reflection.CustomAttributeFormatException(); + case HResults.COR_E_DATAMISALIGNED: + return new System.DataMisalignedException(); + case HResults.COR_E_DIRECTORYNOTFOUND: + return new System.IO.DirectoryNotFoundException(); + case HResults.COR_E_DIVIDEBYZERO: + return new System.DivideByZeroException(); + case HResults.COR_E_DLLNOTFOUND: + return new System.DllNotFoundException(); + case HResults.COR_E_DUPLICATEWAITOBJECT: + return new System.DuplicateWaitObjectException(); + case HResults.COR_E_ENDOFSTREAM: + return new System.IO.EndOfStreamException(); + case HResults.COR_E_ENTRYPOINTNOTFOUND: + return new System.EntryPointNotFoundException(); + case HResults.COR_E_EXCEPTION: + return new System.Exception(); + case HResults.COR_E_EXECUTIONENGINE: +#pragma warning disable CS0618 // ExecutionEngineException is obsolete + return new System.ExecutionEngineException(); +#pragma warning restore CS0618 + case HResults.COR_E_FIELDACCESS: + return new System.FieldAccessException(); + case HResults.COR_E_FILELOAD: + return new System.IO.FileLoadException(); + case HResults.COR_E_FILENOTFOUND: + return new System.IO.FileNotFoundException(); + case HResults.COR_E_FORMAT: + return new System.FormatException(); + case HResults.COR_E_INDEXOUTOFRANGE: + return new System.IndexOutOfRangeException(); + case HResults.COR_E_INSUFFICIENTEXECUTIONSTACK: + return new System.InsufficientExecutionStackException(); + case HResults.COR_E_INVALIDCAST: + return new System.InvalidCastException(); + case HResults.COR_E_INVALIDFILTERCRITERIA: + return new System.Reflection.InvalidFilterCriteriaException(); + case HResults.COR_E_INVALIDOLEVARIANTTYPE: + return new System.Runtime.InteropServices.InvalidOleVariantTypeException(); + case HResults.COR_E_INVALIDOPERATION: + return new System.InvalidOperationException(); + case HResults.COR_E_INVALIDPROGRAM: + return new System.InvalidProgramException(); + case HResults.COR_E_IO: + return new System.IO.IOException(); + case HResults.COR_E_MARSHALDIRECTIVE: + return new System.Runtime.InteropServices.MarshalDirectiveException(); + case HResults.COR_E_MEMBERACCESS: + return new System.MemberAccessException(); + case HResults.COR_E_METHODACCESS: + return new System.MethodAccessException(); + case HResults.COR_E_MISSINGFIELD: + return new System.MissingFieldException(); + case HResults.COR_E_MISSINGMANIFESTRESOURCE: + return new System.Resources.MissingManifestResourceException(); + case HResults.COR_E_MISSINGMEMBER: + return new System.MissingMemberException(); + case HResults.COR_E_MISSINGMETHOD: + return new System.MissingMethodException(); + case HResults.COR_E_MULTICASTNOTSUPPORTED: + return new System.MulticastNotSupportedException(); + case HResults.COR_E_NOTFINITENUMBER: + return new System.NotFiniteNumberException(); + case HResults.COR_E_NOTSUPPORTED: + return new System.NotSupportedException(); + case HResults.E_POINTER: + return new System.NullReferenceException(); + case HResults.COR_E_OBJECTDISPOSED: + return new System.ObjectDisposedException(""); + case HResults.COR_E_OPERATIONCANCELED: + return new System.OperationCanceledException(); + case HResults.COR_E_OUTOFMEMORY: + return new System.OutOfMemoryException(); + case HResults.COR_E_OVERFLOW: + return new System.OverflowException(); + case HResults.COR_E_PATHTOOLONG: + return new System.IO.PathTooLongException(); + case HResults.COR_E_PLATFORMNOTSUPPORTED: + return new System.PlatformNotSupportedException(); + case HResults.COR_E_RANK: + return new System.RankException(); + case HResults.COR_E_REFLECTIONTYPELOAD: + return new System.MissingMethodException(); + case HResults.COR_E_RUNTIMEWRAPPED: + return new System.MissingMethodException(); + case HResults.COR_E_SECURITY: + return new System.Security.SecurityException(); + case HResults.COR_E_SERIALIZATION: + return new System.Runtime.Serialization.SerializationException(); + case HResults.COR_E_STACKOVERFLOW: + return new System.StackOverflowException(); + case HResults.COR_E_SYNCHRONIZATIONLOCK: + return new System.Threading.SynchronizationLockException(); + case HResults.COR_E_SYSTEM: + return new System.SystemException(); + case HResults.COR_E_TARGET: + return new System.Reflection.TargetException(); + case HResults.COR_E_TARGETINVOCATION: + return new System.MissingMethodException(); + case HResults.COR_E_TARGETPARAMCOUNT: + return new System.Reflection.TargetParameterCountException(); + case HResults.COR_E_THREADABORTED: + return new System.Threading.ThreadAbortException(); + case HResults.COR_E_THREADINTERRUPTED: + return new System.Threading.ThreadInterruptedException(); + case HResults.COR_E_THREADSTART: + return new System.Threading.ThreadStartException(); + case HResults.COR_E_THREADSTATE: + return new System.Threading.ThreadStateException(); + case HResults.COR_E_TYPEACCESS: + return new System.TypeAccessException(); + case HResults.COR_E_TYPEINITIALIZATION: + return new System.TypeInitializationException(""); + case HResults.COR_E_TYPELOAD: + return new System.TypeLoadException(); + case HResults.COR_E_TYPEUNLOADED: + return new System.TypeUnloadedException(); + case HResults.COR_E_UNAUTHORIZEDACCESS: + return new System.UnauthorizedAccessException(); + //case HResults.COR_E_UNSUPPORTEDFORMAT: + case HResults.COR_E_VERIFICATION: + return new System.Security.VerificationException(); + //case HResults.E_INVALIDARG: + case HResults.E_NOTIMPL: + return new System.NotImplementedException(); + //case HResults.E_POINTER: + case HResults.RO_E_CLOSED: + return new System.ObjectDisposedException(""); + case HResults.COR_E_ABANDONEDMUTEX: + case HResults.COR_E_AMBIGUOUSIMPLEMENTATION: + case HResults.COR_E_CANNOTUNLOADAPPDOMAIN: + case HResults.COR_E_CONTEXTMARSHAL: + //case HResults.COR_E_HOSTPROTECTION: + case HResults.COR_E_INSUFFICIENTMEMORY: + case HResults.COR_E_INVALIDCOMOBJECT: + case HResults.COR_E_KEYNOTFOUND: + case HResults.COR_E_MISSINGSATELLITEASSEMBLY: + case HResults.COR_E_SAFEARRAYRANKMISMATCH: + case HResults.COR_E_SAFEARRAYTYPEMISMATCH: + //case HResults.COR_E_SAFEHANDLEMISSINGATTRIBUTE: + //case HResults.COR_E_SEMAPHOREFULL: + //case HResults.COR_E_THREADSTOP: + case HResults.COR_E_TIMEOUT: + case HResults.COR_E_WAITHANDLECANNOTBEOPENED: + case HResults.DISP_E_OVERFLOW: + case HResults.E_BOUNDS: + case HResults.E_CHANGED_STATE: + case HResults.E_FAIL: + case HResults.E_HANDLE: + case HResults.ERROR_MRM_MAP_NOT_FOUND: + case HResults.TYPE_E_TYPEMISMATCH: + case HResults.CO_E_NOTINITIALIZED: + case HResults.RPC_E_CHANGED_MODE: + return new COMException("", errorCode); + + case HResults.STG_E_PATHNOTFOUND: + case HResults.CTL_E_PATHNOTFOUND: + { + return new System.IO.DirectoryNotFoundException + { + HResult = errorCode + }; + } + case HResults.FUSION_E_INVALID_PRIVATE_ASM_LOCATION: + case HResults.FUSION_E_SIGNATURE_CHECK_FAILED: + case HResults.FUSION_E_LOADFROM_BLOCKED: + case HResults.FUSION_E_CACHEFILE_FAILED: + case HResults.FUSION_E_ASM_MODULE_MISSING: + case HResults.FUSION_E_INVALID_NAME: + case HResults.FUSION_E_PRIVATE_ASM_DISALLOWED: + case HResults.FUSION_E_HOST_GAC_ASM_MISMATCH: + case HResults.COR_E_MODULE_HASH_CHECK_FAILED: + case HResults.FUSION_E_REF_DEF_MISMATCH: + case HResults.SECURITY_E_INCOMPATIBLE_SHARE: + case HResults.SECURITY_E_INCOMPATIBLE_EVIDENCE: + case HResults.SECURITY_E_UNVERIFIABLE: + case HResults.COR_E_FIXUPSINEXE: + case HResults.ERROR_TOO_MANY_OPEN_FILES: + case HResults.ERROR_SHARING_VIOLATION: + case HResults.ERROR_LOCK_VIOLATION: + case HResults.ERROR_OPEN_FAILED: + case HResults.ERROR_DISK_CORRUPT: + case HResults.ERROR_UNRECOGNIZED_VOLUME: + case HResults.ERROR_DLL_INIT_FAILED: + case HResults.FUSION_E_CODE_DOWNLOAD_DISABLED: + case HResults.CORSEC_E_MISSING_STRONGNAME: + case HResults.MSEE_E_ASSEMBLYLOADINPROGRESS: + case HResults.ERROR_FILE_INVALID: + { + return new System.IO.FileLoadException + { + HResult = errorCode + }; + } + case HResults.CTL_E_FILENOTFOUND: + { + return new System.IO.FileNotFoundException + { + HResult = errorCode + }; + } + default: + return new COMException("", errorCode); + } + } +#endif + /// /// Throws a CLR exception based on the HRESULT. /// @@ -867,7 +1099,7 @@ public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t) throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(t)); } - // For backward compatibility, we allow lookup up of existing delegate to + // For backward compatibility, we allow lookup of existing delegate to // function pointer mappings using abstract MulticastDelegate type. We will check // for the non-abstract delegate type later if no existing mapping is found. if (t.BaseType != typeof(MulticastDelegate) && t != typeof(MulticastDelegate)) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs index 78053e4462175..33c9dcb575ced 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs @@ -161,6 +161,13 @@ public void DangerousAddRef(ref bool success) success = true; } + // Used by internal callers to avoid declaring a bool to pass by ref + internal void DangerousAddRef() + { + bool success = false; + DangerousAddRef(ref success); + } + public void DangerousRelease() => InternalRelease(disposeOrFinalizeOperation: false); private void InternalRelease(bool disposeOrFinalizeOperation) diff --git a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs index 88c593c872e52..307d16a038355 100644 --- a/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs @@ -793,7 +793,7 @@ private static void ThrowIfTypeNeverValidGenericArgument(RuntimeType type) SR.Format(SR.Argument_NeverValidGenericArgument, type)); } - internal static void SanityCheckGenericArguments(RuntimeType[] genericArguments, RuntimeType[] genericParamters) + internal static void SanityCheckGenericArguments(RuntimeType[] genericArguments, RuntimeType[] genericParameters) { if (genericArguments == null) throw new ArgumentNullException(); @@ -806,9 +806,9 @@ internal static void SanityCheckGenericArguments(RuntimeType[] genericArguments, ThrowIfTypeNeverValidGenericArgument(genericArguments[i]); } - if (genericArguments.Length != genericParamters.Length) + if (genericArguments.Length != genericParameters.Length) throw new ArgumentException( - SR.Format(SR.Argument_NotEnoughGenArguments, genericArguments.Length, genericParamters.Length)); + SR.Format(SR.Argument_NotEnoughGenArguments, genericArguments.Length, genericParameters.Length)); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Span.cs b/src/libraries/System.Private.CoreLib/src/System/Span.cs index e82ca80a44a54..f48bdf91f23c4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Span.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Span.cs @@ -399,7 +399,7 @@ public override string ToString() { return new string(new ReadOnlySpan(ref Unsafe.As(ref _pointer.Value), _length)); } - return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length); + return $"System.Span<{typeof(T).Name}>[{_length}]"; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs index be88201d54eea..8c6a7f1420d43 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -6,6 +6,8 @@ using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; using System.Text; using Internal.Runtime.CompilerServices; @@ -578,9 +580,9 @@ private static string JoinCore(ReadOnlySpan separator, string?[] value, in public static string Join(string? separator, IEnumerable values) { - if (values is List valuesIList) + if (values is List valuesList) { - return JoinCore(separator.AsSpan(), CollectionsMarshal.AsSpan(valuesIList)); + return JoinCore(separator.AsSpan(), CollectionsMarshal.AsSpan(valuesList)); } if (values is string?[] valuesArray) @@ -874,11 +876,8 @@ public string Remove(int startIndex, int count) // a remove that just takes a startindex. public string Remove(int startIndex) { - if (startIndex < 0) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex); - - if (startIndex >= Length) - throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLessThanLength); + if ((uint)startIndex > Length) + throw new ArgumentOutOfRangeException(nameof(startIndex), startIndex < 0 ? SR.ArgumentOutOfRange_StartIndex : SR.ArgumentOutOfRange_StartIndexLargerThanLength); return Substring(0, startIndex); } @@ -1494,78 +1493,137 @@ private string[] SplitWithPostProcessing(ReadOnlySpan sepList, ReadOnlySpan /// to store indexes private void MakeSeparatorList(ReadOnlySpan separators, ref ValueListBuilder sepListBuilder) { - char sep0, sep1, sep2; - - switch (separators.Length) + // Special-case no separators to mean any whitespace is a separator. + if (separators.Length == 0) { - // Special-case no separators to mean any whitespace is a separator. - case 0: - for (int i = 0; i < Length; i++) + for (int i = 0; i < Length; i++) + { + if (char.IsWhiteSpace(this[i])) { - if (char.IsWhiteSpace(this[i])) - { - sepListBuilder.Append(i); - } + sepListBuilder.Append(i); } - break; + } + } - // Special-case the common cases of 1, 2, and 3 separators, with manual comparisons against each separator. - case 1: - sep0 = separators[0]; - for (int i = 0; i < Length; i++) + // Special-case the common cases of 1, 2, and 3 separators, with manual comparisons against each separator. + else if (separators.Length <= 3) + { + char sep0, sep1, sep2; + sep0 = separators[0]; + sep1 = separators.Length > 1 ? separators[1] : sep0; + sep2 = separators.Length > 2 ? separators[2] : sep1; + + if (Length >= 16 && Sse41.IsSupported) + { + MakeSeparatorListVectorized(ref sepListBuilder, sep0, sep1, sep2); + return; + } + + for (int i = 0; i < Length; i++) + { + char c = this[i]; + if (c == sep0 || c == sep1 || c == sep2) { - if (this[i] == sep0) - { - sepListBuilder.Append(i); - } + sepListBuilder.Append(i); } - break; - case 2: - sep0 = separators[0]; - sep1 = separators[1]; + } + } + + // Handle > 3 separators with a probabilistic map, ala IndexOfAny. + // This optimizes for chars being unlikely to match a separator. + else + { + unsafe + { + ProbabilisticMap map = default; + uint* charMap = (uint*)↦ + InitializeProbabilisticMap(charMap, separators); + for (int i = 0; i < Length; i++) { char c = this[i]; - if (c == sep0 || c == sep1) + if (IsCharBitSet(charMap, (byte)c) && IsCharBitSet(charMap, (byte)(c >> 8)) && + separators.Contains(c)) { sepListBuilder.Append(i); } } - break; - case 3: - sep0 = separators[0]; - sep1 = separators[1]; - sep2 = separators[2]; - for (int i = 0; i < Length; i++) + } + } + } + + private void MakeSeparatorListVectorized(ref ValueListBuilder sepListBuilder, char c, char c2, char c3) + { + // Redundant test so we won't prejit remainder of this method + // on platforms without SSE. + if (!Sse41.IsSupported) + { + throw new PlatformNotSupportedException(); + } + + // Constant that allows for the truncation of 16-bit (FFFF/0000) values within a register to 4-bit (F/0) + Vector128 shuffleConstant = Vector128.Create(0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); + + Vector128 v1 = Vector128.Create(c); + Vector128 v2 = Vector128.Create(c2); + Vector128 v3 = Vector128.Create(c3); + + ref char c0 = ref MemoryMarshal.GetReference(this.AsSpan()); + int cond = Length & -Vector128.Count; + int i = 0; + + for (; i < cond; i += Vector128.Count) + { + Vector128 charVector = ReadVector(ref c0, i); + Vector128 cmp = Sse2.CompareEqual(charVector, v1); + + cmp = Sse2.Or(Sse2.CompareEqual(charVector, v2), cmp); + cmp = Sse2.Or(Sse2.CompareEqual(charVector, v3), cmp); + + if (Sse41.TestZ(cmp, cmp)) { continue; } + + Vector128 mask = Sse2.ShiftRightLogical(cmp.AsUInt64(), 4).AsByte(); + mask = Ssse3.Shuffle(mask, shuffleConstant); + + uint lowBits = Sse2.ConvertToUInt32(mask.AsUInt32()); + mask = Sse2.ShiftRightLogical(mask.AsUInt64(), 32).AsByte(); + uint highBits = Sse2.ConvertToUInt32(mask.AsUInt32()); + + for (int idx = i; lowBits != 0; idx++) + { + if ((lowBits & 0xF) != 0) { - char c = this[i]; - if (c == sep0 || c == sep1 || c == sep2) - { - sepListBuilder.Append(i); - } + sepListBuilder.Append(idx); } - break; - // Handle > 3 separators with a probabilistic map, ala IndexOfAny. - // This optimizes for chars being unlikely to match a separator. - default: - unsafe - { - ProbabilisticMap map = default; - uint* charMap = (uint*)↦ - InitializeProbabilisticMap(charMap, separators); + lowBits >>= 8; + } - for (int i = 0; i < Length; i++) - { - char c = this[i]; - if (IsCharBitSet(charMap, (byte)c) && IsCharBitSet(charMap, (byte)(c >> 8)) && - separators.Contains(c)) - { - sepListBuilder.Append(i); - } - } + for (int idx = i + 4; highBits != 0; idx++) + { + if ((highBits & 0xF) != 0) + { + sepListBuilder.Append(idx); } - break; + + highBits >>= 8; + } + } + + for (; i < Length; i++) + { + char curr = Unsafe.Add(ref c0, (IntPtr)(uint)i); + if (curr == c || curr == c2 || curr == c3) + { + sepListBuilder.Append(i); + } + } + + static Vector128 ReadVector(ref char c0, int offset) + { + ref char ci = ref Unsafe.Add(ref c0, (IntPtr)(uint)offset); + ref byte b = ref Unsafe.As(ref ci); + return Unsafe.ReadUnaligned>(ref b); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs b/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs index 15a1055607ac8..ce22da699c928 100644 --- a/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/StringComparer.cs @@ -62,6 +62,102 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) return new CultureAwareComparer(culture, options); } + /// + /// Determines whether the specified is a well-known ordinal string comparer. + /// + /// The comparer to query. + /// When this method returns, contains a value stating whether + /// is case-insensitive. Set to if this method returns . + /// + /// if is a well-known ordinal string comparer; + /// otherwise, . + /// + /// + /// A "well-known ordinal comparer" describes a comparer which behaves identically to + /// when passed to or . + /// For example, is a well-known ordinal comparer because + /// a given as a constructor + /// argument will behave identically to a given + /// as a constructor argument. If is on method exit, + /// then behaves identically to when passed to the + /// constructor of such a collection. + /// + public static bool IsWellKnownOrdinalComparer(IEqualityComparer? comparer, out bool ignoreCase) + { + if (comparer is IInternalStringEqualityComparer internalStringComparer) + { + comparer = internalStringComparer.GetUnderlyingEqualityComparer(); // unwrap if necessary + } + + switch (comparer) + { + case StringComparer stringComparer: + return stringComparer.IsWellKnownOrdinalComparerCore(out ignoreCase); + case GenericEqualityComparer: + // special-case EqualityComparer.Default, which is Ordinal-equivalent + ignoreCase = false; + return true; + default: + // unknown comparer + ignoreCase = default; + return false; + } + } + + private protected virtual bool IsWellKnownOrdinalComparerCore(out bool ignoreCase) + { + // unless specialized comparer overrides this, we're not a well-known ordinal comparer + ignoreCase = default; + return false; + } + + /// + /// Determines whether the specified is a well-known culture-aware string comparer. + /// + /// The comparer to query. + /// When this method returns, contains a value indicating which was used + /// to create . Set to if this method returns . + /// When this method returns, contains a value indicating which was used + /// to create . Set to if this method returns . + /// whether + /// + /// if is a well-known culture-aware string comparer; + /// otherwise, . + /// + /// + /// A "well-known culture-aware comparer" describes a comparer which is tied to a specific using + /// some defined . To create a instance wrapped around a + /// and , use . + /// This method returns when given and other non-linguistic comparers as input. + /// + public static bool IsWellKnownCultureAwareComparer(IEqualityComparer? comparer, [NotNullWhen(true)] out CompareInfo? compareInfo, out CompareOptions compareOptions) + { + if (comparer is IInternalStringEqualityComparer internalStringComparer) + { + comparer = internalStringComparer.GetUnderlyingEqualityComparer(); // unwrap if necessary + } + + if (comparer is StringComparer stringComparer) + { + return stringComparer.IsWellKnownCultureAwareComparerCore(out compareInfo, out compareOptions); + } + else + { + // unknown comparer + compareInfo = default; + compareOptions = default; + return false; + } + } + + private protected virtual bool IsWellKnownCultureAwareComparerCore([NotNullWhen(true)] out CompareInfo? compareInfo, out CompareOptions compareOptions) + { + // unless specialized comparer overrides this, we're not a well-known culture-aware comparer + compareInfo = default; + compareOptions = default; + return false; + } + public int Compare(object? x, object? y) { if (x == y) return 0; @@ -202,6 +298,13 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("_options", _options); info.AddValue("_ignoreCase", (_options & CompareOptions.IgnoreCase) != 0); } + + private protected override bool IsWellKnownCultureAwareComparerCore([NotNullWhen(true)] out CompareInfo? compareInfo, out CompareOptions compareOptions) + { + compareInfo = _compareInfo; + compareOptions = _options; + return true; + } } [Serializable] @@ -280,6 +383,12 @@ public override int GetHashCode() int hashCode = nameof(OrdinalComparer).GetHashCode(); return _ignoreCase ? (~hashCode) : hashCode; } + + private protected override bool IsWellKnownOrdinalComparerCore(out bool ignoreCase) + { + ignoreCase = _ignoreCase; + return true; + } } [Serializable] diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs b/src/libraries/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs index 7232108e3970e..3e58726ff86cb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/CodePageDataItem.cs @@ -3,7 +3,7 @@ namespace System.Text { - internal class CodePageDataItem + internal sealed class CodePageDataItem { public int UIFamilyCodePage { get; } public string WebName { get; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.Data.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.Data.cs index 5643d42d7a1fd..c110074189c19 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.Data.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncoderLatin1BestFitFallback.Data.cs @@ -3,7 +3,7 @@ namespace System.Text { - internal partial class EncoderLatin1BestFitFallbackBuffer + internal sealed partial class EncoderLatin1BestFitFallbackBuffer { // Best fit for ASCII, and since it works for ASCII, we use it for latin1 as well. private static readonly char[] s_arrayCharBestFit = diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs index 1c7066e32f9b5..927c1de3707b7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs @@ -1301,7 +1301,7 @@ public override unsafe int GetChars(byte* bytes, int byteCount, _encoding.GetChars(bytes, byteCount, chars, charCount); } - internal class EncodingCharBuffer + internal sealed class EncodingCharBuffer { private unsafe char* _chars; private readonly unsafe char* _charStart; @@ -1449,7 +1449,7 @@ internal unsafe bool Fallback(byte[] byteBuffer) internal int Count => _charCountResult; } - internal class EncodingByteBuffer + internal sealed class EncodingByteBuffer { private unsafe byte* _bytes; private readonly unsafe byte* _byteStart; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs index 7082e6656a37f..c8373167b60ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs @@ -19,6 +19,11 @@ namespace System.Text /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public readonly struct Rune : IComparable, IComparable, IEquatable +#if SYSTEM_PRIVATE_CORELIB +#pragma warning disable SA1001 // Commas should be spaced correctly + , ISpanFormattable +#pragma warning restore SA1001 +#endif { internal const int MaxUtf16CharsPerRune = 2; // supplementary plane code points are encoded as 2 UTF-16 code units internal const int MaxUtf8BytesPerRune = 4; // supplementary plane code points are encoded as 4 UTF-8 code units @@ -911,6 +916,11 @@ public override string ToString() #endif } +#if SYSTEM_PRIVATE_CORELIB + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + TryEncodeToUtf16(destination, out charsWritten); +#endif + /// /// Attempts to create a from the provided input value. /// @@ -994,19 +1004,27 @@ public static bool TryCreate(uint value, out Rune result) /// The property can be queried ahead of time to determine /// the required size of the buffer. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryEncodeToUtf16(Span destination, out int charsWritten) { - if (destination.Length >= 1) + // The Rune type fits cleanly into a register, so pass byval rather than byref + // to avoid stack-spilling the 'this' parameter. + return TryEncodeToUtf16(this, destination, out charsWritten); + } + + private static bool TryEncodeToUtf16(Rune value, Span destination, out int charsWritten) + { + if (!destination.IsEmpty) { - if (IsBmp) + if (value.IsBmp) { - destination[0] = (char)_value; + destination[0] = (char)value._value; charsWritten = 1; return true; } - else if (destination.Length >= 2) + else if (1 < (uint)destination.Length) { - UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar(_value, out destination[0], out destination[1]); + UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar((uint)value._value, out destination[0], out destination[1]); charsWritten = 2; return true; } @@ -1030,49 +1048,57 @@ public bool TryEncodeToUtf16(Span destination, out int charsWritten) /// The property can be queried ahead of time to determine /// the required size of the buffer. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryEncodeToUtf8(Span destination, out int bytesWritten) + { + // The Rune type fits cleanly into a register, so pass byval rather than byref + // to avoid stack-spilling the 'this' parameter. + return TryEncodeToUtf8(this, destination, out bytesWritten); + } + + private static bool TryEncodeToUtf8(Rune value, Span destination, out int bytesWritten) { // The bit patterns below come from the Unicode Standard, Table 3-6. - if (destination.Length >= 1) + if (!destination.IsEmpty) { - if (IsAscii) + if (value.IsAscii) { - destination[0] = (byte)_value; + destination[0] = (byte)value._value; bytesWritten = 1; return true; } - if (destination.Length >= 2) + if (1 < (uint)destination.Length) { - if (_value <= 0x7FFu) + if (value.Value <= 0x7FFu) { // Scalar 00000yyy yyxxxxxx -> bytes [ 110yyyyy 10xxxxxx ] - destination[0] = (byte)((_value + (0b110u << 11)) >> 6); - destination[1] = (byte)((_value & 0x3Fu) + 0x80u); + destination[0] = (byte)((value._value + (0b110u << 11)) >> 6); + destination[1] = (byte)((value._value & 0x3Fu) + 0x80u); bytesWritten = 2; return true; } - if (destination.Length >= 3) + if (2 < (uint)destination.Length) { - if (_value <= 0xFFFFu) + if (value.Value <= 0xFFFFu) { // Scalar zzzzyyyy yyxxxxxx -> bytes [ 1110zzzz 10yyyyyy 10xxxxxx ] - destination[0] = (byte)((_value + (0b1110 << 16)) >> 12); - destination[1] = (byte)(((_value & (0x3Fu << 6)) >> 6) + 0x80u); - destination[2] = (byte)((_value & 0x3Fu) + 0x80u); + destination[0] = (byte)((value._value + (0b1110 << 16)) >> 12); + destination[1] = (byte)(((value._value & (0x3Fu << 6)) >> 6) + 0x80u); + destination[2] = (byte)((value._value & 0x3Fu) + 0x80u); bytesWritten = 3; return true; } - if (destination.Length >= 4) + if (3 < (uint)destination.Length) { // Scalar 000uuuuu zzzzyyyy yyxxxxxx -> bytes [ 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ] - destination[0] = (byte)((_value + (0b11110 << 21)) >> 18); - destination[1] = (byte)(((_value & (0x3Fu << 12)) >> 12) + 0x80u); - destination[2] = (byte)(((_value & (0x3Fu << 6)) >> 6) + 0x80u); - destination[3] = (byte)((_value & 0x3Fu) + 0x80u); + destination[0] = (byte)((value._value + (0b11110 << 21)) >> 18); + destination[1] = (byte)(((value._value & (0x3Fu << 12)) >> 12) + 0x80u); + destination[2] = (byte)(((value._value & (0x3Fu << 6)) >> 6) + 0x80u); + destination[3] = (byte)((value._value & 0x3Fu) + 0x80u); bytesWritten = 4; return true; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs index 7111d94bca5b9..820df398dd088 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -661,7 +661,7 @@ private static int ChunkCount(StringBuilder? stringBuilder) /// /// Used to hold all the chunks indexes when you have many chunks. /// - private class ManyChunkInfo + private sealed class ManyChunkInfo { private readonly StringBuilder[] _chunks; // These are in normal order (first chunk first) private int _chunkPos; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs b/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs index d807d910b429d..5ad89cd3f98a6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs @@ -79,7 +79,7 @@ internal TranscodingStream(Stream innerStream, Encoding innerEncoding, Encoding public override long Position { get => throw new NotSupportedException(SR.NotSupported_UnseekableStream); - set => throw new NotSupportedException(SR.NotSupported_UnseekableStream); + set => ThrowHelper.ThrowNotSupportedException_UnseekableStream(); } public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) @@ -185,7 +185,7 @@ void InitializeReadDataStructures() { if (!CanRead) { - throw Error.GetReadNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnreadableStream(); } _innerDecoder = _innerEncoding.GetDecoder(); @@ -217,7 +217,7 @@ void InitializeReadDataStructures() { if (!CanWrite) { - throw Error.GetWriteNotSupported(); + ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } _innerEncoder = _innerEncoding.GetEncoder(); @@ -428,7 +428,7 @@ public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(SR.NotSupported_UnseekableStream); public override void SetLength(long value) - => throw new NotSupportedException(SR.NotSupported_UnseekableStream); + => ThrowHelper.ThrowNotSupportedException_UnseekableStream(); [StackTraceHidden] private void ThrowIfDisposed() @@ -443,9 +443,7 @@ private void ThrowIfDisposed() [StackTraceHidden] private void ThrowObjectDisposedException() { - throw new ObjectDisposedException( - objectName: GetType().Name, - message: SR.ObjectDisposed_StreamClosed); + ThrowHelper.ThrowObjectDisposedException_StreamClosed(GetType().Name); } public override void Write(byte[] buffer, int offset, int count) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SizeOpt.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SizeOpt.cs new file mode 100644 index 0000000000000..856d4d38d36bb --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SizeOpt.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; + +namespace System.Text.Unicode +{ + internal static unsafe partial class Utf8Utility + { + // On method return, pInputBufferRemaining and pOutputBufferRemaining will both point to where + // the next byte would have been consumed from / the next char would have been written to. + // inputLength in bytes, outputCharsRemaining in chars. + public static OperationStatus TranscodeToUtf16(byte* pInputBuffer, int inputLength, char* pOutputBuffer, int outputCharsRemaining, out byte* pInputBufferRemaining, out char* pOutputBufferRemaining) + { + Debug.Assert(inputLength >= 0, "Input length must not be negative."); + Debug.Assert(pInputBuffer != null || inputLength == 0, "Input length must be zero if input buffer pointer is null."); + + Debug.Assert(outputCharsRemaining >= 0, "Destination length must not be negative."); + Debug.Assert(pOutputBuffer != null || outputCharsRemaining == 0, "Destination length must be zero if destination buffer pointer is null."); + + var input = new ReadOnlySpan(pInputBuffer, inputLength); + var output = new Span(pOutputBuffer, outputCharsRemaining); + + OperationStatus opStatus = OperationStatus.Done; + while (!input.IsEmpty) + { + opStatus = Rune.DecodeFromUtf8(input, out Rune rune, out int bytesConsumedJustNow); + if (opStatus != OperationStatus.Done) { break; } + if (!rune.TryEncodeToUtf16(output, out int charsWrittenJustNow)) { opStatus = OperationStatus.DestinationTooSmall; break; } + input = input.Slice(bytesConsumedJustNow); + output = output.Slice(charsWrittenJustNow); + } + + pInputBufferRemaining = pInputBuffer + inputLength - input.Length; + pOutputBufferRemaining = pOutputBuffer + outputCharsRemaining - output.Length; + + return opStatus; + } + + // On method return, pInputBufferRemaining and pOutputBufferRemaining will both point to where + // the next char would have been consumed from / the next byte would have been written to. + // inputLength in chars, outputBytesRemaining in bytes. + public static OperationStatus TranscodeToUtf8(char* pInputBuffer, int inputLength, byte* pOutputBuffer, int outputBytesRemaining, out char* pInputBufferRemaining, out byte* pOutputBufferRemaining) + { + Debug.Assert(inputLength >= 0, "Input length must not be negative."); + Debug.Assert(pInputBuffer != null || inputLength == 0, "Input length must be zero if input buffer pointer is null."); + + Debug.Assert(outputBytesRemaining >= 0, "Destination length must not be negative."); + Debug.Assert(pOutputBuffer != null || outputBytesRemaining == 0, "Destination length must be zero if destination buffer pointer is null."); + + + var input = new ReadOnlySpan(pInputBuffer, inputLength); + var output = new Span(pOutputBuffer, outputBytesRemaining); + + OperationStatus opStatus = OperationStatus.Done; + while (!input.IsEmpty) + { + opStatus = Rune.DecodeFromUtf16(input, out Rune rune, out int charsConsumedJustNow); + if (opStatus != OperationStatus.Done) { break; } + if (!rune.TryEncodeToUtf8(output, out int bytesWrittenJustNow)) { opStatus = OperationStatus.DestinationTooSmall; break; } + input = input.Slice(charsConsumedJustNow); + output = output.Slice(bytesWrittenJustNow); + } + + pInputBufferRemaining = pInputBuffer + inputLength - input.Length; + pOutputBufferRemaining = pOutputBuffer + outputBytesRemaining - output.Length; + + return opStatus; + } + + // Returns &inputBuffer[inputLength] if the input buffer is valid. + /// + /// Given an input buffer of byte length , + /// returns a pointer to where the first invalid data appears in . + /// + /// + /// Returns a pointer to the end of if the buffer is well-formed. + /// + /// Pointer to Utf8 byte buffer + /// Buffer length in bytes + /// Zero or negative number to be added to the "bytes processed" return value to come up with the total UTF-16 code unit count. + /// Zero or negative number to be added to the "total UTF-16 code unit count" value to come up with the total scalar count. + public static byte* GetPointerToFirstInvalidByte(byte* pInputBuffer, int inputLength, out int utf16CodeUnitCountAdjustment, out int scalarCountAdjustment) + { + Debug.Assert(inputLength >= 0, "Input length must not be negative."); + Debug.Assert(pInputBuffer != null || inputLength == 0, "Input length must be zero if input buffer pointer is null."); + + var input = new ReadOnlySpan(pInputBuffer, inputLength); + int cumulativeUtf16CodeUnitCount = 0; + int cumulativeScalarValueCount = 0; + while (!input.IsEmpty) + { + if (Rune.DecodeFromUtf8(input, out Rune rune, out int bytesConsumed) != OperationStatus.Done) + break; + input = input.Slice(bytesConsumed); + cumulativeUtf16CodeUnitCount += rune.Utf16SequenceLength; + cumulativeScalarValueCount++; + } + + int cumulativeBytesConsumed = inputLength - input.Length; + utf16CodeUnitCountAdjustment = cumulativeUtf16CodeUnitCount - cumulativeBytesConsumed; + scalarCountAdjustment = cumulativeScalarValueCount - cumulativeUtf16CodeUnitCount; + return pInputBuffer + cumulativeBytesConsumed; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Helpers.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Helpers.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Helpers.cs rename to src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Helpers.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Transcoding.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Transcoding.cs similarity index 100% rename from src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Transcoding.cs rename to src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Transcoding.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Validation.cs similarity index 98% rename from src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs rename to src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Validation.cs index 93c404d3245fd..e5424addb26b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.SpeedOpt.Validation.cs @@ -24,6 +24,10 @@ internal static unsafe partial class Utf8Utility /// /// Returns a pointer to the end of if the buffer is well-formed. /// + /// Pointer to Utf8 byte buffer + /// Buffer length in bytes + /// Zero or negative number to be added to the "bytes processed" return value to come up with the total UTF-16 code unit count. + /// Zero or negative number to be added to the "total UTF-16 code unit count" value to come up with the total scalar count. public static byte* GetPointerToFirstInvalidByte(byte* pInputBuffer, int inputLength, out int utf16CodeUnitCountAdjustment, out int scalarCountAdjustment) { Debug.Assert(inputLength >= 0, "Input length must not be negative."); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.WhiteSpace.CoreLib.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.WhiteSpace.CoreLib.cs deleted file mode 100644 index 165ed6b444454..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.WhiteSpace.CoreLib.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using Internal.Runtime.CompilerServices; - -namespace System.Text.Unicode -{ - internal static partial class Utf8Utility - { - /// - /// Returns the index in where the first non-whitespace character - /// appears, or the input length if the data contains only whitespace characters. - /// - public static int GetIndexOfFirstNonWhiteSpaceChar(ReadOnlySpan utf8Data) - { - return (int)GetIndexOfFirstNonWhiteSpaceChar(ref MemoryMarshal.GetReference(utf8Data), (uint)utf8Data.Length); - } - - private static nuint GetIndexOfFirstNonWhiteSpaceChar(ref byte utf8Data, nuint length) - { - // This method is optimized for the case where the input data is ASCII, and if the - // data does need to be trimmed it's likely that only a relatively small number of - // bytes will be trimmed. - - nuint i = 0; - - while (i < length) - { - // Very quick check: see if the byte is in the range [ 21 .. 7F ]. - // If so, we can skip the more expensive logic later in this method. - - if ((sbyte)Unsafe.AddByteOffset(ref utf8Data, i) > (sbyte)0x20) - { - break; - } - - uint possibleAsciiByte = Unsafe.AddByteOffset(ref utf8Data, i); - if (UnicodeUtility.IsAsciiCodePoint(possibleAsciiByte)) - { - // The simple comparison failed. Let's read the actual byte value, - // and if it's ASCII we can delegate to Rune's inlined method - // implementation. - - if (Rune.IsWhiteSpace(Rune.UnsafeCreate(possibleAsciiByte))) - { - i++; - continue; - } - } - else - { - // Not ASCII data. Go back to the slower "decode the entire scalar" - // code path, then compare it against our Unicode tables. - - Rune.DecodeFromUtf8(new ReadOnlySpan(ref utf8Data, (int)length).Slice((int)i), out Rune decodedRune, out int bytesConsumed); - if (Rune.IsWhiteSpace(decodedRune)) - { - i += (uint)bytesConsumed; - continue; - } - } - - break; // If we got here, we saw a non-whitespace subsequence. - } - - return i; - } - - /// - /// Returns the index in where the trailing whitespace sequence - /// begins, or 0 if the data contains only whitespace characters, or the span length if the - /// data does not end with any whitespace characters. - /// - public static int GetIndexOfTrailingWhiteSpaceSequence(ReadOnlySpan utf8Data) - { - return (int)GetIndexOfTrailingWhiteSpaceSequence(ref MemoryMarshal.GetReference(utf8Data), (uint)utf8Data.Length); - } - - private static nuint GetIndexOfTrailingWhiteSpaceSequence(ref byte utf8Data, nuint length) - { - // This method is optimized for the case where the input data is ASCII, and if the - // data does need to be trimmed it's likely that only a relatively small number of - // bytes will be trimmed. - - while (length > 0) - { - // Very quick check: see if the byte is in the range [ 21 .. 7F ]. - // If so, we can skip the more expensive logic later in this method. - - if ((sbyte)Unsafe.Add(ref Unsafe.AddByteOffset(ref utf8Data, length), -1) > (sbyte)0x20) - { - break; - } - - uint possibleAsciiByte = Unsafe.Add(ref Unsafe.AddByteOffset(ref utf8Data, length), -1); - if (UnicodeUtility.IsAsciiCodePoint(possibleAsciiByte)) - { - // The simple comparison failed. Let's read the actual byte value, - // and if it's ASCII we can delegate to Rune's inlined method - // implementation. - - if (Rune.IsWhiteSpace(Rune.UnsafeCreate(possibleAsciiByte))) - { - length--; - continue; - } - } - else - { - // Not ASCII data. Go back to the slower "decode the entire scalar" - // code path, then compare it against our Unicode tables. - - Rune.DecodeLastFromUtf8(new ReadOnlySpan(ref utf8Data, (int)length), out Rune decodedRune, out int bytesConsumed); - if (Rune.IsWhiteSpace(decodedRune)) - { - length -= (uint)bytesConsumed; - continue; - } - } - - break; // If we got here, we saw a non-whitespace subsequence. - } - - return length; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.WhiteSpace.NonCoreLib.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.WhiteSpace.NonCoreLib.cs deleted file mode 100644 index d25d7c5fea580..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.WhiteSpace.NonCoreLib.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Text.Unicode -{ - internal static partial class Utf8Utility - { - /// - /// Returns the index in where the first non-whitespace character - /// appears, or the input length if the data contains only whitespace characters. - /// - public static int GetIndexOfFirstNonWhiteSpaceChar(ReadOnlySpan utf8Data) - { - // This method is optimized for the case where the input data is ASCII, and if the - // data does need to be trimmed it's likely that only a relatively small number of - // bytes will be trimmed. - - int i = 0; - int length = utf8Data.Length; - - while (i < length) - { - // Very quick check: see if the byte is in the range [ 21 .. 7F ]. - // If so, we can skip the more expensive logic later in this method. - - if ((sbyte)utf8Data[i] > (sbyte)0x20) - { - break; - } - - uint possibleAsciiByte = utf8Data[i]; - if (UnicodeUtility.IsAsciiCodePoint(possibleAsciiByte)) - { - // The simple comparison failed. Let's read the actual byte value, - // and if it's ASCII we can delegate to Rune's inlined method - // implementation. - - if (Rune.IsWhiteSpace(new Rune(possibleAsciiByte))) - { - i++; - continue; - } - } - else - { - // Not ASCII data. Go back to the slower "decode the entire scalar" - // code path, then compare it against our Unicode tables. - - Rune.DecodeFromUtf8(utf8Data.Slice(i), out Rune decodedRune, out int bytesConsumed); - if (Rune.IsWhiteSpace(decodedRune)) - { - i += bytesConsumed; - continue; - } - } - - break; // If we got here, we saw a non-whitespace subsequence. - } - - return i; - } - - /// - /// Returns the index in where the trailing whitespace sequence - /// begins, or 0 if the data contains only whitespace characters, or the span length if the - /// data does not end with any whitespace characters. - /// - public static int GetIndexOfTrailingWhiteSpaceSequence(ReadOnlySpan utf8Data) - { - // This method is optimized for the case where the input data is ASCII, and if the - // data does need to be trimmed it's likely that only a relatively small number of - // bytes will be trimmed. - - int length = utf8Data.Length; - - while (length > 0) - { - // Very quick check: see if the byte is in the range [ 21 .. 7F ]. - // If so, we can skip the more expensive logic later in this method. - - if ((sbyte)utf8Data[length - 1] > (sbyte)0x20) - { - break; - } - - uint possibleAsciiByte = utf8Data[length - 1]; - if (UnicodeUtility.IsAsciiCodePoint(possibleAsciiByte)) - { - // The simple comparison failed. Let's read the actual byte value, - // and if it's ASCII we can delegate to Rune's inlined method - // implementation. - - if (Rune.IsWhiteSpace(new Rune(possibleAsciiByte))) - { - length--; - continue; - } - } - else - { - // Not ASCII data. Go back to the slower "decode the entire scalar" - // code path, then compare it against our Unicode tables. - - Rune.DecodeLastFromUtf8(utf8Data.Slice(0, length), out Rune decodedRune, out int bytesConsumed); - if (Rune.IsWhiteSpace(decodedRune)) - { - length -= bytesConsumed; - continue; - } - } - - break; // If we got here, we saw a non-whitespace subsequence. - } - - return length; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs index dd53650dd808b..4caacbf856ccb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/UnicodeDebug.cs @@ -7,6 +7,15 @@ namespace System.Text { internal static class UnicodeDebug { + [Conditional("DEBUG")] + internal static void AssertIsBmpCodePoint(uint codePoint) + { + if (!UnicodeUtility.IsBmpCodePoint(codePoint)) + { + Debug.Fail($"The value {ToHexString(codePoint)} is not a valid BMP code point."); + } + } + [Conditional("DEBUG")] internal static void AssertIsHighSurrogateCodePoint(uint codePoint) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index ebd38dd1afa4d..54a7dc691cc0c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -66,7 +66,7 @@ private static void TimerCallback(object? state) => // separated out into a name /// canceled concurrently. /// /// - public bool IsCancellationRequested => _state >= NotifyingState; + public bool IsCancellationRequested => _state != NotCanceledState; /// A simple helper to determine whether cancellation has finished. internal bool IsCancellationCompleted => _state == NotifyingCompleteState; @@ -365,6 +365,54 @@ private void CancelAfter(uint millisecondsDelay) } } + /// + /// Attempts to reset the to be used for an unrelated operation. + /// + /// + /// true if the has not had cancellation requested and could + /// have its state reset to be reused for a subsequent operation; otherwise, false. + /// + /// + /// is intended to be used by the sole owner of the + /// when it is known that the operation with which the was used has + /// completed, no one else will be attempting to cancel it, and any registrations still remaining are erroneous. + /// Upon a successful reset, such registrations will no longer be notified for any subsequent cancellation of the + /// ; however, if any component still holds a reference to this + /// either directly or indirectly via a + /// handed out from it, polling via their reference will show the current state any time after the reset as + /// it's the same instance. Usage of concurrently with requesting cancellation is not + /// thread-safe and may result in TryReset returning true even if cancellation was already requested and may result + /// in registrations not being invoked as part of the concurrent cancellation request. + /// + public bool TryReset() + { + ThrowIfDisposed(); + + // We can only reset if cancellation has not yet been requested: we never want to allow a CancellationToken + // to transition from canceled to non-canceled. + if (_state == NotCanceledState) + { + // If there is no timer, then we're free to reset. If there is a timer, then we need to first try + // to reset it to be infinite so that it won't fire, and then recognize that it could have already + // fired by the time we successfully changed it, and so check to see whether that's possibly the case. + // If we successfully reset it and it never fired, then we can be sure it won't trigger cancellation. + bool reset = + _timer is not TimerQueueTimer timer || + (timer.Change(Timeout.UnsignedInfinite, Timeout.UnsignedInfinite) && !timer._everQueued); + + if (reset) + { + // We're not canceled and no timer will run to cancel us. + // Clear out all the registrations, and return that we've successfully reset. + Volatile.Read(ref _registrations)?.UnregisterAll(); + return true; + } + } + + // Failed to reset. + return false; + } + /// Releases the resources used by this . /// This method is not thread-safe for any other concurrent calls. public void Dispose() @@ -434,10 +482,7 @@ private void ThrowIfDisposed() { if (_disposed) { - ThrowObjectDisposedException(); - - [DoesNotReturn] - static void ThrowObjectDisposedException() => throw new ObjectDisposedException(null, SR.CancellationTokenSource_Disposed); + ThrowHelper.ThrowObjectDisposedException(ExceptionResource.CancellationTokenSource_Disposed); } } @@ -876,6 +921,25 @@ internal sealed class Registrations /// The associated source. public Registrations(CancellationTokenSource source) => Source = source; + [MethodImpl(MethodImplOptions.AggressiveInlining)] // used in only two places, one of which is a hot path + private void Recycle(CallbackNode node) + { + Debug.Assert(_lock == 1); + + // Clear out the unused node and put it on the singly-linked free list. + // The only field we don't clear out is the associated Registrations, as that's fixed + // throughout the node's lifetime. + node.Id = 0; + node.Callback = null; + node.CallbackState = null; + node.ExecutionContext = null; + node.SynchronizationContext = null; + + node.Prev = null; + node.Next = FreeNodeList; + FreeNodeList = node; + } + /// Unregisters a callback. /// The expected id of the registration. /// The callback node. @@ -925,17 +989,7 @@ public bool Unregister(long id, CallbackNode node) node.Next.Prev = node.Prev; } - // Clear out the now unused node and put it on the singly-linked free list. - // The only field we don't clear out is the associated Source, as that's fixed - // throughout the nodes lifetime. - node.Id = 0; - node.Callback = null; - node.CallbackState = null; - node.ExecutionContext = null; - node.SynchronizationContext = null; - node.Prev = null; - node.Next = FreeNodeList; - FreeNodeList = node; + Recycle(node); return true; } @@ -945,6 +999,30 @@ public bool Unregister(long id, CallbackNode node) } } + /// Moves all registrations to the free list. + public void UnregisterAll() + { + EnterLock(); + try + { + // Null out all callbacks. + CallbackNode? node = Callbacks; + Callbacks = null; + + // Reset and move each node that was in the callbacks list to the free list. + while (node != null) + { + CallbackNode? next = node.Next; + Recycle(node); + node = next; + } + } + finally + { + ExitLock(); + } + } + /// /// Wait for a single callback to complete (or, more specifically, to not be running). /// It is ok to call this method if the callback has already finished. diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Unix.cs index 2cdaeb6bb55c7..eb9b4b171d413 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/EventWaitHandle.Unix.cs @@ -10,7 +10,7 @@ namespace System.Threading { public partial class EventWaitHandle { - private void CreateEventCore(bool initialState, EventResetMode mode, string name, out bool createdNew) + private void CreateEventCore(bool initialState, EventResetMode mode, string? name, out bool createdNew) { if (name != null) throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); @@ -19,7 +19,7 @@ private void CreateEventCore(bool initialState, EventResetMode mode, string name createdNew = true; } - private static OpenExistingResult OpenExistingWorker(string name, out EventWaitHandle result) + private static OpenExistingResult OpenExistingWorker(string name, out EventWaitHandle? result) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs index 92485a329a8f8..4b3dfb3be1cc4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.Unix.cs @@ -44,6 +44,19 @@ private void WaitCore() Interop.Sys.LowLevelMonitor_Wait(_nativeMonitor); } + private bool WaitCore(int timeoutMilliseconds) + { + Debug.Assert(timeoutMilliseconds >= -1); + + if (timeoutMilliseconds < 0) + { + WaitCore(); + return true; + } + + return Interop.Sys.LowLevelMonitor_TimedWait(_nativeMonitor, timeoutMilliseconds); + } + private void Signal_ReleaseCore() { Interop.Sys.LowLevelMonitor_Signal_Release(_nativeMonitor); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs index 6aa1e0cc195fa..c9139eabd3613 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/LowLevelMonitor.cs @@ -90,6 +90,16 @@ public void Wait() SetOwnerThreadToCurrent(); } + public bool Wait(int timeoutMilliseconds) + { + Debug.Assert(timeoutMilliseconds >= -1); + + ResetOwnerThread(); + bool waitResult = WaitCore(timeoutMilliseconds); + SetOwnerThreadToCurrent(); + return waitResult; + } + public void Signal_Release() { ResetOwnerThread(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs index 27f1d17091603..5b1361247ae58 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Mutex.Unix.cs @@ -10,8 +10,9 @@ namespace System.Threading { public sealed partial class Mutex { - private void CreateMutexCore(bool initiallyOwned, string name, out bool createdNew) + private void CreateMutexCore(bool initiallyOwned, string? name, out bool createdNew) { + // See https://github.com/dotnet/runtime/issues/48720 if (name != null) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); @@ -21,7 +22,7 @@ private void CreateMutexCore(bool initiallyOwned, string name, out bool createdN createdNew = true; } - private static OpenExistingResult OpenExistingWorker(string name, out Mutex result) + private static OpenExistingResult OpenExistingWorker(string name, out Mutex? result) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs index ae4cda4813595..aa953ecd6a0af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/NativeRuntimeEventSource.PortableThreadPool.cs @@ -14,7 +14,7 @@ namespace System.Diagnostics.Tracing internal sealed partial class NativeRuntimeEventSource : EventSource { // On Mono|CoreRT, we don't have these keywords defined from the genRuntimeEventSources.py, so we need to manually define them here. - public class Keywords + public static class Keywords { public const EventKeywords ThreadingKeyword = (EventKeywords)0x10000; public const EventKeywords ThreadTransferKeyword = (EventKeywords)0x80000000; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs index 758b47695af1b..fe1eaa37d7577 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Unix.cs @@ -3,7 +3,7 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { private struct CpuUtilizationReader { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs index be5dc808be526..4818512ba8183 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.CpuUtilizationReader.Windows.cs @@ -6,7 +6,7 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { private struct CpuUtilizationReader { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs index 99bad6108fdb3..eba5c66d7e0a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs @@ -8,7 +8,7 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { private static class GateThread { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.Complex.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.Complex.cs index 0622b4b704582..7c0fbc34ceeb0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.Complex.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.Complex.cs @@ -3,9 +3,9 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { - private partial class HillClimbing + private sealed partial class HillClimbing { private struct Complex { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.cs index d34c0dad3dd9b..aead56b38acab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.HillClimbing.cs @@ -6,12 +6,12 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { /// /// Hill climbing algorithm used for determining the number of threads needed for the thread pool. /// - private partial class HillClimbing + private sealed partial class HillClimbing { private const int LogCapacity = 200; private const int DefaultSampleIntervalMsLow = 10; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.ThreadCounts.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.ThreadCounts.cs index 80ef3a5d4ac4c..1c0c121847387 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.ThreadCounts.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.ThreadCounts.cs @@ -6,7 +6,7 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { /// /// Tracks information on the number of threads we want/have in different states in our thread pool. diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs index 59d5b526ff248..bd7d3d857e0ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WaitThread.cs @@ -22,7 +22,7 @@ public CompleteWaitThreadPoolWorkItem(RegisteredWaitHandle registeredWaitHandle, } } - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { /// /// A linked list of s. @@ -136,7 +136,7 @@ private void RemoveWaitThread(WaitThread thread) } } - private class WaitThreadNode + private sealed class WaitThreadNode { public WaitThread Thread { get; } public WaitThreadNode? Next { get; set; } @@ -147,7 +147,7 @@ private class WaitThreadNode /// /// A thread pool wait thread. /// - internal class WaitThread + internal sealed class WaitThread { /// /// The wait handles registered on this wait thread. diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.cs index 190a8ef2e655a..917a1113f5963 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerThread.cs @@ -5,7 +5,7 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { /// /// The worker thread infastructure for the CLR thread pool. diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerTracking.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerTracking.cs index 1bdcf4fe131cf..8c588965add43 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerTracking.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.WorkerTracking.cs @@ -6,7 +6,7 @@ namespace System.Threading { - internal partial class PortableThreadPool + internal sealed partial class PortableThreadPool { private CountsOfThreadsProcessingUserCallbacks _countsOfThreadsProcessingUserCallbacks; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs index f2d7f91d28705..d8767a5158973 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ReaderWriterLockSlim.cs @@ -20,7 +20,7 @@ public enum LockRecursionPolicy // allocate N of these, where N is the maximum number of locks held simultaneously // by that thread. // - internal class ReaderWriterCount + internal sealed class ReaderWriterCount { // Which lock does this object belong to? This is a numeric ID for two reasons: // 1) We don't want this field to keep the lock object alive, and a WeakReference would diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Semaphore.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Semaphore.Unix.cs index b2184b6af4ade..61975a86ebb3f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Semaphore.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Semaphore.Unix.cs @@ -10,7 +10,7 @@ namespace System.Threading { public sealed partial class Semaphore { - private void CreateSemaphoreCore(int initialCount, int maximumCount, string name, out bool createdNew) + private void CreateSemaphoreCore(int initialCount, int maximumCount, string? name, out bool createdNew) { if (name != null) { @@ -21,7 +21,7 @@ private void CreateSemaphoreCore(int initialCount, int maximumCount, string name createdNew = true; } - private static OpenExistingResult OpenExistingWorker(string name, out Semaphore result) + private static OpenExistingResult OpenExistingWorker(string name, out Semaphore? result) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_NamedSynchronizationPrimitives); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs index 171a44f568b93..dc486327717cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/SpinLock.cs @@ -582,9 +582,9 @@ public bool IsHeldByCurrentThread #region Debugger proxy class /// - /// Internal class used by debug type proxy attribute to display the owner thread ID + /// internal class used by debug type proxy attribute to display the owner thread ID /// - internal class SystemThreading_SpinLockDebugView + internal sealed class SystemThreading_SpinLockDebugView { // SpinLock object private SpinLock _spinLock; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs index f468d0bc7a8f2..f2dd5b2e37efa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs @@ -1394,7 +1394,7 @@ internal Task ContinueWith(Func, object?, } // Proxy class for better debugging experience - internal class SystemThreadingTasks_FutureDebugView + internal sealed class SystemThreadingTasks_FutureDebugView { private readonly Task m_task; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index a487e57183696..18a7b33f65679 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -224,7 +224,7 @@ internal static void RemoveFromActiveTasks(Task task) // need to be accessed during the life cycle of a Task, so we don't want to instantiate them every time. Once // one of these properties needs to be written, we will instantiate a ContingentProperties object and set // the appropriate property. - internal class ContingentProperties + internal sealed class ContingentProperties { // Additional context @@ -6487,7 +6487,7 @@ void IThreadPoolWorkItem.Execute() } // Proxy class for better debugging experience - internal class SystemThreadingTasks_TaskDebugView + internal sealed class SystemThreadingTasks_TaskDebugView { private readonly Task m_task; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskExceptionHolder.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskExceptionHolder.cs index 3c3e8092a4217..a3277c58cac00 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskExceptionHolder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskExceptionHolder.cs @@ -24,7 +24,7 @@ namespace System.Threading.Tasks /// is ever GC'd without the holder's contents ever having been requested /// (e.g. by a Task.Wait, Task.get_Exception, etc). /// - internal class TaskExceptionHolder + internal sealed class TaskExceptionHolder { /// The task with which this holder is associated. private readonly Task m_task; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs index be10b19c2a5bf..3118072c75723 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs @@ -56,8 +56,8 @@ namespace System.Threading.Tasks [StructLayout(LayoutKind.Auto)] public readonly struct ValueTask : IEquatable { - /// A task canceled using `new CancellationToken(true)`. - private static readonly Task s_canceledTask = Task.FromCanceled(new CancellationToken(canceled: true)); + /// A task canceled using `new CancellationToken(true)`. Lazily created only when first needed. + private static volatile Task? s_canceledTask; /// null if representing a successful synchronous completion, otherwise a or a . internal readonly object? _obj; @@ -216,7 +216,8 @@ private Task GetTaskForValueTaskSource(IValueTaskSource t) return task; } - return s_canceledTask; + // Benign race condition to initialize cached task, as identity doesn't matter. + return s_canceledTask ??= Task.FromCanceled(new CancellationToken(canceled: true)); } else { @@ -441,7 +442,7 @@ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContex public readonly struct ValueTask : IEquatable> { /// A task canceled using `new CancellationToken(true)`. Lazily created only when first needed. - private static Task? s_canceledTask; + private static volatile Task? s_canceledTask; /// null if has the result, otherwise a or a . internal readonly object? _obj; /// The result to be used if the operation completed successfully synchronously. @@ -602,13 +603,8 @@ private Task GetTaskForValueTaskSource(IValueTaskSource t) return task; } - Task? canceledTask = s_canceledTask; - if (canceledTask == null) - { - // Benign race condition to initialize cached task, as identity doesn't matter. - s_canceledTask = canceledTask = Task.FromCanceled(new CancellationToken(true)); - } - return canceledTask; + // Benign race condition to initialize cached task, as identity doesn't matter. + return s_canceledTask ??= Task.FromCanceled(new CancellationToken(true)); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs new file mode 100644 index 0000000000000..647282ece364e --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Unix.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public sealed partial class Thread + { + internal static void UninterruptibleSleep0() => WaitSubsystem.UninterruptibleSleep0(); + + private static void SleepInternal(int millisecondsTimeout) => WaitSubsystem.Sleep(millisecondsTimeout); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs new file mode 100644 index 0000000000000..573ecc9a4e65b --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.Windows.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Win32.SafeHandles; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public sealed partial class Thread + { + internal static void UninterruptibleSleep0() => Interop.Kernel32.Sleep(0); + + private static void SleepInternal(int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); + Interop.Kernel32.Sleep((uint)millisecondsTimeout); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs index 5876fe48882b1..5baaa386b3474 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Thread.cs @@ -334,6 +334,17 @@ public static Thread CurrentThread } } + [MethodImpl(MethodImplOptions.NoInlining)] // Slow path method. Make sure that the caller frame does not pay for PInvoke overhead. + public static void Sleep(int millisecondsTimeout) + { + if (millisecondsTimeout < Timeout.Infinite) + throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout), millisecondsTimeout, SR.ArgumentOutOfRange_NeedNonNegOrNegative1); + SleepInternal(millisecondsTimeout); + } + + /// Returns the operating system identifier for the current thread. + internal static ulong CurrentOSThreadId => GetCurrentOSThreadId(); + public ExecutionContext? ExecutionContext => ExecutionContext.Capture(); public string? Name diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs index cf80323641199..0da194e8ac1fa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/ThreadLocal.cs @@ -671,7 +671,7 @@ internal LinkedSlot(LinkedSlotVolatile[]? slotArray) /// /// A manager class that assigns IDs to ThreadLocal instances /// - private class IdManager + private sealed class IdManager { // The next ID to try private int _nextIdToTry; @@ -728,7 +728,7 @@ internal void ReturnId(int id) /// (all those LinkedSlot instances can be found by following references from the table slots) and /// releases the table so that it can get GC'd. /// - private class FinalizationHelper + private sealed class FinalizationHelper { internal LinkedSlotVolatile[] SlotArray; private readonly bool _trackAllValues; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs index 72ec6a7276a0c..e2766f270c495 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Timer.cs @@ -37,7 +37,7 @@ namespace System.Threading // in order to minimize contention when lots of threads are concurrently creating and destroying timers often. [DebuggerDisplay("Count = {CountForDebugger}")] [DebuggerTypeProxy(typeof(TimerQueueDebuggerTypeProxy))] - internal partial class TimerQueue + internal sealed partial class TimerQueue { #region Shared TimerQueue instances /// Mapping from a tick count to a time to use when debugging to translate tick count values. @@ -206,6 +206,7 @@ private void FireNextTimers() if (remaining <= 0) { // Timer is ready to fire. + timer._everQueued = true; if (timer._period != Timeout.UnsignedInfinite) { @@ -476,6 +477,7 @@ internal sealed partial class TimerQueueTimer : IThreadPoolWorkItem // instead of with a provided WaitHandle. private int _callbacksRunning; private bool _canceled; + internal bool _everQueued; private object? _notifyWhenNoCallbacksRunning; // may be either WaitHandle or Task internal TimerQueueTimer(TimerCallback timerCallback, object? state, uint dueTime, uint period, bool flowExecutionContext) diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs index 40c84e4ad0fce..c1e8918803e7e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs @@ -9,7 +9,7 @@ namespace System.Threading // // Unix-specific implementation of Timer // - internal partial class TimerQueue : IThreadPoolWorkItem + internal sealed partial class TimerQueue : IThreadPoolWorkItem { private static List? s_scheduledTimers; private static List? s_scheduledTimersToFire; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs index efc16224062b5..005fa12ca6f23 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Unix.cs @@ -3,7 +3,7 @@ namespace System.Threading { - internal partial class TimerQueue + internal sealed partial class TimerQueue { private static long TickCount64 => Environment.TickCount64; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs index 3d70db05dfd40..228cae763e262 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Windows.cs @@ -5,7 +5,7 @@ namespace System.Threading { - internal partial class TimerQueue + internal sealed partial class TimerQueue { private static long TickCount64 { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs new file mode 100644 index 0000000000000..af3a8a38461c8 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @@ -0,0 +1,173 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.IO; +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System.Threading +{ + public abstract partial class WaitHandle + { + internal static unsafe int WaitMultipleIgnoringSyncContext(Span handles, bool waitAll, int millisecondsTimeout) + { + fixed (IntPtr* pHandles = &MemoryMarshal.GetReference(handles)) + { + return WaitForMultipleObjectsIgnoringSyncContext(pHandles, handles.Length, waitAll, millisecondsTimeout); + } + } + + private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr* pHandles, int numHandles, bool waitAll, int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); + + // Normalize waitAll + if (numHandles == 1) + waitAll = false; + +#if CORERT // TODO: reentrant wait support https://github.com/dotnet/runtime/issues/49518 + bool reentrantWait = Thread.ReentrantWaitsEnabled; + + if (reentrantWait) + { + // + // In the CLR, we use CoWaitForMultipleHandles to pump messages while waiting in an STA. In that case, we cannot use WAIT_ALL. + // That's because the wait would only be satisfied if a message arrives while the handles are signalled. + // + if (waitAll) + throw new NotSupportedException(SR.NotSupported_WaitAllSTAThread); + + // CoWaitForMultipleHandles does not support more than 63 handles. It returns RPC_S_CALLPENDING for more than 63 handles + // that is impossible to differentiate from timeout. + if (numHandles > 63) + throw new NotSupportedException(SR.NotSupported_MaxWaitHandles_STA); + } +#endif + + Thread currentThread = Thread.CurrentThread; + currentThread.SetWaitSleepJoinState(); + +#if CORERT + int result; + if (reentrantWait) + { + Debug.Assert(!waitAll); + result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); + } + else + { + result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + } +#else + int result = (int)Interop.Kernel32.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (uint)millisecondsTimeout, Interop.BOOL.FALSE); +#endif + currentThread.ClearWaitSleepJoinState(); + + if (result == Interop.Kernel32.WAIT_FAILED) + { + int errorCode = Interop.Kernel32.GetLastError(); + if (waitAll && errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) + { + // Check for duplicate handles. This is a brute force O(n^2) search, which is intended since the typical + // array length is short enough that this would actually be faster than using a hash set. Also, the worst + // case is not so bad considering that the array length is limited by + // . + for (int i = 1; i < numHandles; ++i) + { + IntPtr handle = pHandles[i]; + for (int j = 0; j < i; ++j) + { + if (pHandles[j] == handle) + { + throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); + } + } + } + } + + ThrowWaitFailedException(errorCode); + } + + return result; + } + + internal static unsafe int WaitOneCore(IntPtr handle, int millisecondsTimeout) + { + return WaitForMultipleObjectsIgnoringSyncContext(&handle, 1, false, millisecondsTimeout); + } + + private static int SignalAndWaitCore(IntPtr handleToSignal, IntPtr handleToWaitOn, int millisecondsTimeout) + { + Debug.Assert(millisecondsTimeout >= -1); + + int ret = (int)Interop.Kernel32.SignalObjectAndWait(handleToSignal, handleToWaitOn, (uint)millisecondsTimeout, Interop.BOOL.FALSE); + + if (ret == Interop.Kernel32.WAIT_FAILED) + { + ThrowWaitFailedException(Interop.Kernel32.GetLastError()); + } + + return ret; + } + + private static void ThrowWaitFailedException(int errorCode) + { + switch (errorCode) + { + case Interop.Errors.ERROR_INVALID_HANDLE: + ThrowInvalidHandleException(); + return; + + case Interop.Errors.ERROR_INVALID_PARAMETER: + throw new ArgumentException(); + + case Interop.Errors.ERROR_ACCESS_DENIED: + throw new UnauthorizedAccessException(); + + case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY: + throw new OutOfMemoryException(); + + case Interop.Errors.ERROR_TOO_MANY_POSTS: + // Only applicable to . Note however, that + // if the semahpore already has the maximum signal count, the Windows SignalObjectAndWait function does not + // return an error, but this code is kept for historical reasons and to convey the intent, since ideally, + // that should be an error. + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts); + + case Interop.Errors.ERROR_NOT_OWNER: + // Only applicable to when signaling a mutex + // that is locked by a different thread. Note that if the mutex is already unlocked, the Windows + // SignalObjectAndWait function does not return an error. + throw new ApplicationException(SR.Arg_SynchronizationLockException); + + case Interop.Errors.ERROR_MUTANT_LIMIT_EXCEEDED: + throw new OverflowException(SR.Overflow_MutexReacquireCount); + + default: + throw new Exception { HResult = errorCode }; + } + } + + internal static Exception ExceptionFromCreationError(int errorCode, string path) + { + switch (errorCode) + { + case Interop.Errors.ERROR_PATH_NOT_FOUND: + return new IOException(SR.Format(SR.IO_PathNotFound_Path, path)); + + case Interop.Errors.ERROR_ACCESS_DENIED: + return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path)); + + case Interop.Errors.ERROR_ALREADY_EXISTS: + return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path)); + + case Interop.Errors.ERROR_FILENAME_EXCED_RANGE: + return new PathTooLongException(); + + default: + return new IOException(SR.Arg_IOException, errorCode); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs index 8311d9dadee04..c82450488734a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @@ -409,6 +409,13 @@ private static bool SignalAndWait(WaitHandle toSignal, WaitHandle toWaitOn, int } } + internal static void ThrowInvalidHandleException() + { + var ex = new InvalidOperationException(SR.InvalidOperation_InvalidHandle); + ex.HResult = HResults.E_HANDLE; + throw ex; + } + public virtual bool WaitOne(TimeSpan timeout) => WaitOneNoCheck(ToTimeoutMilliseconds(timeout)); public virtual bool WaitOne() => WaitOneNoCheck(-1); public virtual bool WaitOne(int millisecondsTimeout, bool exitContext) => WaitOne(millisecondsTimeout); @@ -429,6 +436,8 @@ public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout) => WaitMultiple(waitHandles, false, millisecondsTimeout); internal static int WaitAny(ReadOnlySpan safeWaitHandles, int millisecondsTimeout) => WaitAnyMultiple(safeWaitHandles, millisecondsTimeout); + internal static int WaitAny(ReadOnlySpan waitHandles, int millisecondsTimeout) => + WaitMultiple(waitHandles, false, millisecondsTimeout); public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout) => WaitMultiple(waitHandles, false, ToTimeoutMilliseconds(timeout)); public static int WaitAny(WaitHandle[] waitHandles) => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.HandleManager.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.HandleManager.Unix.cs index dc922c465aa40..1ef3fabfde518 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.HandleManager.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.HandleManager.Unix.cs @@ -16,7 +16,7 @@ public static IntPtr NewHandle(WaitableObject waitableObject) { Debug.Assert(waitableObject != null); - IntPtr handle = RuntimeImports.RhHandleAlloc(waitableObject, GCHandleType.Normal); + IntPtr handle = GCHandle.ToIntPtr(GCHandle.Alloc(waitableObject, GCHandleType.Normal)); // SafeWaitHandle treats -1 and 0 as invalid, and the handle should not be these values anyway Debug.Assert(handle != IntPtr.Zero); @@ -33,7 +33,7 @@ public static WaitableObject FromHandle(IntPtr handle) // We don't know if any other handles are invalid, and this may crash or otherwise do bad things, that is by // design, IntPtr is unsafe by nature. - return (WaitableObject)RuntimeImports.RhHandleGet(handle); + return (WaitableObject)GCHandle.FromIntPtr(handle).Target!; } /// @@ -48,8 +48,8 @@ public static void DeleteHandle(IntPtr handle) // We don't know if any other handles are invalid, and this may crash or otherwise do bad things, that is by // design, IntPtr is unsafe by nature. - ((WaitableObject)RuntimeImports.RhHandleGet(handle)).OnDeleteHandle(); - RuntimeImports.RhHandleFree(handle); + FromHandle(handle).OnDeleteHandle(); + GCHandle.FromIntPtr(handle).Free(); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs index 92c67fcbfb676..58f61e4cd2ed7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.ThreadWaitInfo.Unix.cs @@ -53,7 +53,7 @@ public sealed class ThreadWaitInfo /// - The filled count is /// - Indexes in all arrays that use correspond /// - private WaitableObject[] _waitedObjects; + private WaitableObject?[] _waitedObjects; /// /// - Nodes used for registering a thread's wait on each , in the @@ -84,7 +84,7 @@ public sealed class ThreadWaitInfo /// the thread exits. The linked list has only a head and no tail, which means acquired mutexes are prepended and /// mutexes are abandoned in reverse order. /// - private WaitableObject _lockedMutexesHead; + private WaitableObject? _lockedMutexesHead; public ThreadWaitInfo(Thread thread) { @@ -120,7 +120,7 @@ private bool IsWaiting /// Callers must ensure to clear the array after use. Once is called (followed /// by a call to , the array will be cleared automatically. /// - public WaitableObject[] GetWaitedObjectArray(int requiredCapacity) + public WaitableObject?[] GetWaitedObjectArray(int requiredCapacity) { Debug.Assert(_thread == Thread.CurrentThread); Debug.Assert(_waitedCount == 0); @@ -174,7 +174,7 @@ public void RegisterWait(int waitedCount, bool prioritize, bool isWaitForAll) Debug.Assert(_waitedCount == 0); - WaitableObject[] waitedObjects = _waitedObjects; + WaitableObject?[] waitedObjects = _waitedObjects; #if DEBUG for (int i = 0; i < waitedCount; ++i) { @@ -212,14 +212,14 @@ public void RegisterWait(int waitedCount, bool prioritize, bool isWaitForAll) { for (int i = 0; i < waitedCount; ++i) { - waitedListNodes[i].RegisterPrioritizedWait(waitedObjects[i]); + waitedListNodes[i].RegisterPrioritizedWait(waitedObjects[i]!); } } else { for (int i = 0; i < waitedCount; ++i) { - waitedListNodes[i].RegisterWait(waitedObjects[i]); + waitedListNodes[i].RegisterWait(waitedObjects[i]!); } } } @@ -231,7 +231,7 @@ public void UnregisterWait() for (int i = 0; i < _waitedCount; ++i) { - _waitedListNodes[i].UnregisterWait(_waitedObjects[i]); + _waitedListNodes[i].UnregisterWait(_waitedObjects[i]!); _waitedObjects[i] = null; } _waitedCount = 0; @@ -538,7 +538,7 @@ private bool CheckAndResetPendingInterrupt_NotLocked } } - public WaitableObject LockedMutexesHead + public WaitableObject? LockedMutexesHead { get { @@ -561,7 +561,7 @@ public void OnThreadExiting() { while (true) { - WaitableObject waitableObject = LockedMutexesHead; + WaitableObject? waitableObject = LockedMutexesHead; if (waitableObject == null) { break; @@ -593,7 +593,7 @@ public sealed class WaitedListNode /// /// Link in the linked list /// - private WaitedListNode _previous, _next; + private WaitedListNode? _previous, _next; public WaitedListNode(ThreadWaitInfo waitInfo, int waitedObjectIndex) { @@ -623,7 +623,7 @@ public int WaitedObjectIndex } } - public WaitedListNode Previous + public WaitedListNode? Previous { get { @@ -632,7 +632,7 @@ public WaitedListNode Previous } } - public WaitedListNode Next + public WaitedListNode? Next { get { @@ -651,7 +651,7 @@ public void RegisterWait(WaitableObject waitableObject) Debug.Assert(_previous == null); Debug.Assert(_next == null); - WaitedListNode tail = waitableObject.WaitersTail; + WaitedListNode? tail = waitableObject.WaitersTail; if (tail != null) { _previous = tail; @@ -674,7 +674,7 @@ public void RegisterPrioritizedWait(WaitableObject waitableObject) Debug.Assert(_previous == null); Debug.Assert(_next == null); - WaitedListNode head = waitableObject.WaitersHead; + WaitedListNode? head = waitableObject.WaitersHead; if (head != null) { _next = head; @@ -692,8 +692,8 @@ public void UnregisterWait(WaitableObject waitableObject) s_lock.VerifyIsLocked(); Debug.Assert(waitableObject != null); - WaitedListNode previous = _previous; - WaitedListNode next = _next; + WaitedListNode? previous = _previous; + WaitedListNode? next = _next; if (previous != null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs index 91d21a140c9de..e259f3854015e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.Unix.cs @@ -113,7 +113,9 @@ namespace System.Threading /// - Since provides mutual exclusion for the states of all s in the /// process, any operation that does not involve waiting or releasing a wait can occur with minimal p/invokes /// +#if CORERT [EagerStaticClassConstruction] // the wait subsystem is used during lazy class construction +#endif internal static partial class WaitSubsystem { private static readonly LowLevelLock s_lock = new LowLevelLock(); @@ -121,7 +123,7 @@ internal static partial class WaitSubsystem private static SafeWaitHandle NewHandle(WaitableObject waitableObject) { IntPtr handle = HandleManager.NewHandle(waitableObject); - SafeWaitHandle safeWaitHandle = null; + SafeWaitHandle? safeWaitHandle = null; try { safeWaitHandle = new SafeWaitHandle(handle, ownsHandle: true); @@ -280,7 +282,7 @@ public static int Wait( Debug.Assert(timeoutMilliseconds >= -1); ThreadWaitInfo waitInfo = Thread.CurrentThread.WaitInfo; - WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(waitHandles.Length); + WaitableObject?[] waitableObjects = waitInfo.GetWaitedObjectArray(waitHandles.Length); bool success = false; try { @@ -320,7 +322,7 @@ public static int Wait( if (waitHandles.Length == 1) { - WaitableObject waitableObject = waitableObjects[0]; + WaitableObject waitableObject = waitableObjects[0]!; waitableObjects[0] = null; return waitableObject.Wait(waitInfo, timeoutMilliseconds, interruptible: true, prioritize : false); @@ -373,7 +375,14 @@ public static int SignalAndWait( throw new ThreadInterruptedException(); } - waitableObjectToSignal.Signal(1); + try + { + waitableObjectToSignal.Signal(1); + } + catch (SemaphoreFullException ex) + { + throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts, ex); + } waitCalled = true; return waitableObjectToWaitOn.Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize); } @@ -415,10 +424,5 @@ public static void Interrupt(Thread thread) s_lock.Release(); } } - - public static void OnThreadExiting(Thread thread) - { - thread.WaitInfo.OnThreadExiting(); - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs index 8963e7b04415d..f5f37471440f7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/WaitSubsystem.WaitableObject.Unix.cs @@ -22,18 +22,18 @@ public sealed class WaitableObject /// Only has a thread ownership requirement, and it's a less common type to be used, so /// ownership info is separated to reduce the size. For other types, this is null. /// - private readonly OwnershipInfo _ownershipInfo; + private readonly OwnershipInfo? _ownershipInfo; /// /// Linked list of information about waiting threads /// - private ThreadWaitInfo.WaitedListNode _waitersHead, _waitersTail; + private ThreadWaitInfo.WaitedListNode? _waitersHead, _waitersTail; private WaitableObject( WaitableObjectType type, int initialSignalCount, int maximumSignalCount, - OwnershipInfo ownershipInfo) + OwnershipInfo? ownershipInfo) { Debug.Assert(initialSignalCount >= 0); Debug.Assert(maximumSignalCount > 0); @@ -157,11 +157,11 @@ private bool IsAbandonedMutex get { s_lock.VerifyIsLocked(); - return IsMutex && _ownershipInfo.IsAbandoned; + return IsMutex && _ownershipInfo != null && _ownershipInfo.IsAbandoned; } } - public ThreadWaitInfo.WaitedListNode WaitersHead + public ThreadWaitInfo.WaitedListNode? WaitersHead { get { @@ -175,7 +175,7 @@ public ThreadWaitInfo.WaitedListNode WaitersHead } } - public ThreadWaitInfo.WaitedListNode WaitersTail + public ThreadWaitInfo.WaitedListNode? WaitersTail { get { @@ -196,7 +196,7 @@ private bool IsSignaled s_lock.VerifyIsLocked(); bool isSignaled = _signalCount != 0; - Debug.Assert(isSignaled || !IsMutex || _ownershipInfo.Thread != null); + Debug.Assert(isSignaled || !IsMutex || (_ownershipInfo != null && _ownershipInfo.Thread != null)); return isSignaled; } } @@ -220,7 +220,7 @@ private void AcceptSignal(ThreadWaitInfo waitInfo) Debug.Assert(_type == WaitableObjectType.Mutex); --_signalCount; - Debug.Assert(_ownershipInfo.Thread == null); + Debug.Assert(_ownershipInfo!.Thread == null); _ownershipInfo.AssignOwnership(this, waitInfo); return; } @@ -267,7 +267,7 @@ public int Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool in return isAbandoned ? WaitHandle.WaitAbandoned : WaitHandle.WaitSuccess; } - if (IsMutex && _ownershipInfo.Thread == waitInfo.Thread) + if (IsMutex && _ownershipInfo != null && _ownershipInfo.Thread == waitInfo.Thread) { if (!_ownershipInfo.CanIncrementReacquireCount) { @@ -282,7 +282,7 @@ public int Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool in return WaitHandle.WaitTimeout; } - WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(1); + WaitableObject?[] waitableObjects = waitInfo.GetWaitedObjectArray(1); waitableObjects[0] = this; waitInfo.RegisterWait(1, prioritize, isWaitForAll: false); needToWait = true; @@ -304,7 +304,7 @@ public int Wait_Locked(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool in } public static int Wait( - WaitableObject[] waitableObjects, + WaitableObject?[]? waitableObjects, int count, bool waitForAll, ThreadWaitInfo waitInfo, @@ -335,7 +335,7 @@ public static int Wait( // Check if any is already signaled for (int i = 0; i < count; ++i) { - WaitableObject waitableObject = waitableObjects[i]; + WaitableObject waitableObject = waitableObjects[i]!; Debug.Assert(waitableObject != null); if (waitableObject.IsSignaled) @@ -351,7 +351,7 @@ public static int Wait( if (waitableObject.IsMutex) { - OwnershipInfo ownershipInfo = waitableObject._ownershipInfo; + OwnershipInfo ownershipInfo = waitableObject._ownershipInfo!; if (ownershipInfo.Thread == waitInfo.Thread) { if (!ownershipInfo.CanIncrementReacquireCount) @@ -371,7 +371,7 @@ public static int Wait( bool isAnyAbandonedMutex = false; for (int i = 0; i < count; ++i) { - WaitableObject waitableObject = waitableObjects[i]; + WaitableObject waitableObject = waitableObjects[i]!; Debug.Assert(waitableObject != null); if (waitableObject.IsSignaled) @@ -385,7 +385,7 @@ public static int Wait( if (waitableObject.IsMutex) { - OwnershipInfo ownershipInfo = waitableObject._ownershipInfo; + OwnershipInfo ownershipInfo = waitableObject._ownershipInfo!; if (ownershipInfo.Thread == waitInfo.Thread) { if (!ownershipInfo.CanIncrementReacquireCount) @@ -404,7 +404,7 @@ public static int Wait( { for (int i = 0; i < count; ++i) { - WaitableObject waitableObject = waitableObjects[i]; + WaitableObject waitableObject = waitableObjects[i]!; if (waitableObject.IsSignaled) { waitableObject.AcceptSignal(waitInfo); @@ -412,7 +412,7 @@ public static int Wait( } Debug.Assert(waitableObject.IsMutex); - OwnershipInfo ownershipInfo = waitableObject._ownershipInfo; + OwnershipInfo ownershipInfo = waitableObject._ownershipInfo!; Debug.Assert(ownershipInfo.Thread == waitInfo.Thread); ownershipInfo.IncrementReacquireCount(); } @@ -456,7 +456,7 @@ public static int Wait( public static bool WouldWaitForAllBeSatisfiedOrAborted( Thread waitingThread, - WaitableObject[] waitedObjects, + WaitableObject?[] waitedObjects, int waitedCount, int signaledWaitedObjectIndex, ref bool wouldAnyMutexReacquireCountOverflow, @@ -480,7 +480,7 @@ public static bool WouldWaitForAllBeSatisfiedOrAborted( continue; } - WaitableObject waitedObject = waitedObjects[i]; + WaitableObject waitedObject = waitedObjects[i]!; if (waitedObject.IsSignaled) { if (!isAnyAbandonedMutex && waitedObject.IsAbandonedMutex) @@ -492,7 +492,7 @@ public static bool WouldWaitForAllBeSatisfiedOrAborted( if (waitedObject.IsMutex) { - OwnershipInfo ownershipInfo = waitedObject._ownershipInfo; + OwnershipInfo ownershipInfo = waitedObject._ownershipInfo!; if (ownershipInfo.Thread == waitingThread) { if (!ownershipInfo.CanIncrementReacquireCount) @@ -513,7 +513,7 @@ public static bool WouldWaitForAllBeSatisfiedOrAborted( public static void SatisfyWaitForAll( ThreadWaitInfo waitInfo, - WaitableObject[] waitedObjects, + WaitableObject?[] waitedObjects, int waitedCount, int signaledWaitedObjectIndex) { @@ -534,7 +534,7 @@ public static void SatisfyWaitForAll( continue; } - WaitableObject waitedObject = waitedObjects[i]; + WaitableObject waitedObject = waitedObjects[i]!; if (waitedObject.IsSignaled) { waitedObject.AcceptSignal(waitInfo); @@ -542,7 +542,7 @@ public static void SatisfyWaitForAll( } Debug.Assert(waitedObject.IsMutex); - OwnershipInfo ownershipInfo = waitedObject._ownershipInfo; + OwnershipInfo ownershipInfo = waitedObject._ownershipInfo!; Debug.Assert(ownershipInfo.Thread == waitInfo.Thread); ownershipInfo.IncrementReacquireCount(); } @@ -606,7 +606,7 @@ private void SignalManualResetEvent() return; } - for (ThreadWaitInfo.WaitedListNode waiterNode = _waitersHead, nextWaiterNode; + for (ThreadWaitInfo.WaitedListNode? waiterNode = _waitersHead, nextWaiterNode; waiterNode != null; waiterNode = nextWaiterNode) { @@ -629,7 +629,7 @@ private void SignalAutoResetEvent() return; } - for (ThreadWaitInfo.WaitedListNode waiterNode = _waitersHead, nextWaiterNode; + for (ThreadWaitInfo.WaitedListNode? waiterNode = _waitersHead, nextWaiterNode; waiterNode != null; waiterNode = nextWaiterNode) { @@ -684,7 +684,7 @@ public int SignalSemaphore(int count) return oldSignalCount; } - for (ThreadWaitInfo.WaitedListNode waiterNode = _waitersHead, nextWaiterNode; + for (ThreadWaitInfo.WaitedListNode? waiterNode = _waitersHead, nextWaiterNode; waiterNode != null; waiterNode = nextWaiterNode) { @@ -710,12 +710,12 @@ public void SignalMutex() WaitHandle.ThrowInvalidHandleException(); } - if (IsSignaled || _ownershipInfo.Thread != Thread.CurrentThread) + if (IsSignaled || _ownershipInfo!.Thread != Thread.CurrentThread) { throw new ApplicationException(SR.Arg_SynchronizationLockException); } - if (!_ownershipInfo.TryDecrementReacquireCount()) + if (!_ownershipInfo!.TryDecrementReacquireCount()) { SignalMutex(isAbandoned: false); } @@ -743,10 +743,11 @@ private void SignalMutex(bool isAbandoned) Debug.Assert(IsMutex); Debug.Assert(!IsSignaled); + Debug.Assert(_ownershipInfo != null); _ownershipInfo.RelinquishOwnership(this, isAbandoned); - for (ThreadWaitInfo.WaitedListNode waiterNode = _waitersHead, nextWaiterNode; + for (ThreadWaitInfo.WaitedListNode? waiterNode = _waitersHead, nextWaiterNode; waiterNode != null; waiterNode = nextWaiterNode) { @@ -767,21 +768,21 @@ private void SignalMutex(bool isAbandoned) private sealed class OwnershipInfo { - private Thread _thread; + private Thread? _thread; private int _reacquireCount; private bool _isAbandoned; /// /// Link in the linked list /// - private WaitableObject _previous; + private WaitableObject? _previous; /// /// Link in the linked list /// - private WaitableObject _next; + private WaitableObject? _next; - public Thread Thread + public Thread? Thread { get { @@ -815,11 +816,11 @@ public void AssignOwnership(WaitableObject waitableObject, ThreadWaitInfo waitIn _thread = waitInfo.Thread; _isAbandoned = false; - WaitableObject head = waitInfo.LockedMutexesHead; + WaitableObject? head = waitInfo.LockedMutexesHead; if (head != null) { _next = head; - head._ownershipInfo._previous = waitableObject; + head._ownershipInfo!._previous = waitableObject; } waitInfo.LockedMutexesHead = waitableObject; } @@ -844,12 +845,12 @@ public void RelinquishOwnership(WaitableObject waitableObject, bool isAbandoned) _isAbandoned = isAbandoned; } - WaitableObject previous = _previous; - WaitableObject next = _next; + WaitableObject? previous = _previous; + WaitableObject? next = _next; if (previous != null) { - previous._ownershipInfo._next = next; + previous._ownershipInfo!._next = next; _previous = null; } else @@ -860,7 +861,7 @@ public void RelinquishOwnership(WaitableObject waitableObject, bool isAbandoned) if (next != null) { - next._ownershipInfo._previous = previous; + next._ownershipInfo!._previous = previous; _next = null; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index a5f2bcfb7b155..117e60fdd0617 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. -// This file defines an internal class used to throw exceptions in BCL code. +// This file defines an internal static class used to throw exceptions in BCL code. // The main purpose is to reduce code size. // // The old way to throw an exception generates quite a lot IL code and assembly code. @@ -38,6 +38,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Runtime.CompilerServices; using System.Runtime.Serialization; @@ -218,15 +219,16 @@ internal static void ThrowArgumentException(ExceptionResource resource, Exceptio throw GetArgumentException(resource, argument); } - private static ArgumentNullException GetArgumentNullException(ExceptionArgument argument) + [DoesNotReturn] + internal static void ThrowArgumentException_HandleNotSync(string paramName) { - return new ArgumentNullException(GetArgumentName(argument)); + throw new ArgumentException(SR.Arg_HandleNotSync, paramName); } [DoesNotReturn] internal static void ThrowArgumentNullException(ExceptionArgument argument) { - throw GetArgumentNullException(argument); + throw new ArgumentNullException(GetArgumentName(argument)); } [DoesNotReturn] @@ -259,6 +261,12 @@ internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument throw GetArgumentOutOfRangeException(argument, paramNumber, resource); } + [DoesNotReturn] + internal static void ThrowEndOfFileException() + { + throw new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF); + } + [DoesNotReturn] internal static void ThrowInvalidOperationException() { @@ -307,6 +315,24 @@ internal static void ThrowNotSupportedException(ExceptionResource resource) throw new NotSupportedException(GetResourceString(resource)); } + [DoesNotReturn] + internal static void ThrowNotSupportedException_UnseekableStream() + { + throw new NotSupportedException(SR.NotSupported_UnseekableStream); + } + + [DoesNotReturn] + internal static void ThrowNotSupportedException_UnreadableStream() + { + throw new NotSupportedException(SR.NotSupported_UnreadableStream); + } + + [DoesNotReturn] + internal static void ThrowNotSupportedException_UnwritableStream() + { + throw new NotSupportedException(SR.NotSupported_UnwritableStream); + } + [DoesNotReturn] internal static void ThrowUnauthorizedAccessException(ExceptionResource resource) { @@ -319,6 +345,18 @@ internal static void ThrowObjectDisposedException(string objectName, ExceptionRe throw new ObjectDisposedException(objectName, GetResourceString(resource)); } + [DoesNotReturn] + internal static void ThrowObjectDisposedException_StreamClosed(string? objectName) + { + throw new ObjectDisposedException(objectName, SR.ObjectDisposed_StreamClosed); + } + + [DoesNotReturn] + internal static void ThrowObjectDisposedException_FileClosed() + { + throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed); + } + [DoesNotReturn] internal static void ThrowObjectDisposedException(ExceptionResource resource) { @@ -715,6 +753,8 @@ private static string GetArgumentName(ExceptionArgument argument) return "buffer"; case ExceptionArgument.offset: return "offset"; + case ExceptionArgument.stream: + return "stream"; default: Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); return ""; @@ -871,6 +911,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.Argument_SpansMustHaveSameLength; case ExceptionResource.Argument_InvalidFlag: return SR.Argument_InvalidFlag; + case ExceptionResource.CancellationTokenSource_Disposed: + return SR.CancellationTokenSource_Disposed; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -975,6 +1017,7 @@ internal enum ExceptionArgument suffix, buffer, offset, + stream } // @@ -1049,5 +1092,6 @@ internal enum ExceptionResource Arg_TypeNotSupported, Argument_SpansMustHaveSameLength, Argument_InvalidFlag, + CancellationTokenSource_Disposed, } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.AdjustmentRule.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.AdjustmentRule.cs index caac7f0a4b3cd..fb148e31235f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.AdjustmentRule.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.AdjustmentRule.cs @@ -76,6 +76,24 @@ private AdjustmentRule( _noDaylightTransitions = noDaylightTransitions; } + internal static AdjustmentRule CreateAdjustmentRule( + DateTime dateStart, + DateTime dateEnd, + TimeSpan daylightDelta, + TransitionTime daylightTransitionStart, + TransitionTime daylightTransitionEnd, + TimeSpan baseUtcOffsetDelta) + { + return new AdjustmentRule( + dateStart, + dateEnd, + daylightDelta, + daylightTransitionStart, + daylightTransitionEnd, + baseUtcOffsetDelta, + noDaylightTransitions: false); + } + public static AdjustmentRule CreateAdjustmentRule( DateTime dateStart, DateTime dateEnd, diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs new file mode 100644 index 0000000000000..3e0230f9e81b2 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs @@ -0,0 +1,266 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time"; + private const string FallbackCultureName = "en-US"; + private const string GmtId = "GMT"; + + // Some time zones may give better display names using their location names rather than their generic name. + // We can update this list as need arises. + private static readonly string[] s_ZonesThatUseLocationName = new[] { + "Europe/Minsk", // Prefer "Belarus Time" over "Moscow Standard Time (Minsk)" + "Europe/Moscow", // Prefer "Moscow Time" over "Moscow Standard Time" + "Europe/Simferopol", // Prefer "Simferopol Time" over "Moscow Standard Time (Simferopol)" + "Pacific/Apia", // Prefer "Samoa Time" over "Apia Time" + "Pacific/Pitcairn" // Prefer "Pitcairn Islands Time" over "Pitcairn Time" + }; + + // Main function that is called during construction to populate the three display names + private static void TryPopulateTimeZoneDisplayNamesFromGlobalizationData(string timeZoneId, TimeSpan baseUtcOffset, ref string? standardDisplayName, ref string? daylightDisplayName, ref string? displayName) + { + // Determine the culture to use + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (uiCulture.Name.Length == 0) + uiCulture = CultureInfo.GetCultureInfo(FallbackCultureName); // ICU doesn't work nicely with InvariantCulture + + // Attempt to populate the fields backing the StandardName, DaylightName, and DisplayName from globalization data. + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture.Name, ref daylightDisplayName); + GetFullValueForDisplayNameField(timeZoneId, baseUtcOffset, uiCulture, ref displayName); + } + + // Helper function to get the standard display name for the UTC static time zone instance + private static string GetUtcStandardDisplayName() + { + // Don't bother looking up the name for invariant or English cultures + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (GlobalizationMode.Invariant || uiCulture.Name.Length == 0 || uiCulture.TwoLetterISOLanguageName == "en") + return InvariantUtcStandardDisplayName; + + // Try to get a localized version of "Coordinated Universal Time" from the globalization data + string? standardDisplayName = null; + GetDisplayName(UtcId, Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture.Name, ref standardDisplayName); + + // Final safety check. Don't allow null or abbreviations + if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC") + standardDisplayName = InvariantUtcStandardDisplayName; + + return standardDisplayName; + } + + // Helper function to get the full display name for the UTC static time zone instance + private static string GetUtcFullDisplayName(string timeZoneId, string standardDisplayName) + { + return $"(UTC) {standardDisplayName}"; + } + + // Helper function that retrieves various forms of time zone display names from ICU + private static unsafe void GetDisplayName(string timeZoneId, Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) + { + if (GlobalizationMode.Invariant) + { + return; + } + + string? timeZoneDisplayName; + bool result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + uiCulture, + timeZoneId, + nameType, + out timeZoneDisplayName); + + if (!result && uiCulture != FallbackCultureName) + { + // Try to fallback using FallbackCultureName just in case we can make it work. + result = Interop.CallStringMethod( + (buffer, locale, id, type) => + { + fixed (char* bufferPtr = buffer) + { + return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); + } + }, + FallbackCultureName, + timeZoneId, + nameType, + out timeZoneDisplayName); + } + + // If there is an unknown error, don't set the displayName field. + // It will be set to the abbreviation that was read out of the tzfile. + if (result && !string.IsNullOrEmpty(timeZoneDisplayName)) + { + displayName = timeZoneDisplayName; + } + } + + // Helper function that builds the value backing the DisplayName field from globalization data. + private static void GetFullValueForDisplayNameField(string timeZoneId, TimeSpan baseUtcOffset, CultureInfo uiCulture, ref string? displayName) + { + // There are a few diffent ways we might show the display name depending on the data. + // The algorithm used below should avoid duplicating the same words while still achieving the + // goal of providing a unique, discoverable, and intuitive name. + + // Try to get the generic name for this time zone. + string? genericName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref genericName); + if (genericName == null) + { + // We'll use the fallback display name value already set. + return; + } + + // Get the base offset to prefix in front of the time zone. + // Only UTC and its aliases have "(UTC)", handled earlier. All other zones include an offset, even if it's zero. + string baseOffsetText = $"(UTC{(baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{baseUtcOffset:hh\\:mm})"; + + // Get the generic location name. + string? genericLocationName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref genericLocationName); + + // Some edge cases only apply when the offset is +00:00. + if (baseUtcOffset == TimeSpan.Zero) + { + // GMT and its aliases will just use the equivalent of "Greenwich Mean Time". + string? gmtLocationName = null; + GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.GenericLocation, uiCulture.Name, ref gmtLocationName); + if (genericLocationName == gmtLocationName) + { + displayName = $"{baseOffsetText} {genericName}"; + return; + } + + // Other zones with a zero offset and the equivalent of "Greenwich Mean Time" should only use the location name. + // For example, prefer "Iceland Time" over "Greenwich Mean Time (Reykjavik)". + string? gmtGenericName = null; + GetDisplayName(GmtId, Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture.Name, ref gmtGenericName); + if (genericName == gmtGenericName) + { + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + } + + if (genericLocationName == genericName) + { + // When the location name is the same as the generic name, + // then it is generally good enough to show by itself. + + // *** Example (en-US) *** + // id = "America/Havana" + // baseOffsetText = "(UTC-05:00)" + // standardName = "Cuba Standard Time" + // genericName = "Cuba Time" + // genericLocationName = "Cuba Time" + // exemplarCityName = "Havana" + // displayName = "(UTC-05:00) Cuba Time" + + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + + // Prefer location names in some special cases. + if (StringArrayContains(timeZoneId, s_ZonesThatUseLocationName, StringComparison.OrdinalIgnoreCase)) + { + displayName = $"{baseOffsetText} {genericLocationName}"; + return; + } + + // See if we should include the exemplar city name. + string exemplarCityName = GetExemplarCityName(timeZoneId, uiCulture.Name); + if (uiCulture.CompareInfo.IndexOf(genericName, exemplarCityName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0 && genericLocationName != null) + { + // When an exemplar city is already part of the generic name, + // there's no need to repeat it again so just use the generic name. + + // *** Example (fr-FR) *** + // id = "Australia/Lord_Howe" + // baseOffsetText = "(UTC+10:30)" + // standardName = "heure normale de Lord Howe" + // genericName = "heure de Lord Howe" + // genericLocationName = "heure : Lord Howe" + // exemplarCityName = "Lord Howe" + // displayName = "(UTC+10:30) heure de Lord Howe" + + displayName = $"{baseOffsetText} {genericName}"; + } + else + { + // Finally, use the generic name and the exemplar city together. + // This provides an intuitive name and still disambiguates. + + // *** Example (en-US) *** + // id = "Europe/Rome" + // baseOffsetText = "(UTC+01:00)" + // standardName = "Central European Standard Time" + // genericName = "Central European Time" + // genericLocationName = "Italy Time" + // exemplarCityName = "Rome" + // displayName = "(UTC+01:00) Central European Time (Rome)" + + displayName = $"{baseOffsetText} {genericName} ({exemplarCityName})"; + } + } + + // Helper function that gets an exmplar city name either from ICU or from the IANA time zone ID itself + private static string GetExemplarCityName(string timeZoneId, string uiCultureName) + { + // First try to get the name through the localization data. + string? exemplarCityName = null; + GetDisplayName(timeZoneId, Interop.Globalization.TimeZoneDisplayNameType.ExemplarCity, uiCultureName, ref exemplarCityName); + if (!string.IsNullOrEmpty(exemplarCityName)) + return exemplarCityName; + + // Support for getting exemplar city names was added in ICU 51. + // We may have an older version. For example, in Helix we test on RHEL 7.5 which uses ICU 50.1.2. + // We'll fallback to using an English name generated from the time zone ID. + int i = timeZoneId.LastIndexOf('/'); + return timeZoneId.Substring(i + 1).Replace('_', ' '); + } + + // Helper function that returns an alternative ID using ICU data. Used primarily for converting from Windows IDs. + private static unsafe string? GetAlternativeId(string id) + { + if (!GlobalizationMode.Invariant) + { + if (id.Equals("utc", StringComparison.OrdinalIgnoreCase)) + { + // Special case UTC, as previously ICU would convert it to "Etc/GMT" which is incorrect name for UTC. + return "Etc/UTC"; + } + + foreach (char c in id) + { + // ICU uses some characters as a separator and trim the id at that character. + // while we should fail if the Id contained one of these characters. + if (c == '\\' || c == '\n' || c == '\r') + { + return null; + } + } + + char* buffer = stackalloc char[100]; + int length = Interop.Globalization.WindowsIdToIanaId(id, buffer, 100); + if (length > 0) + { + return new string(buffer, 0, length); + } + } + + return null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs deleted file mode 100644 index 834b719fefe2c..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.GetDisplayName.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Text; -using System.Threading; -using System.Security; - -using Internal.IO; - -namespace System -{ - public sealed partial class TimeZoneInfo - { - private unsafe void GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType nameType, string uiCulture, ref string? displayName) - { - if (GlobalizationMode.Invariant) - { - displayName = _standardDisplayName; - return; - } - - string? timeZoneDisplayName; - bool result = Interop.CallStringMethod( - (buffer, locale, id, type) => - { - fixed (char* bufferPtr = buffer) - { - return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); - } - }, - uiCulture, - _id, - nameType, - out timeZoneDisplayName); - - if (!result && uiCulture != FallbackCultureName) - { - // Try to fallback using FallbackCultureName just in case we can make it work. - result = Interop.CallStringMethod( - (buffer, locale, id, type) => - { - fixed (char* bufferPtr = buffer) - { - return Interop.Globalization.GetTimeZoneDisplayName(locale, id, type, bufferPtr, buffer.Length); - } - }, - FallbackCultureName, - _id, - nameType, - out timeZoneDisplayName); - } - - // If there is an unknown error, don't set the displayName field. - // It will be set to the abbreviation that was read out of the tzfile. - if (result && !string.IsNullOrEmpty(timeZoneDisplayName)) - { - displayName = timeZoneDisplayName; - } - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs new file mode 100644 index 0000000000000..234d63366f3e1 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.MinimalGlobalizationData.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + public sealed partial class TimeZoneInfo + { + private static void TryPopulateTimeZoneDisplayNamesFromGlobalizationData(string timeZoneId, TimeSpan baseUtcOffset, ref string? standardDisplayName, ref string? daylightDisplayName, ref string? displayName) + { + // Do nothing. We'll use the fallback values already set. + } + + private static string GetUtcStandardDisplayName() + { + // For this target, be consistent with other time zone display names that use an abbreviation. + return "UTC"; + } + + private static string GetUtcFullDisplayName(string timeZoneId, string standardDisplayName) + { + // For this target, be consistent with other time zone display names that use the ID. + return $"(UTC) {timeZoneId}"; + } + + private static string? GetAlternativeId(string id) + { + // No alternative IDs in this target. + return null; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index fd2d2556527c6..6c7efd4b662b3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -22,10 +22,36 @@ public sealed partial class TimeZoneInfo private const string ZoneTabFileName = "zone.tab"; private const string TimeZoneEnvironmentVariable = "TZ"; private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR"; - private const string FallbackCultureName = "en-US"; + + // UTC aliases per https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml + // Hard-coded because we need to treat all aliases of UTC the same even when globalization data is not available. + // (This list is not likely to change.) + private static readonly string[] s_UtcAliases = new[] { + "Etc/UTC", + "Etc/UCT", + "Etc/Universal", + "Etc/Zulu", + "UCT", + "UTC", + "Universal", + "Zulu" + }; private TimeZoneInfo(byte[] data, string id, bool dstDisabled) { + _id = id; + + // Handle UTC and its aliases + if (StringArrayContains(_id, s_UtcAliases, StringComparison.OrdinalIgnoreCase)) + { + _standardDisplayName = GetUtcStandardDisplayName(); + _daylightDisplayName = _standardDisplayName; + _displayName = GetUtcFullDisplayName(_id, _standardDisplayName); + _baseUtcOffset = TimeSpan.Zero; + _adjustmentRules = Array.Empty(); + return; + } + TZifHead t; DateTime[] dts; byte[] typeOfLocalTime; @@ -40,12 +66,8 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) // parse the raw TZif bytes; this method can throw ArgumentException when the data is malformed. TZif_ParseRaw(data, out t, out dts, out typeOfLocalTime, out transitionType, out zoneAbbreviations, out StandardTime, out GmtTime, out futureTransitionsPosixFormat); - _id = id; - _displayName = LocalId; - _baseUtcOffset = TimeSpan.Zero; - // find the best matching baseUtcOffset and display strings based on the current utcNow value. - // NOTE: read the display strings from the tzfile now in case they can't be loaded later + // NOTE: read the Standard and Daylight display strings from the tzfile now in case they can't be loaded later // from the globalization data. DateTime utcNow = DateTime.UtcNow; for (int i = 0; i < dts.Length && dts[i] <= utcNow; i++) @@ -80,23 +102,14 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) } } - // Use abbrev as the fallback + // Set fallback values using abbreviations, base offset, and id + // These are expected in environments without time zone globalization data _standardDisplayName = standardAbbrevName; - _daylightDisplayName = daylightAbbrevName; - _displayName = _standardDisplayName; + _daylightDisplayName = daylightAbbrevName ?? standardAbbrevName; + _displayName = $"(UTC{(_baseUtcOffset >= TimeSpan.Zero ? '+' : '-')}{_baseUtcOffset:hh\\:mm}) {_id}"; - string uiCulture = CultureInfo.CurrentUICulture.Name.Length == 0 ? FallbackCultureName : CultureInfo.CurrentUICulture.Name; // ICU doesn't work nicely with Invariant - GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Generic, uiCulture, ref _displayName); - GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.Standard, uiCulture, ref _standardDisplayName); - GetDisplayName(Interop.Globalization.TimeZoneDisplayNameType.DaylightSavings, uiCulture, ref _daylightDisplayName); - - if (_standardDisplayName == _displayName) - { - if (_baseUtcOffset >= TimeSpan.Zero) - _displayName = $"(UTC+{_baseUtcOffset:hh\\:mm}) {_standardDisplayName}"; - else - _displayName = $"(UTC-{_baseUtcOffset:hh\\:mm}) {_standardDisplayName}"; - } + // Try to populate the display names from the globalization data + TryPopulateTimeZoneDisplayNamesFromGlobalizationData(_id, _baseUtcOffset, ref _standardDisplayName, ref _daylightDisplayName, ref _displayName); // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns // with DateTimeOffset, SQL Server, and the W3C XML Specification @@ -114,6 +127,17 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled) ValidateTimeZoneInfo(_id, _baseUtcOffset, _adjustmentRules, out _supportsDaylightSavingTime); } + // The TransitionTime fields are not used when AdjustmentRule.NoDaylightTransitions == true. + // However, there are some cases in the past where DST = true, and the daylight savings offset + // now equals what the current BaseUtcOffset is. In that case, the AdjustmentRule.DaylightOffset + // is going to be TimeSpan.Zero. But we still need to return 'true' from AdjustmentRule.HasDaylightSaving. + // To ensure we always return true from HasDaylightSaving, make a "special" dstStart that will make the logic + // in HasDaylightSaving return true. + private static readonly TransitionTime s_daylightRuleMarker = TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(2), 1, 1); + + // Truncate the date and the time to Milliseconds precision + private static DateTime GetTimeOnlyInMillisecondsPrecision(DateTime input) => new DateTime((input.TimeOfDay.Ticks / TimeSpan.TicksPerMillisecond) * TimeSpan.TicksPerMillisecond); + /// /// Returns a cloned array of AdjustmentRule objects /// @@ -128,11 +152,20 @@ public AdjustmentRule[] GetAdjustmentRules() // as the rules now is public, we should fill it properly so the caller doesn't have to know how we use it internally // and can use it as it is used in Windows - AdjustmentRule[] rules = new AdjustmentRule[_adjustmentRules.Length]; + List rulesList = new List(_adjustmentRules.Length); for (int i = 0; i < _adjustmentRules.Length; i++) { - AdjustmentRule? rule = _adjustmentRules[i]; + AdjustmentRule rule = _adjustmentRules[i]; + + if (rule.NoDaylightTransitions && + rule.DaylightTransitionStart != s_daylightRuleMarker && + rule.DaylightDelta == TimeSpan.Zero && rule.BaseUtcOffsetDelta == TimeSpan.Zero) + { + // This rule has no time transition, ignore it. + continue; + } + DateTime start = rule.DateStart.Kind == DateTimeKind.Utc ? // At the daylight start we didn't start the daylight saving yet then we convert to Local time // by adding the _baseUtcOffset to the UTC time @@ -144,13 +177,51 @@ public AdjustmentRule[] GetAdjustmentRules() new DateTime(rule.DateEnd.Ticks + _baseUtcOffset.Ticks + rule.DaylightDelta.Ticks, DateTimeKind.Unspecified) : rule.DateEnd; - TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, start.Hour, start.Minute, start.Second), start.Month, start.Day); - TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, end.Hour, end.Minute, end.Second), end.Month, end.Day); + if (start.Year == end.Year || !rule.NoDaylightTransitions) + { + // If the rule is covering only one year then the start and end transitions would occur in that year, we don't need to split the rule. + // Also, rule.NoDaylightTransitions be false in case the rule was created from a POSIX time zone string and having a DST transition. We can represent this in one rule too + TransitionTime startTransition = rule.NoDaylightTransitions ? TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(start), start.Month, start.Day) : rule.DaylightTransitionStart; + TransitionTime endTransition = rule.NoDaylightTransitions ? TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(end), end.Month, end.Day) : rule.DaylightTransitionEnd; + rulesList.Add(AdjustmentRule.CreateAdjustmentRule(start.Date, end.Date, rule.DaylightDelta, startTransition, endTransition, rule.BaseUtcOffsetDelta)); + } + else + { + // For rules spanning more than one year. The time transition inside this rule would apply for the whole time spanning these years + // and not for partial time of every year. + // AdjustmentRule cannot express such rule using the DaylightTransitionStart and DaylightTransitionEnd because + // the DaylightTransitionStart and DaylightTransitionEnd express the transition for every year. + // We split the rule into more rules. The first rule will start from the start year of the original rule and ends at the end of the same year. + // The second splitted rule would cover the middle range of the original rule and ranging from the year start+1 to + // year end-1. The transition time in this rule would start from Jan 1st to end of December. + // The last splitted rule would start from the Jan 1st of the end year of the original rule and ends at the end transition time of the original rule. + + // Add the first rule. + DateTime endForFirstRule = new DateTime(start.Year + 1, 1, 1).AddMilliseconds(-1); // At the end of the first year + TransitionTime startTransition = TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(start), start.Month, start.Day); + TransitionTime endTransition = TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(endForFirstRule), endForFirstRule.Month, endForFirstRule.Day); + rulesList.Add(AdjustmentRule.CreateAdjustmentRule(start.Date, endForFirstRule.Date, rule.DaylightDelta, startTransition, endTransition, rule.BaseUtcOffsetDelta)); + + // Check if there is range of years between the start and the end years + if (end.Year - start.Year > 1) + { + // Add the middle rule. + DateTime middleYearStart = new DateTime(start.Year + 1, 1, 1); + DateTime middleYearEnd = new DateTime(end.Year, 1, 1).AddMilliseconds(-1); + startTransition = TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(middleYearStart), middleYearStart.Month, middleYearStart.Day); + endTransition = TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(middleYearEnd), middleYearEnd.Month, middleYearEnd.Day); + rulesList.Add(AdjustmentRule.CreateAdjustmentRule(middleYearStart.Date, middleYearEnd.Date, rule.DaylightDelta, startTransition, endTransition, rule.BaseUtcOffsetDelta)); + } - rules[i] = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(start.Date, end.Date, rule.DaylightDelta, startTransition, endTransition); + // Add the end rule. + DateTime endYearStart = new DateTime(end.Year, 1, 1); // At the beginning of the last year + startTransition = TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(endYearStart), endYearStart.Month, endYearStart.Day); + endTransition = TransitionTime.CreateFixedDateRule(GetTimeOnlyInMillisecondsPrecision(end), end.Month, end.Day); + rulesList.Add(AdjustmentRule.CreateAdjustmentRule(endYearStart.Date, end.Date, rule.DaylightDelta, startTransition, endTransition, rule.BaseUtcOffsetDelta)); + } } - return rules; + return rulesList.ToArray(); } private static void PopulateAllSystemTimeZones(CachedData cachedData) @@ -164,36 +235,6 @@ private static void PopulateAllSystemTimeZones(CachedData cachedData) } } - private static unsafe string? GetAlternativeId(string id) - { - if (!GlobalizationMode.Invariant) - { - if (id.Equals("utc", StringComparison.OrdinalIgnoreCase)) - { - //special case UTC as ICU will convert it to "Etc/GMT" which is incorrect name for UTC. - return "Etc/UTC"; - } - foreach (char c in id) - { - // ICU uses some characters as a separator and trim the id at that character. - // while we should fail if the Id contained one of these characters. - if (c == '\\' || c == '\n' || c == '\r') - { - return null; - } - } - - char* buffer = stackalloc char[100]; - int length = Interop.Globalization.WindowsIdToIanaId(id, buffer, 100); - if (length > 0) - { - return new string(buffer, 0, length); - } - } - - return null; - } - /// /// Helper function for retrieving the local system time zone. /// May throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException. @@ -583,7 +624,7 @@ private static bool CompareTimeZoneFile(string filePath, byte[] buffer, byte[] r { int n = stream.Read(buffer, index, count); if (n == 0) - throw Error.GetEndOfFile(); + ThrowHelper.ThrowEndOfFileException(); int end = index + n; for (; index < end; index++) @@ -930,7 +971,7 @@ private static void TZif_GenerateAdjustmentRule(ref int index, TimeSpan timeZone baseUtcDelta, noDaylightTransitions: true); - if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + if (!IsValidAdjustmentRuleOffset(timeZoneBaseUtcOffset, r)) { NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); } @@ -957,7 +998,7 @@ private static void TZif_GenerateAdjustmentRule(ref int index, TimeSpan timeZone // is going to be TimeSpan.Zero. But we still need to return 'true' from AdjustmentRule.HasDaylightSaving. // To ensure we always return true from HasDaylightSaving, make a "special" dstStart that will make the logic // in HasDaylightSaving return true. - dstStart = TransitionTime.CreateFixedDateRule(DateTime.MinValue.AddMilliseconds(2), 1, 1); + dstStart = s_daylightRuleMarker; } else { @@ -973,7 +1014,7 @@ private static void TZif_GenerateAdjustmentRule(ref int index, TimeSpan timeZone baseUtcDelta, noDaylightTransitions: true); - if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + if (!IsValidAdjustmentRuleOffset(timeZoneBaseUtcOffset, r)) { NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); } @@ -1010,7 +1051,7 @@ private static void TZif_GenerateAdjustmentRule(ref int index, TimeSpan timeZone noDaylightTransitions: true); } - if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + if (!IsValidAdjustmentRuleOffset(timeZoneBaseUtcOffset, r)) { NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); } @@ -1068,7 +1109,7 @@ private static TZifType TZif_GetEarlyDateTransitionType(TZifType[] transitionTyp /// Creates an AdjustmentRule given the POSIX TZ environment variable string. /// /// - /// See http://man7.org/linux/man-pages/man3/tzset.3.html for the format and semantics of this POSX string. + /// See http://man7.org/linux/man-pages/man3/tzset.3.html for the format and semantics of this POSIX string. /// private static AdjustmentRule? TZif_CreateAdjustmentRuleForPosixFormat(string posixFormat, DateTime startTransitionDate, TimeSpan timeZoneBaseUtcOffset) { @@ -1238,8 +1279,7 @@ private static DateTime ParseTimeOfDay(ReadOnlySpan time) { if (date[0] != 'J') { - // should be n Julian day format which we don't support. - // + // should be n Julian day format. // This specifies the Julian day, with n between 0 and 365. February 29 is counted in leap years. // // n would be a relative number from the beginning of the year. which should handle if the @@ -1255,11 +1295,30 @@ private static DateTime ParseTimeOfDay(ReadOnlySpan time) // 0 30 31 58 59 89 334 364 // |-------Jan--------|-------Feb--------|-------Mar--------|....|-------Dec--------| // - // // For example if n is specified as 60, this means in leap year the rule will start at Mar 1, // while in non leap year the rule will start at Mar 2. // - // If we need to support n format, we'll have to have a floating adjustment rule support this case. + // This n Julian day format is very uncommon and mostly used for convenience to specify dates like January 1st + // which we can support without any major modification to the Adjustment rules. We'll support this rule for day + // numbers less than 59 (up to Feb 28). Otherwise we'll skip this POSIX rule. + // We've never encountered any time zone file using this format for days beyond Feb 28. + + if (int.TryParse(date, out int julianDay) && julianDay < 59) + { + int d, m; + if (julianDay <= 30) // January + { + m = 1; + d = julianDay + 1; + } + else // February + { + m = 2; + d = julianDay - 30; + } + + return TransitionTime.CreateFixedDateRule(ParseTimeOfDay(time), m, d); + } // Since we can't support this rule, return null to indicate to skip the POSIX rule. return null; @@ -1272,7 +1331,7 @@ private static DateTime ParseTimeOfDay(ReadOnlySpan time) } /// - /// Parses a string like Jn or n into month and day values. + /// Parses a string like Jn into month and day values. /// private static void TZif_ParseJulianDay(ReadOnlySpan date, out int month, out int day) { @@ -1731,5 +1790,19 @@ private enum TZVersion : byte V3, // when adding more versions, ensure all the logic using TZVersion is still correct } + + // Helper function for string array search. (LINQ is not available here.) + private static bool StringArrayContains(string value, string[] source, StringComparison comparison) + { + foreach (string s in source) + { + if (string.Equals(s, value, comparison)) + { + return true; + } + } + + return false; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs index 6223e66e018ae..a13f48d6258de 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs @@ -34,6 +34,7 @@ public sealed partial class TimeZoneInfo private const string LastEntryValue = "LastEntry"; private const int MaxKeyLength = 255; + private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time"; private sealed partial class CachedData { @@ -1000,5 +1001,49 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out } } } + + // Helper function to get the standard display name for the UTC static time zone instance + private static string GetUtcStandardDisplayName() + { + // Don't bother looking up the name for invariant or English cultures + CultureInfo uiCulture = CultureInfo.CurrentUICulture; + if (uiCulture.Name.Length == 0 || uiCulture.TwoLetterISOLanguageName == "en") + return InvariantUtcStandardDisplayName; + + // Try to get a localized version of "Coordinated Universal Time" from the globalization data + string? standardDisplayName = null; + using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(TimeZonesRegistryHive + "\\" + UtcId, writable: false)) + { + if (key != null) + { + // read the MUI_ registry key + string? standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty) as string; + + // try to load the string from the native resource DLL(s) + if (!string.IsNullOrEmpty(standardNameMuiResource)) + { + standardDisplayName = TryGetLocalizedNameByMuiNativeResource(standardNameMuiResource); + } + + // fallback to using the standard registry key + if (string.IsNullOrEmpty(standardDisplayName)) + { + standardDisplayName = key.GetValue(StandardValue, string.Empty) as string; + } + } + } + + // Final safety check. Don't allow null or abbreviations + if (standardDisplayName == null || standardDisplayName == "GMT" || standardDisplayName == "UTC") + standardDisplayName = InvariantUtcStandardDisplayName; + + return standardDisplayName; + } + + // Helper function to get the full display name for the UTC static time zone instance + private static string GetUtcFullDisplayName(string timeZoneId, string standardDisplayName) + { + return $"(UTC) {standardDisplayName}"; + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs index 771cef56a3a8c..1ef3a1841ffd1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs @@ -53,7 +53,7 @@ private enum TimeZoneInfoResult private const string UtcId = "UTC"; private const string LocalId = "Local"; - private static readonly TimeZoneInfo s_utcTimeZone = CreateCustomTimeZone(UtcId, TimeSpan.Zero, "(UTC) Coordinated Universal Time", "Coordinated Universal Time"); + private static readonly TimeZoneInfo s_utcTimeZone = CreateUtcTimeZone(); private static CachedData s_cachedData = new CachedData(); @@ -1976,7 +1976,7 @@ private static void ValidateTimeZoneInfo(string id, TimeSpan baseUtcOffset, Adju throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesNoNulls); } - if (!IsValidAdjustmentRuleOffest(baseUtcOffset, current)) + if (!IsValidAdjustmentRuleOffset(baseUtcOffset, current)) { throw new InvalidTimeZoneException(SR.ArgumentOutOfRange_UtcOffsetAndDaylightDelta); } @@ -2009,10 +2009,18 @@ private static TimeSpan GetUtcOffset(TimeSpan baseUtcOffset, AdjustmentRule adju /// /// Helper function that performs adjustment rule validation /// - private static bool IsValidAdjustmentRuleOffest(TimeSpan baseUtcOffset, AdjustmentRule adjustmentRule) + private static bool IsValidAdjustmentRuleOffset(TimeSpan baseUtcOffset, AdjustmentRule adjustmentRule) { TimeSpan utcOffset = GetUtcOffset(baseUtcOffset, adjustmentRule); return !UtcOffsetOutOfRange(utcOffset); } + + // Helper function to create the static UTC time zone instance + private static TimeZoneInfo CreateUtcTimeZone() + { + string standardDisplayName = GetUtcStandardDisplayName(); + string displayName = GetUtcFullDisplayName(UtcId, standardDisplayName); + return CreateCustomTimeZone(UtcId, TimeSpan.Zero, displayName, standardDisplayName); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Utf8Extensions.CoreLib.cs b/src/libraries/System.Private.CoreLib/src/System/Utf8Extensions.CoreLib.cs deleted file mode 100644 index ef0bc2105e1d3..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Utf8Extensions.CoreLib.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System -{ - public static partial class Utf8Extensions - { - /// Creates a new over the portion of the target . - /// The target . - /// Returns default when is null. - public static ReadOnlyMemory AsMemory(this Utf8String? text) - { - if (text is null) - return default; - - return new ReadOnlyMemory(text, 0, text.Length); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The index at which to begin this slice. - /// Returns default when is null. - /// - /// Thrown when the specified index is not in range (<0 or >text.Length). - /// - public static ReadOnlyMemory AsMemory(this Utf8String? text, int start) - { - if (text is null) - { - if (start != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - - if ((uint)start > (uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - - return new ReadOnlyMemory(text, start, text.Length - start); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The index at which to begin this slice. - public static ReadOnlyMemory AsMemory(this Utf8String? text, Index startIndex) - { - if (text is null) - { - if (!startIndex.Equals(Index.Start)) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - return default; - } - - int actualIndex = startIndex.GetOffset(text.Length); - if ((uint)actualIndex > (uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new ReadOnlyMemory(text, actualIndex, text.Length - actualIndex); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// Returns default when is null. - /// - /// Thrown when the specified index or is not in range. - /// - public static ReadOnlyMemory AsMemory(this Utf8String? text, int start, int length) - { - if (text is null) - { - if (start != 0 || length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - -#if TARGET_64BIT - // See comment in Span.Slice for how this works. - if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#else - if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#endif - - return new ReadOnlyMemory(text, start, length); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The range used to indicate the start and length of the sliced string. - public static ReadOnlyMemory AsMemory(this Utf8String? text, Range range) - { - if (text is null) - { - Index startIndex = range.Start; - Index endIndex = range.End; - - if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start)) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - return default; - } - - (int start, int length) = range.GetOffsetAndLength(text.Length); - return new ReadOnlyMemory(text, start, length); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan CreateSpan(Utf8String text) => - new ReadOnlySpan(ref text.DangerousGetMutableReference(), text.Length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan CreateSpan(Utf8String text, int start) => - new ReadOnlySpan(ref text.DangerousGetMutableReference(start), text.Length - start); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan CreateSpan(Utf8String text, int start, int length) => - new ReadOnlySpan(ref text.DangerousGetMutableReference(start), length); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlyMemory CreateMemoryBytes(Utf8String text, int start, int length) => - new ReadOnlyMemory(text, start, length); - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/Utf8Extensions.cs b/src/libraries/System.Private.CoreLib/src/System/Utf8Extensions.cs deleted file mode 100644 index 3249bb911ec98..0000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Utf8Extensions.cs +++ /dev/null @@ -1,295 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.Unicode; - -namespace System -{ - public static partial class Utf8Extensions - { - /// - /// Projects as a . - /// - public static ReadOnlySpan AsBytes(this ReadOnlySpan text) - { - return MemoryMarshal.Cast(text); - } - - /// - /// Creates a new readonly span over the portion of the target . - /// - /// The target . - /// Returns default when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsBytes(this Utf8String? text) - { - if (text is null) - return default; - - return CreateSpan(text); - } - - /// - /// Creates a new readonly span over the portion of the target . - /// - /// The target . - /// The index at which to begin this slice. - /// Thrown when is null. - /// - /// Thrown when the specified index is not in range (<0 or >text.Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsBytes(this Utf8String? text, int start) - { - if (text is null) - { - if (start != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - - if ((uint)start > (uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - - return CreateSpan(text, start); - } - - /// - /// Creates a new readonly span over the portion of the target . - /// - /// The target . - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// Returns default when is null. - /// - /// Thrown when the specified index or is not in range. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsBytes(this Utf8String? text, int start, int length) - { - if (text is null) - { - if (start != 0 || length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - -#if TARGET_64BIT - // See comment in Span.Slice for how this works. - if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#else - if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#endif - - return CreateSpan(text, start, length); - } - - /// - /// Creates a new over the target . - /// - /// The target . - /// Returns default when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Utf8Span AsSpan(this Utf8String? text) - { - if (text is null) - return default; - - return new Utf8Span(text); - } - - /// - /// Creates a new over the portion of the target . - /// - /// The target . - /// The index at which to begin this slice. - /// Thrown when is null. - /// - /// Thrown when the specified index is not in range (<0 or >text.Length). - /// - /// - /// Thrown if the resulting span would split a multi-byte UTF-8 subsequence. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Utf8Span AsSpan(this Utf8String? text, int start) - { - if (text is null) - { - if (start != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - - if ((uint)start > (uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - - // It's always safe for us to read just past the end of the string (since there's a null terminator), - // so we don't need to perform any additional bounds checking. We only need to check that we're not - // splitting in the middle of a multi-byte UTF-8 subsequence. - - if (Utf8Utility.IsUtf8ContinuationByte(text.DangerousGetMutableReference(start))) - { - Utf8String.ThrowImproperStringSplit(); - } - - return Utf8Span.UnsafeCreateWithoutValidation(CreateSpan(text, start)); - } - - /// - /// Creates a new over the portion of the target . - /// - /// The target . - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// Returns default when is null. - /// - /// Thrown when the specified index or is not in range. - /// - /// - /// Thrown if the resulting span would split a multi-byte UTF-8 subsequence. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Utf8Span AsSpan(this Utf8String? text, int start, int length) - { - if (text is null) - { - if (start != 0 || length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - -#if TARGET_64BIT - // See comment in Span.Slice for how this works. - if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#else - if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#endif - - // It's always safe for us to read just past the end of the string (since there's a null terminator), - // so we don't need to perform any additional bounds checking. We only need to check that we're not - // splitting in the middle of a multi-byte UTF-8 subsequence. - - if (Utf8Utility.IsUtf8ContinuationByte(text.DangerousGetMutableReference(start)) - || Utf8Utility.IsUtf8ContinuationByte(text.DangerousGetMutableReference(start + length))) - { - Utf8String.ThrowImproperStringSplit(); - } - - return Utf8Span.UnsafeCreateWithoutValidation(CreateSpan(text, start, length)); - } - - /// Creates a new over the portion of the target . - /// The target . - /// Returns default when is null. - public static ReadOnlyMemory AsMemoryBytes(this Utf8String? text) - { - if (text is null) - return default; - - return CreateMemoryBytes(text, 0, text.Length); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The index at which to begin this slice. - /// Returns default when is null. - /// - /// Thrown when the specified index is not in range (<0 or >text.Length). - /// - public static ReadOnlyMemory AsMemoryBytes(this Utf8String? text, int start) - { - if (text is null) - { - if (start != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - - if ((uint)start > (uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - - return CreateMemoryBytes(text, start, text.Length - start); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The index at which to begin this slice. - public static ReadOnlyMemory AsMemoryBytes(this Utf8String? text, Index startIndex) - { - if (text is null) - { - if (!startIndex.Equals(Index.Start)) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - return default; - } - - int actualIndex = startIndex.GetOffset(text.Length); - if ((uint)actualIndex > (uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return CreateMemoryBytes(text, actualIndex, text.Length - actualIndex); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// Returns default when is null. - /// - /// Thrown when the specified index or is not in range. - /// - public static ReadOnlyMemory AsMemoryBytes(this Utf8String? text, int start, int length) - { - if (text is null) - { - if (start != 0 || length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); - return default; - } - -#if TARGET_64BIT - // See comment in Span.Slice for how this works. - if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#else - if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start); -#endif - - return CreateMemoryBytes(text, start, length); - } - - /// Creates a new over the portion of the target . - /// The target . - /// The range used to indicate the start and length of the sliced string. - public static ReadOnlyMemory AsMemoryBytes(this Utf8String? text, Range range) - { - if (text is null) - { - Index startIndex = range.Start; - Index endIndex = range.End; - - if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start)) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text); - - return default; - } - - (int start, int length) = range.GetOffsetAndLength(text.Length); - return CreateMemoryBytes(text, start, length); - } - - /// - /// Returns a representation of this instance. - /// - public static Utf8String ToUtf8String(this Rune rune) => Utf8String.CreateFromRune(rune); - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Attributes.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Attributes.cs index 9da06545c254d..1367f3c0dacbf 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Attributes.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Attributes.cs @@ -5,7 +5,7 @@ namespace System.Runtime.Serialization { - internal class Attributes + internal sealed class Attributes { private static readonly XmlDictionaryString[] s_serializationLocalNames = new XmlDictionaryString[] { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/BitFlagsGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/BitFlagsGenerator.cs index b1bec72dae15f..ca8c911f01296 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/BitFlagsGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/BitFlagsGenerator.cs @@ -8,7 +8,7 @@ namespace System.Runtime.Serialization { - internal class BitFlagsGenerator + internal sealed class BitFlagsGenerator { private readonly int _bitCount; private readonly CodeGenerator _ilg; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs index 36f35e3177d8d..b1230c1e97808 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ClassDataContract.cs @@ -678,7 +678,7 @@ internal bool RequiresMemberAccessForWrite(SecurityException? securityException) return false; } - private class ClassDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class ClassDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static Type[]? s_serInfoCtorArgs; @@ -1473,7 +1473,7 @@ internal Member(DataMember member, string ns, int baseTypeIndex) internal int baseTypeIndex; } - internal class DataMemberConflictComparer : IComparer + internal sealed class DataMemberConflictComparer : IComparer { public int Compare(Member x, Member y) { @@ -1522,7 +1522,7 @@ internal ClassDataContractCriticalHelper Clone() } } - internal class DataMemberComparer : IComparer + internal sealed class DataMemberComparer : IComparer { public int Compare(DataMember? x, DataMember? y) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs index 3362c7a7bf550..780866c355f49 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CodeGenerator.cs @@ -15,7 +15,7 @@ namespace System.Runtime.Serialization { - internal class CodeGenerator + internal sealed class CodeGenerator { private static MethodInfo? s_getTypeFromHandle; private static MethodInfo GetTypeFromHandle @@ -1496,7 +1496,7 @@ internal void ToString(Type type) } } - internal class ArgBuilder + internal sealed class ArgBuilder { internal int Index; internal Type ArgType; @@ -1507,7 +1507,7 @@ internal ArgBuilder(int index, Type argType) } } - internal class ForState + internal sealed class ForState { private readonly LocalBuilder? _indexVar; private readonly Label _beginLabel; @@ -1591,7 +1591,7 @@ internal enum Cmp GreaterThanOrEqualTo } - internal class IfState + internal sealed class IfState { private Label _elseBegin; private Label _endIf; @@ -1622,7 +1622,7 @@ internal Label ElseBegin } - internal class SwitchState + internal sealed class SwitchState { private readonly Label _defaultLabel; private readonly Label _endOfSwitchLabel; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs index 66e671cfaeb6c..0c25be7988c52 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/CollectionDataContract.cs @@ -23,7 +23,7 @@ internal interface IKeyValuePairAdapter { } //Special Adapter class to serialize KeyValuePair as Dictionary needs it when polymorphism is involved [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/System.Collections.Generic")] - internal class KeyValuePairAdapter : IKeyValuePairAdapter + internal sealed class KeyValuePairAdapter : IKeyValuePairAdapter { private K _kvpKey; private T _kvpValue; @@ -454,7 +454,7 @@ internal Type GetCollectionElementType() return _helper.GetCollectionElementType(); } - private class CollectionDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class CollectionDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static Type[]? s_knownInterfaces; @@ -1546,7 +1546,7 @@ public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, Xml return o; } - internal class DictionaryEnumerator : IEnumerator> + internal sealed class DictionaryEnumerator : IEnumerator> { private readonly IDictionaryEnumerator _enumerator; @@ -1581,7 +1581,7 @@ public void Reset() } } - internal class GenericDictionaryEnumerator : IEnumerator> + internal sealed class GenericDictionaryEnumerator : IEnumerator> { private readonly IEnumerator> _enumerator; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs index 7d667cf1728d8..c7e2d8d8cbfa4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs @@ -2182,7 +2182,7 @@ internal interface IGenericNameProvider bool ParametersFromBuiltInNamespaces { get; } } - internal class GenericNameProvider : IGenericNameProvider + internal sealed class GenericNameProvider : IGenericNameProvider { private readonly string _genericTypeName; private readonly object[] _genericParams; //Type or DataContract @@ -2265,7 +2265,7 @@ private XmlQualifiedName GetStableName(int i) - internal class TypeHandleRefEqualityComparer : IEqualityComparer + internal sealed class TypeHandleRefEqualityComparer : IEqualityComparer { public bool Equals(TypeHandleRef? x, TypeHandleRef? y) { @@ -2278,7 +2278,7 @@ public int GetHashCode(TypeHandleRef obj) } } - internal class TypeHandleRef + internal sealed class TypeHandleRef { private RuntimeTypeHandle _value; @@ -2304,7 +2304,7 @@ public RuntimeTypeHandle Value } } - internal class IntRef + internal sealed class IntRef { private readonly int _value; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs index 7f475094c5d8b..47e6106473a1a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataMember.cs @@ -12,7 +12,7 @@ namespace System.Runtime.Serialization { - internal class DataMember + internal sealed class DataMember { private readonly CriticalHelper _helper; @@ -147,7 +147,7 @@ internal FastInvokerBuilder.Setter Setter } } - private class CriticalHelper + private sealed class CriticalHelper { private DataContract? _memberTypeContract; private string _name = null!; // Name is always initialized right after construction diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs index 2310c2d7f502f..012b8ec54929b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DiagnosticUtility.cs @@ -22,7 +22,7 @@ public static void Assert(string message) } } - internal class DiagnosticUtility + internal static class DiagnosticUtility { [Conditional("DEBUG")] [DoesNotReturn] @@ -65,10 +65,3 @@ internal static Exception ThrowHelperCallback(Exception e) } } } - -namespace System.ServiceModel -{ - internal class DiagnosticUtility : System.Runtime.Serialization.DiagnosticUtility - { - } -} diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs index e6d46aac89983..cbb4d22476fc9 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/EnumDataContract.cs @@ -65,7 +65,7 @@ internal override bool CanContainReferences get { return false; } } - private class EnumDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class EnumDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private static readonly Dictionary s_typeToName = new Dictionary(); private static readonly Dictionary s_nameToType = new Dictionary(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs index b828cab0f81f3..0429b5f724603 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataObject.cs @@ -23,7 +23,7 @@ internal IList? Members } } - internal class ExtensionDataMember + internal sealed class ExtensionDataMember { private IDataNode? _value; private int _memberIndex; @@ -191,7 +191,7 @@ internal void AddQualifiedNameAttribute(ElementData element, string elementPrefi } } - internal class ClassDataNode : DataNode + internal sealed class ClassDataNode : DataNode { private IList? _members; @@ -213,7 +213,7 @@ public override void Clear() } } - internal class XmlDataNode : DataNode + internal sealed class XmlDataNode : DataNode { private IList? _xmlAttributes; private IList? _xmlChildNodes; @@ -251,7 +251,7 @@ public override void Clear() } } - internal class CollectionDataNode : DataNode + internal sealed class CollectionDataNode : DataNode { private IList? _items; private string? _itemName; @@ -302,7 +302,7 @@ public override void Clear() } } - internal class ISerializableDataNode : DataNode + internal sealed class ISerializableDataNode : DataNode { private string? _factoryTypeName; private string? _factoryTypeNamespace; @@ -347,7 +347,7 @@ public override void Clear() } } - internal class ISerializableDataMember + internal sealed class ISerializableDataMember { private IDataNode? _value; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs index 06fe919e77703..58be047a9dd09 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ExtensionDataReader.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization { // NOTE: XmlReader methods that are not needed have been left un-implemented - internal class ExtensionDataReader : XmlReader + internal sealed class ExtensionDataReader : XmlReader { private enum ExtensionDataNodeType { @@ -508,7 +508,7 @@ private static void AddPrefix(string prefix, string ns) } } - internal class AttributeData + internal sealed class AttributeData { public string? prefix; public string? ns; @@ -516,7 +516,7 @@ internal class AttributeData public string? value; } - internal class ElementData + internal sealed class ElementData { public string? localName; public string? ns; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs index d1f0ec55771cd..9eca66158743c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/GenericParameterDataContract.cs @@ -30,7 +30,7 @@ public override bool IsBuiltInDataContract } } - private class GenericParameterDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class GenericParameterDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private readonly int _parameterPosition; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/HybridObjectCache.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/HybridObjectCache.cs index 035f1556b67a8..d0e9f8e118785 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/HybridObjectCache.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/HybridObjectCache.cs @@ -8,7 +8,7 @@ namespace System.Runtime.Serialization { - internal class HybridObjectCache + internal sealed class HybridObjectCache { private Dictionary? _objectDictionary; private Dictionary? _referencedObjectDictionary; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ByteArrayHelperWithString.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ByteArrayHelperWithString.cs index 90263ce535a02..06fce438e58b6 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ByteArrayHelperWithString.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ByteArrayHelperWithString.cs @@ -8,7 +8,7 @@ namespace System.Runtime.Serialization.Json { - internal class ByteArrayHelperWithString : ArrayHelper + internal sealed class ByteArrayHelperWithString : ArrayHelper { public static readonly ByteArrayHelperWithString Instance = new ByteArrayHelperWithString(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs index ebf49e42ac6eb..4a66fdf2b83c1 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/DataContractJsonSerializer.cs @@ -157,7 +157,7 @@ internal static void CheckIfTypeIsReference(DataContract dataContract) { if (dataContract.IsReference) { - throw System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError( + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( XmlObjectSerializer.CreateSerializationException(SR.Format( SR.JsonUnsupportedForIsReference, DataContract.GetClrTypeFullName(dataContract.UnderlyingType), diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs index 19ac60805082e..b0ec7cdd07a09 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonByteArrayDataContract.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonByteArrayDataContract : JsonDataContract + internal sealed class JsonByteArrayDataContract : JsonDataContract { public JsonByteArrayDataContract(ByteArrayDataContract traditionalByteArrayDataContract) : base(traditionalByteArrayDataContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs index 13e8a4031810f..4c4c0d33b6e32 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonClassDataContract.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonClassDataContract : JsonDataContract + internal sealed class JsonClassDataContract : JsonDataContract { private readonly JsonClassDataContractCriticalHelper _helper; @@ -108,7 +108,7 @@ public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object ob JsonFormatWriterDelegate(jsonWriter, obj, context, TraditionalClassDataContract, MemberNames); } - private class JsonClassDataContractCriticalHelper : JsonDataContractCriticalHelper + private sealed class JsonClassDataContractCriticalHelper : JsonDataContractCriticalHelper { private JsonFormatClassReaderDelegate? _jsonFormatReaderDelegate; private JsonFormatClassWriterDelegate? _jsonFormatWriterDelegate; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs index 3374eb79c89ee..13693553deb9b 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonCollectionDataContract.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonCollectionDataContract : JsonDataContract + internal sealed class JsonCollectionDataContract : JsonDataContract { private readonly JsonCollectionDataContractCriticalHelper _helper; @@ -158,7 +158,7 @@ public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object ob JsonFormatWriterDelegate(jsonWriter, obj, context, TraditionalCollectionDataContract); } - private class JsonCollectionDataContractCriticalHelper : JsonDataContractCriticalHelper + private sealed class JsonCollectionDataContractCriticalHelper : JsonDataContractCriticalHelper { private JsonFormatCollectionReaderDelegate? _jsonFormatReaderDelegate; private JsonFormatGetOnlyCollectionReaderDelegate? _jsonFormatGetOnlyReaderDelegate; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs index f0ad3d71763c1..6482fd752639c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonDataContract.cs @@ -298,7 +298,7 @@ private void AddCollectionItemContractsToKnownDataContracts() } } - internal class JsonReadWriteDelegates + internal sealed class JsonReadWriteDelegates { // this is the global dictionary for JSON delegates introduced for multi-file private static readonly Dictionary s_jsonDelegates = new Dictionary(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs index 939f83ba5b23e..fa2b5ae042a80 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEncodingStreamWrapper.cs @@ -12,7 +12,7 @@ namespace System.Runtime.Serialization.Json // This wrapper does not support seek. // Supports: UTF-8, Unicode, BigEndianUnicode // ASSUMPTION (Microsoft): This class will only be used for EITHER reading OR writing. It can be done, it would just mean more buffers. - internal class JsonEncodingStreamWrapper : Stream + internal sealed class JsonEncodingStreamWrapper : Stream { private static readonly UnicodeEncoding s_validatingBEUTF16 = new UnicodeEncoding(true, false, true); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs index f169cf1488ba5..f27c5361ebd90 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonEnumDataContract.cs @@ -3,7 +3,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonEnumDataContract : JsonDataContract + internal sealed class JsonEnumDataContract : JsonDataContract { private readonly JsonEnumDataContractCriticalHelper _helper; @@ -46,7 +46,7 @@ public override void WriteJsonValueCore(XmlWriterDelegator jsonWriter, object ob } } - private class JsonEnumDataContractCriticalHelper : JsonDataContractCriticalHelper + private sealed class JsonEnumDataContractCriticalHelper : JsonDataContractCriticalHelper { private readonly bool _isULong; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs index 47bd84821c9e3..49f3055ec2427 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatReaderGenerator.cs @@ -43,7 +43,7 @@ public JsonFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader return _helper.GenerateGetOnlyCollectionReader(collectionContract); } - private class CriticalHelper + private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXReader private LocalBuilder _objectLocal = null!; // initialized in CreateObject and ReadCollection diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs index 5248b9420c304..e47e30fd67361 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonFormatWriterGenerator.cs @@ -17,7 +17,7 @@ namespace System.Runtime.Serialization.Json internal delegate void JsonFormatClassWriterDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, ClassDataContract dataContract, XmlDictionaryString[]? memberNames); internal delegate void JsonFormatCollectionWriterDelegate(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContextComplexJson context, CollectionDataContract dataContract); - internal class JsonFormatWriterGenerator + internal sealed class JsonFormatWriterGenerator { private readonly CriticalHelper _helper; @@ -36,7 +36,7 @@ internal JsonFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionD return _helper.GenerateCollectionWriter(collectionContract); } - private class CriticalHelper + private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXWriter private ArgBuilder _xmlWriterArg = null!; // initialized in InitArgs diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index 601ddc1843198..034d57d757da0 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -7,7 +7,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonObjectDataContract : JsonDataContract + internal sealed class JsonObjectDataContract : JsonDataContract { public JsonObjectDataContract(DataContract traditionalDataContract) : base(traditionalDataContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs index 7a9f805fee299..57c61347c83a2 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonQNameDataContract.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonQNameDataContract : JsonDataContract + internal sealed class JsonQNameDataContract : JsonDataContract { public JsonQNameDataContract(QNameDataContract traditionalQNameDataContract) : base(traditionalQNameDataContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonReaderDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonReaderDelegator.cs index 083877a47b8c9..e36a4fe304a0d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonReaderDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonReaderDelegator.cs @@ -8,7 +8,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonReaderDelegator : XmlReaderDelegator + internal sealed class JsonReaderDelegator : XmlReaderDelegator { private readonly DateTimeFormat? _dateTimeFormat; private DateTimeArrayJsonHelperWithString? _dateTimeArrayHelper; @@ -259,7 +259,7 @@ internal bool TryReadJsonDateTimeArray(XmlObjectSerializerReadContext context, return true; } - private class DateTimeArrayJsonHelperWithString : ArrayHelper + private sealed class DateTimeArrayJsonHelperWithString : ArrayHelper { private readonly DateTimeFormat? _dateTimeFormat; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs index e9e1d3c5d77c4..89365be7c7626 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonStringDataContract.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonStringDataContract : JsonDataContract + internal sealed class JsonStringDataContract : JsonDataContract { public JsonStringDataContract(StringDataContract traditionalStringDataContract) : base(traditionalStringDataContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs index 5e5605931c44f..de4dce0cf7fce 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonUriDataContract.cs @@ -9,7 +9,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonUriDataContract : JsonDataContract + internal sealed class JsonUriDataContract : JsonDataContract { public JsonUriDataContract(UriDataContract traditionalUriDataContract) : base(traditionalUriDataContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs index 0ed5f9f7d5a4c..9c164eed11301 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonWriterDelegator.cs @@ -6,7 +6,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonWriterDelegator : XmlWriterDelegator + internal sealed class JsonWriterDelegator : XmlWriterDelegator { private readonly DateTimeFormat? _dateTimeFormat; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs index 0dea02feddb5e..12044fb8b2ec4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonXmlDataContract.cs @@ -8,7 +8,7 @@ namespace System.Runtime.Serialization.Json { - internal class JsonXmlDataContract : JsonDataContract + internal sealed class JsonXmlDataContract : JsonDataContract { public JsonXmlDataContract(XmlDataContract traditionalXmlDataContract) : base(traditionalXmlDataContract) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs index 3fd8d58bd346e..2c1909e007870 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/ReflectionJsonFormatWriter.cs @@ -13,7 +13,7 @@ namespace System.Runtime.Serialization.Json { - internal class ReflectionJsonFormatWriter + internal sealed class ReflectionJsonFormatWriter { private readonly ReflectionJsonClassWriter _reflectionClassWriter = new ReflectionJsonClassWriter(); @@ -188,7 +188,7 @@ private void ReflectionWriteArrayAttribute(XmlWriterDelegator xmlWriter) } } - internal class ReflectionJsonClassWriter : ReflectionClassWriter + internal sealed class ReflectionJsonClassWriter : ReflectionClassWriter { protected override int ReflectionWriteMembers(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context, ClassDataContract classContract, ClassDataContract derivedMostClassContract, int childElementIndex, XmlDictionaryString[]? memberNames) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs index a035f951261d4..21c6548561922 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonReader.cs @@ -12,7 +12,7 @@ namespace System.Runtime.Serialization.Json { - internal class XmlJsonReader : XmlBaseReader, IXmlJsonReaderInitializer + internal sealed class XmlJsonReader : XmlBaseReader, IXmlJsonReaderInitializer { private const int MaxTextChunk = 2048; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs index 31c8c2ffeaf9b..103013f095985 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlJsonWriter.cs @@ -12,7 +12,7 @@ namespace System.Runtime.Serialization.Json { - internal class XmlJsonWriter : XmlDictionaryWriter, IXmlJsonWriterInitializer + internal sealed class XmlJsonWriter : XmlDictionaryWriter, IXmlJsonWriterInitializer { private const char BACK_SLASH = '\\'; private const char FORWARD_SLASH = '/'; @@ -1576,7 +1576,7 @@ private void WriteValue(Array array) _dataType = oldDataType; } - private class JsonNodeWriter : XmlUTF8NodeWriter + private sealed class JsonNodeWriter : XmlUTF8NodeWriter { internal unsafe void WriteChars(char* chars, int charCount) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs index 39011d6f38b74..f5f9b404b46a5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerReadContextComplexJson.cs @@ -14,7 +14,7 @@ namespace System.Runtime.Serialization.Json { - internal class XmlObjectSerializerReadContextComplexJson : XmlObjectSerializerReadContextComplex + internal sealed class XmlObjectSerializerReadContextComplexJson : XmlObjectSerializerReadContextComplex { private string? _extensionDataValueType; private readonly DateTimeFormat? _dateTimeFormat; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs index 15d0335e7dbae..996e2c3915f93 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/XmlObjectSerializerWriteContextComplexJson.cs @@ -13,7 +13,7 @@ namespace System.Runtime.Serialization.Json { - internal class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex + internal sealed class XmlObjectSerializerWriteContextComplexJson : XmlObjectSerializerWriteContextComplex { private readonly EmitTypeInformation _emitXsiType; private bool _perCallXsiTypeAlreadyEmitted; @@ -316,12 +316,12 @@ internal static void VerifyObjectCompatibilityWithInterface(DataContract contrac Type contractType = contract.GetType(); if ((contractType == typeof(XmlDataContract)) && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(declaredType)) { - throw System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.XmlObjectAssignedToIncompatibleInterface, graph.GetType(), declaredType))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.XmlObjectAssignedToIncompatibleInterface, graph.GetType(), declaredType))); } if ((contractType == typeof(CollectionDataContract)) && !CollectionDataContract.IsCollectionInterface(declaredType)) { - throw System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.CollectionAssignedToIncompatibleInterface, graph.GetType(), declaredType))); + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.Format(SR.CollectionAssignedToIncompatibleInterface, graph.GetType(), declaredType))); } } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ObjectToIdCache.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ObjectToIdCache.cs index dbe86840f9b9f..38b9bc57e070f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ObjectToIdCache.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ObjectToIdCache.cs @@ -7,7 +7,7 @@ namespace System.Runtime.Serialization { - internal class ObjectToIdCache + internal sealed class ObjectToIdCache { internal int m_currentCount; internal int[] m_ids; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs index c094c8d4fed4b..4ca4a4717a3ad 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/PrimitiveDataContract.cs @@ -116,7 +116,7 @@ protected bool TryReadNullAtTopLevel(XmlReaderDelegator reader) return false; } - private class PrimitiveDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class PrimitiveDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private MethodInfo? _xmlFormatWriterMethod; private MethodInfo? _xmlFormatContentWriterMethod; @@ -177,12 +177,12 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class AsmxCharDataContract : CharDataContract + internal sealed class AsmxCharDataContract : CharDataContract { internal AsmxCharDataContract() : base(DictionaryGlobals.CharLocalName, DictionaryGlobals.AsmxTypesNamespace) { } } - internal class BooleanDataContract : PrimitiveDataContract + internal sealed class BooleanDataContract : PrimitiveDataContract { public BooleanDataContract() : base(typeof(bool), DictionaryGlobals.BooleanLocalName, DictionaryGlobals.SchemaNamespace) { @@ -208,7 +208,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class SignedByteDataContract : PrimitiveDataContract + internal sealed class SignedByteDataContract : PrimitiveDataContract { public SignedByteDataContract() : base(typeof(sbyte), DictionaryGlobals.SignedByteLocalName, DictionaryGlobals.SchemaNamespace) { @@ -234,7 +234,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class UnsignedByteDataContract : PrimitiveDataContract + internal sealed class UnsignedByteDataContract : PrimitiveDataContract { public UnsignedByteDataContract() : base(typeof(byte), DictionaryGlobals.UnsignedByteLocalName, DictionaryGlobals.SchemaNamespace) { @@ -260,7 +260,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class ShortDataContract : PrimitiveDataContract + internal sealed class ShortDataContract : PrimitiveDataContract { public ShortDataContract() : base(typeof(short), DictionaryGlobals.ShortLocalName, DictionaryGlobals.SchemaNamespace) { @@ -286,7 +286,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class UnsignedShortDataContract : PrimitiveDataContract + internal sealed class UnsignedShortDataContract : PrimitiveDataContract { public UnsignedShortDataContract() : base(typeof(ushort), DictionaryGlobals.UnsignedShortLocalName, DictionaryGlobals.SchemaNamespace) { @@ -312,7 +312,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class NullPrimitiveDataContract : PrimitiveDataContract + internal sealed class NullPrimitiveDataContract : PrimitiveDataContract { public NullPrimitiveDataContract() : base(typeof(NullPrimitiveDataContract), DictionaryGlobals.EmptyString, DictionaryGlobals.EmptyString) { @@ -351,7 +351,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class IntDataContract : PrimitiveDataContract + internal sealed class IntDataContract : PrimitiveDataContract { public IntDataContract() : base(typeof(int), DictionaryGlobals.IntLocalName, DictionaryGlobals.SchemaNamespace) { @@ -377,7 +377,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class UnsignedIntDataContract : PrimitiveDataContract + internal sealed class UnsignedIntDataContract : PrimitiveDataContract { public UnsignedIntDataContract() : base(typeof(uint), DictionaryGlobals.UnsignedIntLocalName, DictionaryGlobals.SchemaNamespace) { @@ -433,32 +433,32 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class IntegerDataContract : LongDataContract + internal sealed class IntegerDataContract : LongDataContract { internal IntegerDataContract() : base(DictionaryGlobals.integerLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class PositiveIntegerDataContract : LongDataContract + internal sealed class PositiveIntegerDataContract : LongDataContract { internal PositiveIntegerDataContract() : base(DictionaryGlobals.positiveIntegerLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NegativeIntegerDataContract : LongDataContract + internal sealed class NegativeIntegerDataContract : LongDataContract { internal NegativeIntegerDataContract() : base(DictionaryGlobals.negativeIntegerLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NonPositiveIntegerDataContract : LongDataContract + internal sealed class NonPositiveIntegerDataContract : LongDataContract { internal NonPositiveIntegerDataContract() : base(DictionaryGlobals.nonPositiveIntegerLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NonNegativeIntegerDataContract : LongDataContract + internal sealed class NonNegativeIntegerDataContract : LongDataContract { internal NonNegativeIntegerDataContract() : base(DictionaryGlobals.nonNegativeIntegerLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class UnsignedLongDataContract : PrimitiveDataContract + internal sealed class UnsignedLongDataContract : PrimitiveDataContract { public UnsignedLongDataContract() : base(typeof(ulong), DictionaryGlobals.UnsignedLongLocalName, DictionaryGlobals.SchemaNamespace) { @@ -484,7 +484,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class FloatDataContract : PrimitiveDataContract + internal sealed class FloatDataContract : PrimitiveDataContract { public FloatDataContract() : base(typeof(float), DictionaryGlobals.FloatLocalName, DictionaryGlobals.SchemaNamespace) { @@ -510,7 +510,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class DoubleDataContract : PrimitiveDataContract + internal sealed class DoubleDataContract : PrimitiveDataContract { public DoubleDataContract() : base(typeof(double), DictionaryGlobals.DoubleLocalName, DictionaryGlobals.SchemaNamespace) { @@ -536,7 +536,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class DecimalDataContract : PrimitiveDataContract + internal sealed class DecimalDataContract : PrimitiveDataContract { public DecimalDataContract() : base(typeof(decimal), DictionaryGlobals.DecimalLocalName, DictionaryGlobals.SchemaNamespace) { @@ -562,7 +562,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class DateTimeDataContract : PrimitiveDataContract + internal sealed class DateTimeDataContract : PrimitiveDataContract { public DateTimeDataContract() : base(typeof(DateTime), DictionaryGlobals.DateTimeLocalName, DictionaryGlobals.SchemaNamespace) { @@ -624,107 +624,107 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class TimeDataContract : StringDataContract + internal sealed class TimeDataContract : StringDataContract { internal TimeDataContract() : base(DictionaryGlobals.timeLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class DateDataContract : StringDataContract + internal sealed class DateDataContract : StringDataContract { internal DateDataContract() : base(DictionaryGlobals.dateLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class HexBinaryDataContract : StringDataContract + internal sealed class HexBinaryDataContract : StringDataContract { internal HexBinaryDataContract() : base(DictionaryGlobals.hexBinaryLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class GYearMonthDataContract : StringDataContract + internal sealed class GYearMonthDataContract : StringDataContract { internal GYearMonthDataContract() : base(DictionaryGlobals.gYearMonthLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class GYearDataContract : StringDataContract + internal sealed class GYearDataContract : StringDataContract { internal GYearDataContract() : base(DictionaryGlobals.gYearLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class GMonthDayDataContract : StringDataContract + internal sealed class GMonthDayDataContract : StringDataContract { internal GMonthDayDataContract() : base(DictionaryGlobals.gMonthDayLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class GDayDataContract : StringDataContract + internal sealed class GDayDataContract : StringDataContract { internal GDayDataContract() : base(DictionaryGlobals.gDayLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class GMonthDataContract : StringDataContract + internal sealed class GMonthDataContract : StringDataContract { internal GMonthDataContract() : base(DictionaryGlobals.gMonthLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NormalizedStringDataContract : StringDataContract + internal sealed class NormalizedStringDataContract : StringDataContract { internal NormalizedStringDataContract() : base(DictionaryGlobals.normalizedStringLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class TokenDataContract : StringDataContract + internal sealed class TokenDataContract : StringDataContract { internal TokenDataContract() : base(DictionaryGlobals.tokenLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class LanguageDataContract : StringDataContract + internal sealed class LanguageDataContract : StringDataContract { internal LanguageDataContract() : base(DictionaryGlobals.languageLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NameDataContract : StringDataContract + internal sealed class NameDataContract : StringDataContract { internal NameDataContract() : base(DictionaryGlobals.NameLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NCNameDataContract : StringDataContract + internal sealed class NCNameDataContract : StringDataContract { internal NCNameDataContract() : base(DictionaryGlobals.NCNameLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class IDDataContract : StringDataContract + internal sealed class IDDataContract : StringDataContract { internal IDDataContract() : base(DictionaryGlobals.XSDIDLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class IDREFDataContract : StringDataContract + internal sealed class IDREFDataContract : StringDataContract { internal IDREFDataContract() : base(DictionaryGlobals.IDREFLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class IDREFSDataContract : StringDataContract + internal sealed class IDREFSDataContract : StringDataContract { internal IDREFSDataContract() : base(DictionaryGlobals.IDREFSLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class ENTITYDataContract : StringDataContract + internal sealed class ENTITYDataContract : StringDataContract { internal ENTITYDataContract() : base(DictionaryGlobals.ENTITYLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class ENTITIESDataContract : StringDataContract + internal sealed class ENTITIESDataContract : StringDataContract { internal ENTITIESDataContract() : base(DictionaryGlobals.ENTITIESLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NMTOKENDataContract : StringDataContract + internal sealed class NMTOKENDataContract : StringDataContract { internal NMTOKENDataContract() : base(DictionaryGlobals.NMTOKENLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class NMTOKENSDataContract : StringDataContract + internal sealed class NMTOKENSDataContract : StringDataContract { internal NMTOKENSDataContract() : base(DictionaryGlobals.NMTOKENSLocalName, DictionaryGlobals.SchemaNamespace) { } } - internal class ByteArrayDataContract : PrimitiveDataContract + internal sealed class ByteArrayDataContract : PrimitiveDataContract { public ByteArrayDataContract() : base(typeof(byte[]), DictionaryGlobals.ByteArrayLocalName, DictionaryGlobals.SchemaNamespace) { @@ -758,7 +758,7 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class ObjectDataContract : PrimitiveDataContract + internal sealed class ObjectDataContract : PrimitiveDataContract { public ObjectDataContract() : base(typeof(object), DictionaryGlobals.ObjectLocalName, DictionaryGlobals.SchemaNamespace) { @@ -839,7 +839,7 @@ public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, Xml } } - internal class XsDurationDataContract : TimeSpanDataContract + internal sealed class XsDurationDataContract : TimeSpanDataContract { public XsDurationDataContract() : base(DictionaryGlobals.TimeSpanLocalName, DictionaryGlobals.SchemaNamespace) { } } @@ -874,12 +874,12 @@ public override void WriteXmlElement(XmlWriterDelegator xmlWriter, object? obj, } } - internal class AsmxGuidDataContract : GuidDataContract + internal sealed class AsmxGuidDataContract : GuidDataContract { internal AsmxGuidDataContract() : base(DictionaryGlobals.GuidLocalName, DictionaryGlobals.AsmxTypesNamespace) { } } - internal class UriDataContract : PrimitiveDataContract + internal sealed class UriDataContract : PrimitiveDataContract { public UriDataContract() : base(typeof(Uri), DictionaryGlobals.UriLocalName, DictionaryGlobals.SchemaNamespace) { @@ -911,7 +911,7 @@ public override void WriteXmlElement(XmlWriterDelegator writer, object? obj, Xml } } - internal class QNameDataContract : PrimitiveDataContract + internal sealed class QNameDataContract : PrimitiveDataContract { public QNameDataContract() : base(typeof(XmlQualifiedName), DictionaryGlobals.QNameLocalName, DictionaryGlobals.SchemaNamespace) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs index c5c343a1b8d52..d071056761ce3 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionFeature.cs @@ -3,7 +3,7 @@ namespace System.Runtime.Serialization { - internal class ReflectionBasedSerializationFeature + internal static class ReflectionBasedSerializationFeature { public const string Name = "System.Runtime.Serialization.ReflectionBasedSerialization"; } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs index 643f3a631001f..1e53b9f8bae61 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/ReflectionXmlFormatWriter.cs @@ -10,7 +10,7 @@ namespace System.Runtime.Serialization { - internal class ReflectionXmlFormatWriter + internal sealed class ReflectionXmlFormatWriter { private readonly ReflectionXmlClassWriter _reflectionClassWriter = new ReflectionXmlClassWriter(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs index 3e1b22ad958e1..c6d6daf0f4991 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SchemaExporter.cs @@ -16,7 +16,7 @@ namespace System.Runtime.Serialization { - internal class SchemaExporter + internal sealed class SchemaExporter { private readonly XmlSchemaSet _schemas; private XmlDocument? _xmlDoc; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs index 2db16229f71fc..38022467d2b6c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SpecialTypeDataContract.cs @@ -16,7 +16,7 @@ public SpecialTypeDataContract(Type type, XmlDictionaryString name, XmlDictionar public override bool IsBuiltInDataContract => true; - private class SpecialTypeDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class SpecialTypeDataContractCriticalHelper : DataContract.DataContractCriticalHelper { internal SpecialTypeDataContractCriticalHelper(Type type, XmlDictionaryString name, XmlDictionaryString ns) : base(type) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs index 1cd727072f8c5..c748213db1398 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/SurrogateDataContract.cs @@ -78,7 +78,7 @@ private void SerializationSurrogateGetObjectData(object obj, SerializationInfo s return newObj; } - private class SurrogateDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class SurrogateDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private readonly ISerializationSurrogate serializationSurrogate; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs index 7c0a0a9bb5376..eb7d871ecf1f4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XPathQueryGenerator.cs @@ -97,7 +97,7 @@ private static IEnumerable GetDataMembers(ClassDataContract contract } } - private class ExportContext + private sealed class ExportContext { private readonly XmlNamespaceManager _namespaces; private int _nextPrefix; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs index c8974ea1975f9..f47a2f0a9dc33 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlDataContract.cs @@ -111,7 +111,7 @@ internal CreateXmlSerializableDelegate CreateXmlSerializableDelegate public override bool IsBuiltInDataContract => UnderlyingType == Globals.TypeOfXmlElement || UnderlyingType == Globals.TypeOfXmlNodeArray; - private class XmlDataContractCriticalHelper : DataContract.DataContractCriticalHelper + private sealed class XmlDataContractCriticalHelper : DataContract.DataContractCriticalHelper { private DataContractDictionary? _knownDataContracts; private bool _isKnownTypeAttributeChecked; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs index 483f80392a910..0a716e0468ccc 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatReaderGenerator.cs @@ -50,7 +50,7 @@ public XmlFormatGetOnlyCollectionReaderDelegate GenerateGetOnlyCollectionReader( /// changes to how IL generated could affect how data is deserialized and what gets access to data, /// therefore we mark it for review so that changes to generation logic are reviewed. /// - private class CriticalHelper + private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXReader private LocalBuilder? _objectLocal; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs index 0dc6946c5029a..53895dab0532d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlFormatWriterGenerator.cs @@ -41,7 +41,7 @@ internal XmlFormatCollectionWriterDelegate GenerateCollectionWriter(CollectionDa /// changes to how IL generated could affect how data is serialized and what gets access to data, /// therefore we mark it for review so that changes to generation logic are reviewed. /// - private class CriticalHelper + private sealed class CriticalHelper { private CodeGenerator _ilg = null!; // initialized in GenerateXXXWriter private ArgBuilder _xmlWriterArg = null!; // initialized in InitArgs diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs index d5faeffd9ccb5..a6a66b6c44acd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableReader.cs @@ -10,7 +10,7 @@ namespace System.Runtime.Serialization { - internal class XmlSerializableReader : XmlReader, IXmlLineInfo + internal sealed class XmlSerializableReader : XmlReader, IXmlLineInfo { private XmlReaderDelegator _xmlReader = null!; // initialized in BeginRead private int _startDepth; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs index f127737dfa132..8dc67936bf496 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/XmlSerializableWriter.cs @@ -7,7 +7,7 @@ namespace System.Runtime.Serialization { - internal class XmlSerializableWriter : XmlWriter + internal sealed class XmlSerializableWriter : XmlWriter { private XmlWriter _xmlWriter = null!; // initialized in BeginWrite private int _depth; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Text/Base64Encoding.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Text/Base64Encoding.cs index c6d617d4e89d8..90e465237ed2c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Text/Base64Encoding.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Text/Base64Encoding.cs @@ -8,7 +8,7 @@ namespace System.Text { - internal class Base64Encoding : Encoding + internal sealed class Base64Encoding : Encoding { private static ReadOnlySpan Char2val => new byte[128] // rely on C# compiler optimization to eliminate allocation { @@ -186,7 +186,7 @@ public unsafe override int GetBytes(char[] chars, int charIndex, int charCount, } } - public unsafe virtual int GetBytes(byte[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + public unsafe int GetBytes(byte[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) { if (chars == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(chars))); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Text/BinHexEncoding.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Text/BinHexEncoding.cs index 959f916fcfaa2..2d2c21442d364 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Text/BinHexEncoding.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Text/BinHexEncoding.cs @@ -6,7 +6,7 @@ namespace System.Text { - internal class BinHexEncoding : Encoding + internal sealed class BinHexEncoding : Encoding { public override int GetMaxByteCount(int charCount) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs index 76e7640138cff..1522ba1e25a95 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ArrayHelper.cs @@ -94,7 +94,7 @@ public void WriteArray(XmlDictionaryWriter writer, string prefix, TArgument loca // Int8 is not supported since sbyte[] is non-CLS compliant, and uncommon // UniqueId is not supported since elements may be variable size strings - internal class BooleanArrayHelperWithString : ArrayHelper + internal sealed class BooleanArrayHelperWithString : ArrayHelper { public static readonly BooleanArrayHelperWithString Instance = new BooleanArrayHelperWithString(); @@ -109,7 +109,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class BooleanArrayHelperWithDictionaryString : ArrayHelper + internal sealed class BooleanArrayHelperWithDictionaryString : ArrayHelper { public static readonly BooleanArrayHelperWithDictionaryString Instance = new BooleanArrayHelperWithDictionaryString(); @@ -124,7 +124,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class Int16ArrayHelperWithString : ArrayHelper + internal sealed class Int16ArrayHelperWithString : ArrayHelper { public static readonly Int16ArrayHelperWithString Instance = new Int16ArrayHelperWithString(); @@ -139,7 +139,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class Int16ArrayHelperWithDictionaryString : ArrayHelper + internal sealed class Int16ArrayHelperWithDictionaryString : ArrayHelper { public static readonly Int16ArrayHelperWithDictionaryString Instance = new Int16ArrayHelperWithDictionaryString(); @@ -154,7 +154,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class Int32ArrayHelperWithString : ArrayHelper + internal sealed class Int32ArrayHelperWithString : ArrayHelper { public static readonly Int32ArrayHelperWithString Instance = new Int32ArrayHelperWithString(); @@ -169,7 +169,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class Int32ArrayHelperWithDictionaryString : ArrayHelper + internal sealed class Int32ArrayHelperWithDictionaryString : ArrayHelper { public static readonly Int32ArrayHelperWithDictionaryString Instance = new Int32ArrayHelperWithDictionaryString(); @@ -184,7 +184,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class Int64ArrayHelperWithString : ArrayHelper + internal sealed class Int64ArrayHelperWithString : ArrayHelper { public static readonly Int64ArrayHelperWithString Instance = new Int64ArrayHelperWithString(); @@ -199,7 +199,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class Int64ArrayHelperWithDictionaryString : ArrayHelper + internal sealed class Int64ArrayHelperWithDictionaryString : ArrayHelper { public static readonly Int64ArrayHelperWithDictionaryString Instance = new Int64ArrayHelperWithDictionaryString(); @@ -214,7 +214,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class SingleArrayHelperWithString : ArrayHelper + internal sealed class SingleArrayHelperWithString : ArrayHelper { public static readonly SingleArrayHelperWithString Instance = new SingleArrayHelperWithString(); @@ -229,7 +229,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class SingleArrayHelperWithDictionaryString : ArrayHelper + internal sealed class SingleArrayHelperWithDictionaryString : ArrayHelper { public static readonly SingleArrayHelperWithDictionaryString Instance = new SingleArrayHelperWithDictionaryString(); @@ -244,7 +244,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class DoubleArrayHelperWithString : ArrayHelper + internal sealed class DoubleArrayHelperWithString : ArrayHelper { public static readonly DoubleArrayHelperWithString Instance = new DoubleArrayHelperWithString(); @@ -259,7 +259,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class DoubleArrayHelperWithDictionaryString : ArrayHelper + internal sealed class DoubleArrayHelperWithDictionaryString : ArrayHelper { public static readonly DoubleArrayHelperWithDictionaryString Instance = new DoubleArrayHelperWithDictionaryString(); @@ -274,7 +274,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class DecimalArrayHelperWithString : ArrayHelper + internal sealed class DecimalArrayHelperWithString : ArrayHelper { public static readonly DecimalArrayHelperWithString Instance = new DecimalArrayHelperWithString(); @@ -289,7 +289,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class DecimalArrayHelperWithDictionaryString : ArrayHelper + internal sealed class DecimalArrayHelperWithDictionaryString : ArrayHelper { public static readonly DecimalArrayHelperWithDictionaryString Instance = new DecimalArrayHelperWithDictionaryString(); @@ -304,7 +304,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class DateTimeArrayHelperWithString : ArrayHelper + internal sealed class DateTimeArrayHelperWithString : ArrayHelper { public static readonly DateTimeArrayHelperWithString Instance = new DateTimeArrayHelperWithString(); @@ -319,7 +319,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class DateTimeArrayHelperWithDictionaryString : ArrayHelper + internal sealed class DateTimeArrayHelperWithDictionaryString : ArrayHelper { public static readonly DateTimeArrayHelperWithDictionaryString Instance = new DateTimeArrayHelperWithDictionaryString(); @@ -334,7 +334,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class GuidArrayHelperWithString : ArrayHelper + internal sealed class GuidArrayHelperWithString : ArrayHelper { public static readonly GuidArrayHelperWithString Instance = new GuidArrayHelperWithString(); @@ -349,7 +349,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class GuidArrayHelperWithDictionaryString : ArrayHelper + internal sealed class GuidArrayHelperWithDictionaryString : ArrayHelper { public static readonly GuidArrayHelperWithDictionaryString Instance = new GuidArrayHelperWithDictionaryString(); @@ -364,7 +364,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, Xm } } - internal class TimeSpanArrayHelperWithString : ArrayHelper + internal sealed class TimeSpanArrayHelperWithString : ArrayHelper { public static readonly TimeSpanArrayHelperWithString Instance = new TimeSpanArrayHelperWithString(); @@ -379,7 +379,7 @@ protected override void WriteArray(XmlDictionaryWriter writer, string prefix, st } } - internal class TimeSpanArrayHelperWithDictionaryString : ArrayHelper + internal sealed class TimeSpanArrayHelperWithDictionaryString : ArrayHelper { public static readonly TimeSpanArrayHelperWithDictionaryString Instance = new TimeSpanArrayHelperWithDictionaryString(); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs index 08416bf4ef91f..09af5cbd9a501 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/EncodingStreamWrapper.cs @@ -18,7 +18,7 @@ namespace System.Xml // ASSUMPTION (Microsoft): The byte buffer is large enough to hold the declaration // ASSUMPTION (Microsoft): The buffer manipulation methods (FillBuffer/Compare/etc.) will only be used to parse the declaration // during construction. - internal class EncodingStreamWrapper : Stream + internal sealed class EncodingStreamWrapper : Stream { private enum SupportedEncoding { UTF8, UTF16LE, UTF16BE, None } private static readonly UTF8Encoding s_safeUTF8 = new UTF8Encoding(false, false); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/PrefixHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/PrefixHandle.cs index 79742003b84f4..af24916d0dc0c 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/PrefixHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/PrefixHandle.cs @@ -16,7 +16,7 @@ internal enum PrefixHandleType Max, } - internal class PrefixHandle : IEquatable + internal sealed class PrefixHandle : IEquatable { private readonly XmlBufferReader _bufferReader; private PrefixHandleType _type; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs index 07d9bbb1ddac7..eacd50236de1e 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/StringHandle.cs @@ -15,7 +15,7 @@ internal enum StringHandleConstStringType Item = 2 } - internal class StringHandle : IEquatable + internal sealed class StringHandle : IEquatable { private readonly XmlBufferReader _bufferReader; private StringHandleType _type; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs index 94a4908b414ca..f09a06baa1572 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs @@ -65,7 +65,7 @@ internal enum ValueHandleType ConstString } - internal class ValueHandle + internal sealed class ValueHandle { private readonly XmlBufferReader _bufferReader; private ValueHandleType _type; @@ -773,7 +773,7 @@ public bool TryReadChars(char[] chars, int offset, int count, out int actual) else { DiagnosticUtility.DebugAssert(byteOffset + actualByteCount < bytes.Length, - string.Format("byteOffset {0} + actualByteCount {1} MUST BE < bytes.Length {2}", byteOffset, actualByteCount, bytes.Length)); + $"byteOffset {byteOffset} + actualByteCount {actualByteCount} MUST BE < bytes.Length {bytes.Length}"); // Request a few more bytes to get at least one character actualCharCount = decoder.GetChars(bytes, byteOffset + actualByteCount, 1, chars, charOffset); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs index bc1dceaad4490..0fe175fc22771 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseReader.cs @@ -2621,7 +2621,7 @@ public XmlClosedNode(XmlBufferReader bufferReader) } } - private class AttributeSorter : IComparer + private sealed class AttributeSorter : IComparer { private object[]? _indeces; private XmlAttributeNode[]? _attributeNodes; @@ -2719,7 +2719,7 @@ public int CompareQNameType(QNameType type1, QNameType type2) } } - private class NamespaceManager + private sealed class NamespaceManager { private readonly XmlBufferReader _bufferReader; private Namespace[]? _namespaces; @@ -3008,7 +3008,7 @@ private bool TryGetShortPrefix(string s, out PrefixHandleType shortPrefix) return false; } - private class XmlAttribute + private sealed class XmlAttribute { private XmlSpace _space; private string _lang = string.Empty; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs index a16eade973bf4..0b29ab21bfe30 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs @@ -1792,7 +1792,7 @@ private void WriteAttributeText(string value) _attributeValue += value; } - private class Element + private sealed class Element { private string? _prefix; private string? _localName; @@ -1850,7 +1850,7 @@ private enum DocumentState : byte End // Nothing further to write } - private class NamespaceManager + private sealed class NamespaceManager { private Namespace[]? _namespaces; private Namespace? _lastNameSpace; @@ -2301,7 +2301,7 @@ public void Sign(XmlCanonicalWriter signingWriter) } } - private class XmlAttribute + private sealed class XmlAttribute { private XmlSpace _space; private string? _lang; @@ -2353,7 +2353,7 @@ public void Clear() } } - private class Namespace + private sealed class Namespace { private string? _prefix; private string? _ns; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs index 7f7e0e38ab3e7..4505fb2d23612 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryReader.cs @@ -21,7 +21,7 @@ void SetInput(Stream stream, OnXmlDictionaryReaderClose? onClose); } - internal class XmlBinaryReader : XmlBaseReader, IXmlBinaryReaderInitializer + internal sealed class XmlBinaryReader : XmlBaseReader, IXmlBinaryReaderInitializer { private bool _isTextWithEndElement; private bool _buffered; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs index c353ce30366a8..53a9d45ffce18 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriter.cs @@ -20,7 +20,7 @@ public interface IXmlBinaryWriterInitializer void SetOutput(Stream stream, IXmlDictionary? dictionary, XmlBinaryWriterSession? session, bool ownsStream); } - internal class XmlBinaryNodeWriter : XmlStreamNodeWriter + internal sealed class XmlBinaryNodeWriter : XmlStreamNodeWriter { private IXmlDictionary? _dictionary; private XmlBinaryWriterSession? _session; @@ -1068,7 +1068,7 @@ public void WriteTo(XmlBinaryNodeWriter writer) } } - internal class XmlBinaryWriter : XmlBaseWriter, IXmlBinaryWriterInitializer + internal sealed class XmlBinaryWriter : XmlBaseWriter, IXmlBinaryWriterInitializer { private XmlBinaryNodeWriter _writer = null!; // initialized in SetOutput private char[]? _chars; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriterSession.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriterSession.cs index 1f2078d5bd79e..6ee7fa1614ab4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriterSession.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBinaryWriterSession.cs @@ -101,7 +101,7 @@ internal bool TryLookup(XmlDictionaryString s, out int key) return false; } - private class PriorityDictionary where K : class + private sealed class PriorityDictionary where K : class { private Dictionary? _dictionary; private readonly Entry[] _list; @@ -226,7 +226,7 @@ private struct Entry } } - private class IntArray + private sealed class IntArray { private int[] _array; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs index 20956f5a1c56d..dc124955be3bd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBufferReader.cs @@ -15,7 +15,7 @@ namespace System.Xml { - internal class XmlBufferReader + internal sealed class XmlBufferReader { private readonly XmlDictionaryReader _reader; private Stream? _stream; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs index b7c740ae5c974..24c718eb3bf58 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlCanonicalWriter.cs @@ -918,7 +918,7 @@ private bool Equals(byte[] buffer1, int offset1, int length1, byte[] buffer2, in return true; } - private class AttributeSorter : IComparer + private sealed class AttributeSorter : IComparer { private readonly XmlCanonicalWriter _writer; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionary.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionary.cs index 49d54b8691fca..d078ffcf5bff4 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionary.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionary.cs @@ -97,7 +97,7 @@ public virtual bool TryLookup(XmlDictionaryString value, [NotNullWhen(true)] out return true; } - private class EmptyDictionary : IXmlDictionary + private sealed class EmptyDictionary : IXmlDictionary { public bool TryLookup(string value, [NotNullWhen(true)] out XmlDictionaryString? result) { diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs index bf8e1bc8eb683..cbb815091a66a 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryAsyncCheckWriter.cs @@ -11,7 +11,7 @@ namespace System.Xml { - internal class XmlDictionaryAsyncCheckWriter : XmlDictionaryWriter + internal sealed class XmlDictionaryAsyncCheckWriter : XmlDictionaryWriter { private readonly XmlDictionaryWriter _coreWriter; private Task? _lastTask; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs index 160d6ff1498b7..13098a6aa2956 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryReader.cs @@ -1325,7 +1325,7 @@ public virtual int ReadArray(XmlDictionaryString localName, XmlDictionaryString return ReadArray(XmlDictionaryString.GetString(localName), XmlDictionaryString.GetString(namespaceUri), array, offset, count); } - private class XmlWrappedReader : XmlDictionaryReader, IXmlLineInfo + private sealed class XmlWrappedReader : XmlDictionaryReader, IXmlLineInfo { private readonly XmlReader _reader; private XmlNamespaceManager? _nsMgr; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryString.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryString.cs index 9adecff254248..1fe0b442bae66 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryString.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryString.cs @@ -86,7 +86,7 @@ public override string ToString() return _value; } - private class EmptyStringDictionary : IXmlDictionary + private sealed class EmptyStringDictionary : IXmlDictionary { private readonly XmlDictionaryString _empty; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs index 6d2fb2d345e8c..9c3b659ae760d 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlDictionaryWriter.cs @@ -642,7 +642,7 @@ protected override void Dispose(bool disposing) public override void Close() { } - private class XmlWrappedWriter : XmlDictionaryWriter + private sealed class XmlWrappedWriter : XmlDictionaryWriter { private readonly XmlWriter _writer; private int _depth; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlNodeWriter.cs index 8cdf0455382f8..88fb622bde8bd 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlNodeWriter.cs @@ -106,7 +106,7 @@ public virtual Task WriteBase64TextAsync(byte[] trailBuffer, int trailCount, byt } public abstract void WriteQualifiedName(string prefix, XmlDictionaryString localName); - private class XmlNullNodeWriter : XmlNodeWriter + private sealed class XmlNullNodeWriter : XmlNodeWriter { public override void Flush() { } public override void Close() { } diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs index fb539d2ac2ba1..5d25fcf4fb83f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlSigningNodeWriter.cs @@ -7,7 +7,7 @@ namespace System.Xml { - internal class XmlSigningNodeWriter : XmlNodeWriter + internal sealed class XmlSigningNodeWriter : XmlNodeWriter { private XmlNodeWriter _writer = null!; // initialized by SetOutput private XmlCanonicalWriter _signingWriter = null!; // initialized by SetOutput diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextReader.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextReader.cs index bfa8557cee4b8..7ddea5e25d53f 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextReader.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextReader.cs @@ -21,7 +21,7 @@ public interface IXmlTextReaderInitializer void SetInput(Stream stream, Encoding? encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose? onClose); } - internal class XmlUTF8TextReader : XmlBaseReader, IXmlLineInfo, IXmlTextReaderInitializer + internal sealed class XmlUTF8TextReader : XmlBaseReader, IXmlLineInfo, IXmlTextReaderInitializer { private const int MaxTextChunk = 2048; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs index 22e4d66296e80..34e38ec5a32d5 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlUTF8TextWriter.cs @@ -12,7 +12,7 @@ public interface IXmlTextWriterInitializer void SetOutput(Stream stream, Encoding encoding, bool ownsStream); } - internal class XmlUTF8TextWriter : XmlBaseWriter, IXmlTextWriterInitializer + internal sealed class XmlUTF8TextWriter : XmlBaseWriter, IXmlTextWriterInitializer { private XmlUTF8NodeWriter? _writer; diff --git a/src/libraries/System.Private.Uri/src/System/Uri.cs b/src/libraries/System.Private.Uri/src/System/Uri.cs index db7d057afbb4b..4b7e1f4ceff0f 100644 --- a/src/libraries/System.Private.Uri/src/System/Uri.cs +++ b/src/libraries/System.Private.Uri/src/System/Uri.cs @@ -144,7 +144,7 @@ internal void DebugAssertInCtor() Debug.Assert((_flags & Flags.Debug_LeftConstructor) == 0); } - private class UriInfo + private sealed class UriInfo { public Offset Offset; public string? String; @@ -184,7 +184,7 @@ private struct Offset public ushort End; }; - private class MoreInfo + private sealed class MoreInfo { public string? Path; public string? Query; @@ -2667,7 +2667,7 @@ private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat case UriFormat.UriEscaped: if (NotAny(Flags.UserEscaped)) { - UriHelper.EscapeString(slice, ref dest, checkExistingEscaped: true, '?', '#');; + UriHelper.EscapeString(slice, ref dest, checkExistingEscaped: true, '?', '#'); } else { diff --git a/src/libraries/System.Private.Uri/src/System/UriSyntax.cs b/src/libraries/System.Private.Uri/src/System/UriSyntax.cs index 1e5b70c0622d2..a1533657bc0ec 100644 --- a/src/libraries/System.Private.Uri/src/System/UriSyntax.cs +++ b/src/libraries/System.Private.Uri/src/System/UriSyntax.cs @@ -108,7 +108,7 @@ public abstract partial class UriParser internal const int NoDefaultPort = -1; private const int c_InitialTableSize = 25; - private class BuiltInUriParser : UriParser + private sealed class BuiltInUriParser : UriParser { // // All BuiltIn parsers use that ctor. They are marked with "simple" and "built-in" flags diff --git a/src/libraries/System.Private.Uri/tests/ExtendedFunctionalTests/UriRelativeResolutionTest.cs b/src/libraries/System.Private.Uri/tests/ExtendedFunctionalTests/UriRelativeResolutionTest.cs index 85f11a3ead40c..5ad415ba616e0 100644 --- a/src/libraries/System.Private.Uri/tests/ExtendedFunctionalTests/UriRelativeResolutionTest.cs +++ b/src/libraries/System.Private.Uri/tests/ExtendedFunctionalTests/UriRelativeResolutionTest.cs @@ -12,7 +12,7 @@ public class UriRelativeResolutionTest { // See RFC 3986 Section 5.2.2 and 5.4 http://www.ietf.org/rfc/rfc3986.txt - private readonly Uri _fullBaseUri = new Uri("http://user:psw@host:9090/path1/path2/path3/fileA?query#fragment"); + private readonly Uri _fullBaseUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/fileA?query#fragment"); [Fact] public void Uri_Relative_BaseVsAbsolute_ReturnsFullAbsolute() @@ -385,7 +385,7 @@ public void Uri_Relative_BaseVsSlashTrippleDotSlash_ReturnsSlashTrippleDotSlash( [Fact] public void Uri_Relative_BaseMadeRelativeToSamePath_ReturnsQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/fileA?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/fileA?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); string expectedResult = "?AQuery#AFragment"; // compareUri.GetParts(UriComponents.Query | UriComponents.Fragment,UriFormat.Unescaped); @@ -395,7 +395,7 @@ public void Uri_Relative_BaseMadeRelativeToSamePath_ReturnsQueryAndFragment() [Fact] public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -408,7 +408,7 @@ public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlashPlusQueryAnd [Fact] public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlash() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -420,7 +420,7 @@ public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlash() [Fact] public void Uri_Relative_BaseMadeRelativeToLastSlashWithExtra_ReturnsDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/Path4/fileb?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/Path4/fileb?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -432,7 +432,7 @@ public void Uri_Relative_BaseMadeRelativeToLastSlashWithExtra_ReturnsDotSlashPlu [Fact] public void Uri_Relative_BaseMadeRelativeToSecondToLastSlash_ReturnsDoubleDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -444,7 +444,7 @@ public void Uri_Relative_BaseMadeRelativeToSecondToLastSlash_ReturnsDoubleDotSla [Fact] public void Uri_Relative_BaseMadeRelativeToThirdToLastSlash_ReturnsDoubleDoubleDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -456,7 +456,7 @@ public void Uri_Relative_BaseMadeRelativeToThirdToLastSlash_ReturnsDoubleDoubleD [Fact] public void Uri_Relative_BaseMadeRelativeToEmptyPath_ReturnsTrippleDoubleDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderRefreshTest.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderRefreshTest.cs index 93a12c4260477..b2e6391d6b97d 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderRefreshTest.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderRefreshTest.cs @@ -7,7 +7,7 @@ namespace System.PrivateUri.Tests { public class UriBuilderRefreshTest { - private static readonly Uri s_starterUri = new Uri("http://user:psw@host:9090/path/file.txt?query#fragment"); + private static readonly Uri s_starterUri = new Uri("http://user:PLACEHOLDER@host:9090/path/file.txt?query#fragment"); [Fact] public void UriBuilder_ChangeScheme_Refreshed() diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs index cbfbaf38104b9..570894a66a1ed 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs @@ -214,7 +214,7 @@ public void UserName_Get_Set(string value, string expected) [InlineData(null, "")] public void Password_Get_Set(string value, string expected) { - var uriBuilder = new UriBuilder("http://userinfo1:userinfo2@domain/path?query#fragment"); + var uriBuilder = new UriBuilder("http://userinfo1:PLACEHOLDER@domain/path?query#fragment"); uriBuilder.Password = value; Assert.Equal(expected, uriBuilder.Password); diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs index ba545259aba1e..8a548c85abe0b 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriRelativeResolutionTest.cs @@ -16,10 +16,10 @@ public class UriRelativeResolutionTest { // See RFC 3986 Section 5.2.2 and 5.4 http://www.ietf.org/rfc/rfc3986.txt - private readonly Uri _fullBaseUri = new Uri("http://user:psw@host:9090/path1/path2/path3/fileA?query#fragment"); - private const string FullBaseUriGetLeftPart_Path = "http://user:psw@host:9090/path1/path2/path3/fileA"; - private const string FullBaseUriGetLeftPart_Authority = "http://user:psw@host:9090"; - private const string FullBaseUriGetLeftPart_Query = "http://user:psw@host:9090/path1/path2/path3/fileA?query"; + private readonly Uri _fullBaseUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/fileA?query#fragment"); + private const string FullBaseUriGetLeftPart_Path = "http://user:PLACEHOLDER@host:9090/path1/path2/path3/fileA"; + private const string FullBaseUriGetLeftPart_Authority = "http://user:PLACEHOLDER@host:9090"; + private const string FullBaseUriGetLeftPart_Query = "http://user:PLACEHOLDER@host:9090/path1/path2/path3/fileA?query"; [Fact] public void Uri_Relative_BaseVsAbsolute_ReturnsFullAbsolute() @@ -525,7 +525,7 @@ public void Uri_Relative_BaseVsSlashTrippleDotSlash_ReturnsSlashTrippleDotSlash( [Fact] public void Uri_Relative_BaseMadeRelativeToSamePath_ReturnsQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/fileA?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/fileA?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); string expectedResult = "?AQuery#AFragment"; // compareUri.GetParts(UriComponents.Query | UriComponents.Fragment,UriFormat.Unescaped); @@ -535,7 +535,7 @@ public void Uri_Relative_BaseMadeRelativeToSamePath_ReturnsQueryAndFragment() [Fact] public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -548,7 +548,7 @@ public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlashPlusQueryAnd [Fact] public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlash() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -560,7 +560,7 @@ public void Uri_Relative_BaseMadeRelativeToLastSlash_ReturnsDotSlash() [Fact] public void Uri_Relative_BaseMadeRelativeToLastSlashWithExtra_ReturnsDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/path3/Path4/fileb?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/path3/Path4/fileb?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -572,7 +572,7 @@ public void Uri_Relative_BaseMadeRelativeToLastSlashWithExtra_ReturnsDotSlashPlu [Fact] public void Uri_Relative_BaseMadeRelativeToSecondToLastSlash_ReturnsDoubleDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/path2/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/path2/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -584,7 +584,7 @@ public void Uri_Relative_BaseMadeRelativeToSecondToLastSlash_ReturnsDoubleDotSla [Fact] public void Uri_Relative_BaseMadeRelativeToThirdToLastSlash_ReturnsDoubleDoubleDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/path1/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/path1/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric @@ -596,7 +596,7 @@ public void Uri_Relative_BaseMadeRelativeToThirdToLastSlash_ReturnsDoubleDoubleD [Fact] public void Uri_Relative_BaseMadeRelativeToEmptyPath_ReturnsTrippleDoubleDotSlashPlusQueryAndFragment() { - Uri compareUri = new Uri("http://user:psw@host:9090/?AQuery#AFragment"); + Uri compareUri = new Uri("http://user:PLACEHOLDER@host:9090/?AQuery#AFragment"); Uri relative = _fullBaseUri.MakeRelativeUri(compareUri); Uri reassembled = new Uri(_fullBaseUri, relative); // Symetric diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs index 2b2222f315d82..24be0ef822743 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs @@ -750,6 +750,7 @@ public static void Uri_CachesDnsSafeHost() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49932", TestPlatforms.Android)] public static void Uri_DoesNotLockOnString() { // Don't intern the string we lock on diff --git a/src/libraries/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj b/src/libraries/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj index b83cd467a556f..c6a5b62dd33c2 100644 --- a/src/libraries/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj +++ b/src/libraries/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj @@ -5,9 +5,6 @@ ../../src/Resources/Strings.resx $(NetCoreAppCurrent) enable - - true diff --git a/src/libraries/System.Private.Xml.Linq/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.Private.Xml.Linq/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.Private.Xml.Linq/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.Private.Xml.Linq/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/BaseUriAnnotation.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/BaseUriAnnotation.cs index c61db3f8b6b7f..d690e8320eef5 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/BaseUriAnnotation.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/BaseUriAnnotation.cs @@ -3,7 +3,7 @@ namespace System.Xml.Linq { - internal class BaseUriAnnotation + internal sealed class BaseUriAnnotation { internal string baseUri; diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/LineInfoEndElementAnnotation.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/LineInfoEndElementAnnotation.cs index 9dce18477049e..f82be67c2fc85 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/LineInfoEndElementAnnotation.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/LineInfoEndElementAnnotation.cs @@ -8,7 +8,7 @@ namespace System.Xml.Linq /// if that element is not empty element and we want to store the line info /// for its end element tag. /// - internal class LineInfoEndElementAnnotation : LineInfoAnnotation + internal sealed class LineInfoEndElementAnnotation : LineInfoAnnotation { public LineInfoEndElementAnnotation(int lineNumber, int linePosition) : base(lineNumber, linePosition) diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs index b27a85a49ea15..84f4af1d57b76 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XLinq.cs @@ -408,7 +408,7 @@ private async Task WriteStartElementAsync(XElement e, CancellationToken cancella internal struct NamespaceResolver { - private class NamespaceDeclaration + private sealed class NamespaceDeclaration { public string prefix = null!; public XNamespace ns = null!; diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeBuilder.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeBuilder.cs index 027eafd2ba5e4..4e4164c04c326 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeBuilder.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeBuilder.cs @@ -6,7 +6,7 @@ namespace System.Xml.Linq { - internal class XNodeBuilder : XmlWriter + internal sealed class XNodeBuilder : XmlWriter { private List? _content; private XContainer? _parent; diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs index 161386bd1d27c..2ee9abc085136 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs @@ -5,7 +5,7 @@ namespace System.Xml.Linq { - internal class XNodeReader : XmlReader, IXmlLineInfo + internal sealed class XNodeReader : XmlReader, IXmlLineInfo { private static readonly char[] s_WhitespaceChars = new char[] { ' ', '\t', '\n', '\r' }; diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XObjectChangeAnnotation.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XObjectChangeAnnotation.cs index 54409b0e11a8a..4816511a54416 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XObjectChangeAnnotation.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XObjectChangeAnnotation.cs @@ -3,7 +3,7 @@ namespace System.Xml.Linq { - internal class XObjectChangeAnnotation + internal sealed class XObjectChangeAnnotation { internal EventHandler? changing; internal EventHandler? changed; diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Schema/XNodeValidator.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Schema/XNodeValidator.cs index 5d1f1208f4138..a1c1b986df663 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Schema/XNodeValidator.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Schema/XNodeValidator.cs @@ -12,7 +12,7 @@ namespace System.Xml.Schema { - internal class XNodeValidator + internal sealed class XNodeValidator { private readonly XmlSchemaSet schemas; private readonly ValidationEventHandler? validationEventHandler; @@ -379,7 +379,7 @@ private void ValidationCallback(object? sender, ValidationEventArgs e) } } - internal class XmlSchemaInfoEqualityComparer : IEqualityComparer + internal sealed class XmlSchemaInfoEqualityComparer : IEqualityComparer { public bool Equals(XmlSchemaInfo? si1, XmlSchemaInfo? si2) { diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs index 2455ff886c3cc..074dd99d4a9db 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/XPath/XNodeNavigator.cs @@ -8,7 +8,7 @@ namespace System.Xml.XPath { - internal class XNodeNavigator : XPathNavigator, IXmlLineInfo + internal sealed class XNodeNavigator : XPathNavigator, IXmlLineInfo { internal static readonly string xmlPrefixNamespace = XNamespace.Xml.NamespaceName; internal static readonly string xmlnsPrefixNamespace = XNamespace.Xmlns.NamespaceName; diff --git a/src/libraries/System.Private.Xml/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.Private.Xml/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.Private.Xml/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml deleted file mode 100644 index 5cc8e589f0faa..0000000000000 --- a/src/libraries/System.Private.Xml/src/ILLink/ILLink.Suppressions.xml +++ /dev/null @@ -1,359 +0,0 @@ - - - - - ILLink - IL2026 - member - M:System.Xml.Serialization.ReflectionAwareILGen.ILGenForCreateInstance(System.Xml.Serialization.CodeGenerator,System.Type,System.Type,System.Boolean) - - - ILLink - IL2026 - member - M:System.Xml.Serialization.TempAssembly.GetTypeFromAssembly(System.Reflection.Assembly,System.String) - - - ILLink - IL2026 - member - M:System.Xml.Serialization.TempAssembly.LoadGeneratedAssembly(System.Type,System.String,System.Xml.Serialization.XmlSerializerImplementation@) - - - ILLink - IL2026 - member - M:System.Xml.Serialization.XmlAttributes.get_IgnoreAttribute - - - ILLink - IL2057 - member - M:System.Xml.Serialization.XmlSerializationWriterCodeGen.WriteCheckDefault(System.Xml.Serialization.TypeMapping,System.String,System.Object,System.Boolean) - - - ILLink - IL2060 - member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.GetSetMemberValueDelegate(System.Object,System.String) - - - ILLink - IL2067 - member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.ReflectionCreateObject(System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.Compiler.AddImport(System.Type,System.Collections.Hashtable) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionAwareCodeGen.WriteMappingInfo(System.Xml.Serialization.TypeMapping,System.String,System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionAwareCodeGen.WriteMemberInfo(System.Type,System.String,System.String,System.String) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionAwareILGen.ILGenForCreateInstance(System.Xml.Serialization.CodeGenerator,System.Type,System.Boolean,System.Boolean) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionAwareILGen.ILGenForCreateInstance(System.Xml.Serialization.CodeGenerator,System.Type,System.Type,System.Boolean) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionXmlSerializationHelper.GetMember(System.Type,System.String) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.AddObjectsIntoTargetCollection(System.Object,System.Collections.Generic.List{System.Object},System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.GetDefaultConstructor(System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.SourceInfo.ConvertNullableValue(System.Type,System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.TempAssembly.GetMethodFromType(System.Type,System.String) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.TypeExtensions.TryConvertTo(System.Type,System.Object,System.Object@) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.TypeScope.GetConstructorFlags(System.Type,System.Exception@) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.TypeScope.GetDefaultIndexer(System.Type,System.String) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.TypeScope.GetEnumeratorElementType(System.Type,System.Xml.Serialization.TypeFlags@) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.TypeScope.ShouldBeReplaced(System.Reflection.MemberInfo,System.Type,System.Reflection.MemberInfo@) - - - ILLink - IL2070 - member - M:System.Xml.Serialization.XmlReflectionImporter.GetMethodFromSchemaProvider(System.Xml.Serialization.XmlSchemaProviderAttribute,System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Xsl.Runtime.EarlyBoundInfo.#ctor(System.String,System.Type) - - - ILLink - IL2070 - member - M:System.Xml.Xsl.XslCompiledTransform.Load(System.Type) - - - ILLink - IL2072 - member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.WriteNullableMethod(System.Xml.Serialization.NullableMapping,System.Boolean,System.String) - - - ILLink - IL2072 - member - M:System.Xml.Serialization.TempAssembly.get_Contract - - - ILLink - IL2072 - member - M:System.Xml.Serialization.TempAssembly.LoadGeneratedAssembly(System.Type,System.String,System.Xml.Serialization.XmlSerializerImplementation@) - - - ILLink - IL2072 - member - M:System.Xml.Serialization.XmlSerializationILGen.GenerateTypedSerializer(System.String,System.String,System.Xml.Serialization.XmlMapping,System.Xml.Serialization.CodeIdentifiers,System.String,System.String,System.String) - - - ILLink - IL2072 - member - M:System.Xml.Xsl.XsltOld.Processor.#ctor(System.Xml.XPath.XPathNavigator,System.Xml.Xsl.XsltArgumentList,System.Xml.XmlResolver,System.Xml.Xsl.XsltOld.Stylesheet,System.Collections.Generic.List{System.Xml.Xsl.XsltOld.TheQuery},System.Xml.Xsl.XsltOld.RootAction,System.Xml.Xsl.XsltOld.Debugger.IXsltDebugger) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.CodeGenerator.GetPropertyMethodFromBaseType(System.Reflection.PropertyInfo,System.Boolean) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.EnumModel.get_Constants - - - ILLink - IL2075 - member - M:System.Xml.Serialization.FieldModel.#ctor(System.Reflection.MemberInfo,System.Type,System.Xml.Serialization.TypeDesc) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.ReflectionAwareILGen.ILGenForCreateInstance(System.Xml.Serialization.CodeGenerator,System.Type,System.Type,System.Boolean) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.ReflectionAwareILGen.WriteLocalDecl(System.String,System.Xml.Serialization.SourceInfo) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.ReflectionXmlSerializationHelper.GetMember(System.Type,System.String) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.ReflectionXmlSerializationReader.WriteLiteralStructMethod(System.Xml.Serialization.StructMapping,System.Boolean,System.Boolean,System.String) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteStructMethod(System.Xml.Serialization.StructMapping,System.String,System.String,System.Object,System.Boolean,System.Boolean) - - - ILLink - IL2075 - member - T:System.Xml.Serialization.ReflectionXmlSerializationReader - - - ILLink - IL2075 - member - M:System.Xml.Serialization.SourceInfo.InternalLoad(System.Type,System.Boolean) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.StructModel.GetMemberInfos - - - ILLink - IL2075 - member - M:System.Xml.Serialization.TypeScope.GetEnumeratorElementType(System.Type,System.Xml.Serialization.TypeFlags@) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlReflectionImporter.GetChoiceIdentifierType(System.Xml.Serialization.XmlChoiceIdentifierAttribute,System.Xml.Serialization.StructModel,System.Boolean,System.String) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationILGen.GenerateBaseSerializer(System.String,System.String,System.String,System.Xml.Serialization.CodeIdentifiers) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationILGen.GenerateGetSerializer(System.Collections.Generic.Dictionary{System.String,System.String},System.Xml.Serialization.XmlMapping[],System.Reflection.Emit.TypeBuilder) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationILGen.GenerateSerializerContract(System.String,System.Xml.Serialization.XmlMapping[],System.Type[],System.String,System.String[],System.String,System.String[],System.Collections.Generic.Dictionary{System.String,System.String}) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationILGen.GenerateTypedSerializer(System.String,System.String,System.Xml.Serialization.XmlMapping,System.Xml.Serialization.CodeIdentifiers,System.String,System.String,System.String) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationILGen.GenerateTypedSerializers(System.Collections.Generic.Dictionary{System.String,System.String},System.Reflection.Emit.TypeBuilder) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationReaderILGen.WriteAttributes(System.Xml.Serialization.XmlSerializationReaderILGen.Member[],System.Xml.Serialization.XmlSerializationReaderILGen.Member,System.String,System.Reflection.Emit.LocalBuilder) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationReaderILGen.WriteSourceEnd(System.String,System.Type,System.Type) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationWriterILGen.WriteArrayItems(System.Xml.Serialization.ElementAccessor[],System.Xml.Serialization.TextAccessor,System.Xml.Serialization.ChoiceIdentifierAccessor,System.Xml.Serialization.TypeDesc,System.String,System.String) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationWriterILGen.WriteCheckDefault(System.Xml.Serialization.SourceInfo,System.Object,System.Boolean) - - - ILLink - IL2075 - member - M:System.Xml.Serialization.XmlSerializationWriterILGen.WriteElement(System.Xml.Serialization.SourceInfo,System.Xml.Serialization.ElementAccessor,System.String,System.Boolean) - - - ILLink - IL2075 - member - M:System.Xml.Xsl.IlGen.XmlILModule.BakeMethods - - - ILLink - IL2075 - member - M:System.Xml.Xsl.XsltOld.XsltCompileContext.GetExtentionMethod(System.String,System.String,System.Xml.XPath.XPathResultType[],System.Object@) - - - ILLink - IL2077 - member - M:System.Xml.Serialization.SerializableMapping.RetrieveSerializableSchema - - - ILLink - IL2080 - member - M:System.Xml.Serialization.XmlSerializationWriterILGen.WriteText(System.Xml.Serialization.SourceInfo,System.Xml.Serialization.TextAccessor) - - - ILLink - IL2080 - member - M:System.Xml.Xsl.Runtime.XmlExtensionFunction.Bind - - - ILLink - IL2080 - member - M:System.Xml.Xsl.Runtime.XmlExtensionFunction.CanBind - - - diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Base64Decoder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Base64Decoder.cs index d21d89e9e6ae7..e7ec9664f1939 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Base64Decoder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Base64Decoder.cs @@ -5,7 +5,7 @@ namespace System.Xml { - internal class Base64Decoder : IncrementalReadDecoder + internal sealed class Base64Decoder : IncrementalReadDecoder { // // Fields diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Base64Encoder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Base64Encoder.cs index 1e2212ab4d2c3..18d72d4778367 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Base64Encoder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Base64Encoder.cs @@ -105,7 +105,7 @@ internal void Flush() } } - internal partial class XmlRawWriterBase64Encoder : Base64Encoder + internal sealed partial class XmlRawWriterBase64Encoder : Base64Encoder { private readonly XmlRawWriter _rawWriter; @@ -120,7 +120,7 @@ internal override void WriteChars(char[] chars, int index, int count) } } - internal partial class XmlTextWriterBase64Encoder : Base64Encoder + internal sealed partial class XmlTextWriterBase64Encoder : Base64Encoder { private readonly XmlTextEncoder _xmlTextEncoder; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Base64EncoderAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Base64EncoderAsync.cs index 3c7f28c78a9dd..233b1a5044a39 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Base64EncoderAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Base64EncoderAsync.cs @@ -95,7 +95,7 @@ internal async Task FlushAsync() } } - internal partial class XmlTextWriterBase64Encoder : Base64Encoder + internal sealed partial class XmlTextWriterBase64Encoder : Base64Encoder { internal override Task WriteCharsAsync(char[] chars, int index, int count) { @@ -103,7 +103,7 @@ internal override Task WriteCharsAsync(char[] chars, int index, int count) } } - internal partial class XmlRawWriterBase64Encoder : Base64Encoder + internal sealed partial class XmlRawWriterBase64Encoder : Base64Encoder { internal override Task WriteCharsAsync(char[] chars, int index, int count) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/BinHexDecoder.cs b/src/libraries/System.Private.Xml/src/System/Xml/BinHexDecoder.cs index 32ca96a647d6e..90a9359058044 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/BinHexDecoder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/BinHexDecoder.cs @@ -6,7 +6,7 @@ namespace System.Xml { - internal class BinHexDecoder : IncrementalReadDecoder + internal sealed class BinHexDecoder : IncrementalReadDecoder { // // Fields diff --git a/src/libraries/System.Private.Xml/src/System/Xml/BinaryXml/XmlBinaryReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/BinaryXml/XmlBinaryReader.cs index d31976925b065..36d739c9dae89 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/BinaryXml/XmlBinaryReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/BinaryXml/XmlBinaryReader.cs @@ -215,7 +215,7 @@ public void AdjustPosition(int adj) } } - private class NamespaceDecl + private sealed class NamespaceDecl { public string prefix; public string uri; @@ -254,7 +254,7 @@ public void Init() } } - private class NestedBinXml + private sealed class NestedBinXml { public SymbolTables symbolTables; public int docState; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/BitStack.cs b/src/libraries/System.Private.Xml/src/System/Xml/BitStack.cs index b621a0037e09d..5482a325b88fa 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/BitStack.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/BitStack.cs @@ -9,7 +9,7 @@ namespace System.Xml /// /// Manages a stack of bits. Exposes push, pop, and peek operations. /// - internal class BitStack + internal sealed class BitStack { private uint[]? _bitStack; private int _stackPos; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/ByteStack.cs b/src/libraries/System.Private.Xml/src/System/Xml/ByteStack.cs index 4314b0cc8ecec..89b44fb79db4f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/ByteStack.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/ByteStack.cs @@ -12,7 +12,7 @@ namespace System.Xml // so that next time Push() is called it simply returns the last // object that was already on the stack. - internal class ByteStack + internal sealed class ByteStack { private byte[] _stack; private readonly int _growthRate; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentIterator.cs index fd68a47141d07..80c4ab6d2df75 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Cache/XPathDocumentIterator.cs @@ -52,7 +52,7 @@ public override int CurrentPosition /// /// Iterate over all element children with a particular QName. /// - internal class XPathDocumentElementChildIterator : XPathDocumentBaseIterator + internal sealed class XPathDocumentElementChildIterator : XPathDocumentBaseIterator { private readonly string? _localName; private readonly string _namespaceUri; @@ -110,7 +110,7 @@ public override bool MoveNext() /// /// Iterate over all content children with a particular XPathNodeType. /// - internal class XPathDocumentKindChildIterator : XPathDocumentBaseIterator + internal sealed class XPathDocumentKindChildIterator : XPathDocumentBaseIterator { private readonly XPathNodeType _typ; @@ -163,7 +163,7 @@ public override bool MoveNext() /// /// Iterate over all element descendants with a particular QName. /// - internal class XPathDocumentElementDescendantIterator : XPathDocumentBaseIterator + internal sealed class XPathDocumentElementDescendantIterator : XPathDocumentBaseIterator { private readonly XPathDocumentNavigator? _end; private readonly string? _localName; @@ -236,7 +236,7 @@ public override bool MoveNext() /// /// Iterate over all content descendants with a particular XPathNodeType. /// - internal class XPathDocumentKindDescendantIterator : XPathDocumentBaseIterator + internal sealed class XPathDocumentKindDescendantIterator : XPathDocumentBaseIterator { private readonly XPathDocumentNavigator? _end; private readonly XPathNodeType _typ; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs index c7b9b6315af08..cfbdc9e8a0d53 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/CharEntityEncoderFallback.cs @@ -10,7 +10,7 @@ namespace System.Xml // // CharEntityEncoderFallback // - internal class CharEntityEncoderFallback : EncoderFallback + internal sealed class CharEntityEncoderFallback : EncoderFallback { private CharEntityEncoderFallbackBuffer? _fallbackBuffer; @@ -78,7 +78,7 @@ internal bool CanReplaceAt(int index) // // CharEntityFallbackBuffer // - internal class CharEntityEncoderFallbackBuffer : EncoderFallbackBuffer + internal sealed class CharEntityEncoderFallbackBuffer : EncoderFallbackBuffer { private readonly CharEntityEncoderFallback _parent; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs index 55bf9134f9094..b4210f187a411 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlEncodedRawTextWriter.cs @@ -786,7 +786,7 @@ private void OutputRestAmps() // // 4). SE SC same as above EE a). check stored blockPro // b). true: indentLevel no change - internal class HtmlEncodedRawTextWriterIndent : HtmlEncodedRawTextWriter + internal sealed class HtmlEncodedRawTextWriterIndent : HtmlEncodedRawTextWriter { // // Fields diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs index 55294ffdd97ea..2d3597534cb58 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/HtmlUtf8RawTextWriter.cs @@ -763,7 +763,7 @@ private void OutputRestAmps() // // 4). SE SC same as above EE a). check stored blockPro // b). true: indentLevel no change - internal class HtmlUtf8RawTextWriterIndent : HtmlUtf8RawTextWriter + internal sealed class HtmlUtf8RawTextWriterIndent : HtmlUtf8RawTextWriter { // // Fields diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/IncrementalReadDecoders.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/IncrementalReadDecoders.cs index 804790302addd..0db640ed5520e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/IncrementalReadDecoders.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/IncrementalReadDecoders.cs @@ -22,7 +22,7 @@ internal abstract class IncrementalReadDecoder // // Dummy IncrementalReadDecoder // - internal class IncrementalReadDummyDecoder : IncrementalReadDecoder + internal sealed class IncrementalReadDummyDecoder : IncrementalReadDecoder { internal override int DecodedCount { get { return -1; } } internal override bool IsFull { get { return false; } } @@ -35,7 +35,7 @@ internal override void Reset() { } // // IncrementalReadDecoder for ReadChars // - internal class IncrementalReadCharsDecoder : IncrementalReadDecoder + internal sealed class IncrementalReadCharsDecoder : IncrementalReadDecoder { private char[]? _buffer; private int _startIndex; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs index 1c6a61c43beb3..8bc1f54dc8b50 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs @@ -5,7 +5,7 @@ namespace System { - internal partial class LocalAppContextSwitches + internal static partial class LocalAppContextSwitches { private static int s_dontThrowOnInvalidSurrogatePairs; public static bool DontThrowOnInvalidSurrogatePairs diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriter.cs index 86e8bd878a676..48f952a9e513f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriter.cs @@ -19,7 +19,7 @@ namespace System.Xml /// /// It also performs well-formed document checks if standalone="yes" and/or a doc-type-decl is output. /// - internal class QueryOutputWriter : XmlRawWriter + internal sealed class QueryOutputWriter : XmlRawWriter { private readonly XmlRawWriter _wrapped; private bool _inCDataSection; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriterV1.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriterV1.cs index 88cf3c9f91571..02288d196166e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriterV1.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/QueryOutputWriterV1.cs @@ -20,7 +20,7 @@ namespace System.Xml /// It also calls WriteStateDocument if standalone="yes" and/or a DocType declaration is written out in order to enforce document conformance /// checking. /// - internal class QueryOutputWriterV1 : XmlWriter + internal sealed class QueryOutputWriterV1 : XmlWriter { private readonly XmlWriter _wrapped; private bool _inCDataSection; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelper.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelper.cs index 043e9118ec91b..99ca6edc3049a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelper.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelper.cs @@ -5,7 +5,7 @@ namespace System.Xml { - internal partial class ReadContentAsBinaryHelper + internal sealed partial class ReadContentAsBinaryHelper { // Private enums private enum State diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelperAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelperAsync.cs index 9b2492bebc40b..8a596529f426e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelperAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadContentAsBinaryHelperAsync.cs @@ -6,7 +6,7 @@ namespace System.Xml { - internal partial class ReadContentAsBinaryHelper + internal sealed partial class ReadContentAsBinaryHelper { // Internal methods diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs index 5b0d0ea23deba..f345e151b61cb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/ReadOnlyTernaryTree.cs @@ -27,7 +27,7 @@ internal enum AttributeProperties : uint { DEFAULT = 0, URI = 1, BOOLEAN = 2, NA * * Note: Only strings composed of ASCII characters can exist in the tree. */ - internal class TernaryTreeReadOnly + internal sealed class TernaryTreeReadOnly { private readonly byte[] _nodeBuffer; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/TextEncodedRawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/TextEncodedRawTextWriter.cs index 113c95cec4fa0..04be69b71795a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/TextEncodedRawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/TextEncodedRawTextWriter.cs @@ -19,7 +19,7 @@ namespace System.Xml // Encoder class to output to any encoding. The TextUtf8RawTextWriter class combined the encoding // operation with serialization in order to achieve better performance. // - internal class TextEncodedRawTextWriter : XmlEncodedRawTextWriter + internal sealed class TextEncodedRawTextWriter : XmlEncodedRawTextWriter { // Construct an instance of this class that outputs text to the TextWriter interface. public TextEncodedRawTextWriter(TextWriter writer, XmlWriterSettings settings) : base(writer, settings) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/TextUtf8RawTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/TextUtf8RawTextWriter.cs index f0b317f3d54b8..a2efd3f57f46d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/TextUtf8RawTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/TextUtf8RawTextWriter.cs @@ -19,7 +19,7 @@ namespace System.Xml // Encoder class to output to any encoding. The TextUtf8RawTextWriter class combined the encoding // operation with serialization in order to achieve better performance. // - internal class TextUtf8RawTextWriter : XmlUtf8RawTextWriter + internal sealed class TextUtf8RawTextWriter : XmlUtf8RawTextWriter { // Construct an instance of this class that serializes to a Stream interface. public TextUtf8RawTextWriter(Stream stream, XmlWriterSettings settings) : base(stream, settings) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/ValidatingReaderNodeData.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/ValidatingReaderNodeData.cs index 74d47994fbb59..f6fef4cb4beb6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/ValidatingReaderNodeData.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/ValidatingReaderNodeData.cs @@ -12,7 +12,7 @@ namespace System.Xml { - internal class ValidatingReaderNodeData + internal sealed class ValidatingReaderNodeData { private string _localName; private string _namespaceUri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckReader.cs index 1569a36b4d36f..3a1764e5b2c3f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckReader.cs @@ -914,7 +914,7 @@ public override Task ReadOuterXmlAsync() #endregion } - internal class XmlAsyncCheckReaderWithNS : XmlAsyncCheckReader, IXmlNamespaceResolver + internal sealed class XmlAsyncCheckReaderWithNS : XmlAsyncCheckReader, IXmlNamespaceResolver { private readonly IXmlNamespaceResolver _readerAsIXmlNamespaceResolver; @@ -1004,7 +1004,7 @@ IDictionary IXmlNamespaceResolver.GetNamespacesInScope(XmlNamesp #endregion } - internal class XmlAsyncCheckReaderWithLineInfoNSSchema : XmlAsyncCheckReaderWithLineInfoNS, IXmlSchemaInfo + internal sealed class XmlAsyncCheckReaderWithLineInfoNSSchema : XmlAsyncCheckReaderWithLineInfoNS, IXmlSchemaInfo { private readonly IXmlSchemaInfo _readerAsIXmlSchemaInfo; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs index d96b1b1621dbb..fb59fb91910c2 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAsyncCheckWriter.cs @@ -6,7 +6,7 @@ namespace System.Xml { - internal class XmlAsyncCheckWriter : XmlWriter + internal sealed class XmlAsyncCheckWriter : XmlWriter { private readonly XmlWriter _coreWriter; private Task _lastTask = Task.CompletedTask; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAutoDetectWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAutoDetectWriter.cs index 78b163d37906b..70d649f86b615 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAutoDetectWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlAutoDetectWriter.cs @@ -16,7 +16,7 @@ namespace System.Xml /// This writer implements XmlOutputMethod.AutoDetect. If the first element is "html", then output will be /// directed to an Html writer. Otherwise, output will be directed to an Xml writer. /// - internal class XmlAutoDetectWriter : XmlRawWriter, IRemovableWriter + internal sealed class XmlAutoDetectWriter : XmlRawWriter, IRemovableWriter { private XmlRawWriter? _wrapped; private OnRemoveWriter? _onRemove; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs index 2f9708a1131e0..38d606efedb80 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingReader.cs @@ -663,7 +663,7 @@ private void FinishReadBinary() // // XmlCharCheckingReaderWithNS // - internal class XmlCharCheckingReaderWithNS : XmlCharCheckingReader, IXmlNamespaceResolver + internal sealed class XmlCharCheckingReaderWithNS : XmlCharCheckingReader, IXmlNamespaceResolver { internal IXmlNamespaceResolver readerAsNSResolver; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriter.cs index f3cb979c70f0e..2174c3017b124 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriter.cs @@ -14,7 +14,7 @@ namespace System.Xml // // XmlCharCheckingWriter // - internal partial class XmlCharCheckingWriter : XmlWrappingWriter + internal sealed partial class XmlCharCheckingWriter : XmlWrappingWriter { // // Fields diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriterAsync.cs index 8d3b8c4934d85..dc58522f067f4 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlCharCheckingWriterAsync.cs @@ -15,7 +15,7 @@ namespace System.Xml // // XmlCharCheckingWriter // - internal partial class XmlCharCheckingWriter : XmlWrappingWriter + internal sealed partial class XmlCharCheckingWriter : XmlWrappingWriter { public override Task WriteDocTypeAsync(string name, string? pubid, string? sysid, string? subset) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlSubtreeReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlSubtreeReader.cs index 5e395fe8a4386..8d9718c698716 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlSubtreeReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlSubtreeReader.cs @@ -15,7 +15,7 @@ internal sealed partial class XmlSubtreeReader : XmlWrappingReader, IXmlLineInfo // // Private types // - private class NodeData + private sealed class NodeData { internal XmlNodeType type; internal string localName; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs index a25a9f08501aa..e03320af38639 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextEncoder.cs @@ -13,7 +13,7 @@ namespace System.Xml // // This class does special handling of text content for XML. For example // it will replace special characters with entities whenever necessary. - internal class XmlTextEncoder + internal sealed class XmlTextEncoder { // // Fields diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.Unix.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.Unix.cs index 4321e78151e17..a1605b1c4ada2 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.Unix.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.Unix.cs @@ -5,7 +5,7 @@ namespace System.Xml { - internal partial class XmlTextReaderImpl + internal sealed partial class XmlTextReaderImpl { static partial void ConvertAbsoluteUnixPathToAbsoluteUri([NotNullIfNotNull("url")] ref string? url, XmlResolver? resolver) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs index 6927a9e9452ec..d8e0f167f8970 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImpl.cs @@ -14,7 +14,7 @@ namespace System.Xml { - internal partial class XmlTextReaderImpl : XmlReader, IXmlLineInfo, IXmlNamespaceResolver + internal sealed partial class XmlTextReaderImpl : XmlReader, IXmlLineInfo, IXmlNamespaceResolver { private static UTF8Encoding? s_utf8BomThrowing; @@ -115,7 +115,7 @@ private enum IncrementalReadState //later init means in the construction stage, do not open filestream and do not read any data from Stream/TextReader //the purpose is to make the Create of XmlReader do not block on IO. - private class LaterInitParam + private sealed class LaterInitParam { public bool useAsync; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs index 646b3309d7c09..06dfee97fbd89 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplAsync.cs @@ -17,7 +17,7 @@ namespace System.Xml { - internal partial class XmlTextReaderImpl : XmlReader, IXmlLineInfo, IXmlNamespaceResolver + internal sealed partial class XmlTextReaderImpl : XmlReader, IXmlLineInfo, IXmlNamespaceResolver { private void CheckAsyncCall() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpers.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpers.cs index b44ef13593419..c2feee7131890 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpers.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpers.cs @@ -15,7 +15,7 @@ namespace System.Xml { - internal partial class XmlTextReaderImpl + internal sealed partial class XmlTextReaderImpl { // // ParsingState @@ -119,7 +119,7 @@ internal int LinePos // // XmlContext // - private class XmlContext + private sealed class XmlContext { internal XmlSpace xmlSpace; internal string xmlLang; @@ -146,7 +146,7 @@ internal XmlContext(XmlContext previousContext) // // NoNamespaceManager // - private class NoNamespaceManager : XmlNamespaceManager + private sealed class NoNamespaceManager : XmlNamespaceManager { public NoNamespaceManager() : base() { } public override string DefaultNamespace { get { return string.Empty; } } @@ -164,7 +164,7 @@ public override void RemoveNamespace(string prefix, string uri) { } // // DtdParserProxy: IDtdParserAdapter proxy for XmlTextReaderImpl // - internal partial class DtdParserProxy : IDtdParserAdapterV1 + internal sealed partial class DtdParserProxy : IDtdParserAdapterV1 { // Fields private readonly XmlTextReaderImpl _reader; @@ -330,7 +330,7 @@ bool IDtdParserAdapterV1.V1CompatibilityMode // // NodeData // - private class NodeData : IComparable + private sealed class NodeData : IComparable { // static instance with no data - is used when XmlTextReader is closed private static volatile NodeData? s_None; @@ -720,7 +720,7 @@ int IComparable.CompareTo(object? obj) // DtdDefaultAttributeInfoToNodeDataComparer // // Compares IDtdDefaultAttributeInfo to NodeData - private class DtdDefaultAttributeInfoToNodeDataComparer : IComparer + private sealed class DtdDefaultAttributeInfoToNodeDataComparer : IComparer { private static readonly IComparer s_instance = new DtdDefaultAttributeInfoToNodeDataComparer(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpersAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpersAsync.cs index e877cf316e079..f6f2d8c60e2f5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpersAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextReaderImplHelpersAsync.cs @@ -16,12 +16,12 @@ namespace System.Xml { - internal partial class XmlTextReaderImpl + internal sealed partial class XmlTextReaderImpl { // // DtdParserProxy: IDtdParserAdapter proxy for XmlTextReaderImpl // - internal partial class DtdParserProxy : IDtdParserAdapterV1 + internal sealed partial class DtdParserProxy : IDtdParserAdapterV1 { Task IDtdParserAdapter.ReadDataAsync() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlValidatingReaderImpl.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlValidatingReaderImpl.cs index 5cf5a335aa40e..c2cd3871de4c0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlValidatingReaderImpl.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlValidatingReaderImpl.cs @@ -32,7 +32,7 @@ private enum ParsingFunction None, } - internal class ValidationEventHandling : IValidationEventHandling + internal sealed class ValidationEventHandling : IValidationEventHandling { // Fields private readonly XmlValidatingReaderImpl _reader; @@ -833,7 +833,7 @@ internal event ValidationEventHandler ValidationEventHandler } remove { - _eventHandling.RemoveHandler(value); ; + _eventHandling.RemoveHandler(value); } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs index 1b1471ae6ae7b..f4ff03f0bf01d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs @@ -12,7 +12,7 @@ namespace System.Xml { - internal partial class XmlWellFormedWriter : XmlWriter + internal sealed partial class XmlWellFormedWriter : XmlWriter { // // Private types used by the XmlWellFormedWriter are defined in XmlWellFormedWriterHelpers.cs diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs index 20c3a31f29cf5..0e3ef48bc3acc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterAsync.cs @@ -17,7 +17,7 @@ namespace System.Xml { - internal partial class XmlWellFormedWriter : XmlWriter + internal sealed partial class XmlWellFormedWriter : XmlWriter { public override Task WriteStartDocumentAsync() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpers.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpers.cs index 866f25c4ea61e..e905d9663c363 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpers.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpers.cs @@ -9,12 +9,12 @@ namespace System.Xml { - internal partial class XmlWellFormedWriter : XmlWriter + internal sealed partial class XmlWellFormedWriter : XmlWriter { // // Private types // - private class NamespaceResolverProxy : IXmlNamespaceResolver + private sealed class NamespaceResolverProxy : IXmlNamespaceResolver { private readonly XmlWellFormedWriter _wfWriter; @@ -146,7 +146,7 @@ private enum SpecialAttribute XmlLang } - private partial class AttributeValueCache + private sealed partial class AttributeValueCache { private enum ItemType { @@ -161,7 +161,7 @@ private enum ItemType ValueString, } - private class Item + private sealed class Item { internal ItemType type; internal object data; @@ -180,7 +180,7 @@ internal void Set(ItemType type, object data) } } - private class BufferChunk + private sealed class BufferChunk { internal char[] buffer; internal int index; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpersAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpersAsync.cs index 36b337574fb38..fb81eb472358c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpersAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriterHelpersAsync.cs @@ -10,7 +10,7 @@ namespace System.Xml { - internal partial class XmlWellFormedWriter : XmlWriter + internal sealed partial class XmlWellFormedWriter : XmlWriter { private partial struct ElementScope { @@ -50,7 +50,7 @@ internal async Task WriteDeclAsync(XmlWriter writer, XmlRawWriter? rawWriter) } } - private partial class AttributeValueCache + private sealed partial class AttributeValueCache { internal async Task ReplayAsync(XmlWriter writer) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReader.cs index a5a87a9a41ad7..f363a07f02f41 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReader.cs @@ -12,7 +12,7 @@ namespace System.Xml { - internal partial class XsdCachingReader : XmlReader, IXmlLineInfo + internal sealed partial class XsdCachingReader : XmlReader, IXmlLineInfo { private enum CachingReaderState { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReaderAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReaderAsync.cs index 50123abbbf0f6..211b27d067995 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReaderAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdCachingReaderAsync.cs @@ -13,7 +13,7 @@ namespace System.Xml { - internal partial class XsdCachingReader : XmlReader, IXmlLineInfo + internal sealed partial class XsdCachingReader : XmlReader, IXmlLineInfo { // Gets the text value of the current node. public override Task GetValueAsync() diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs index cfdb657bd9055..294b174638b79 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReader.cs @@ -16,7 +16,7 @@ namespace System.Xml { internal delegate void CachingEventHandler(XsdCachingReader cachingReader); - internal class AttributePSVIInfo + internal sealed class AttributePSVIInfo { internal string? localName; internal string? namespaceUri; @@ -37,7 +37,7 @@ internal void Reset() } } - internal partial class XsdValidatingReader : XmlReader, IXmlSchemaInfo, IXmlLineInfo, IXmlNamespaceResolver + internal sealed partial class XsdValidatingReader : XmlReader, IXmlSchemaInfo, IXmlLineInfo, IXmlNamespaceResolver { private enum ValidatingReaderState { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReaderAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReaderAsync.cs index daa599cecf443..97b475c53dbe4 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReaderAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/XsdValidatingReaderAsync.cs @@ -15,7 +15,7 @@ namespace System.Xml { - internal partial class XsdValidatingReader : XmlReader, IXmlSchemaInfo, IXmlLineInfo, IXmlNamespaceResolver + internal sealed partial class XsdValidatingReader : XmlReader, IXmlSchemaInfo, IXmlLineInfo, IXmlNamespaceResolver { // Gets the text value of the current node. public override Task GetValueAsync() @@ -269,7 +269,7 @@ private Task ReadAsync_ReadAhead(Task task) if (task.IsSuccess()) { _validationState = ValidatingReaderState.Read; - return AsyncHelper.DoneTaskTrue; ; + return AsyncHelper.DoneTaskTrue; } else { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/DomNameTable.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/DomNameTable.cs index 748f0c3d0b278..069d4d564d9d8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/DomNameTable.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/DomNameTable.cs @@ -7,7 +7,7 @@ namespace System.Xml { - internal class DomNameTable + internal sealed class DomNameTable { private XmlName[] _entries; private int _count; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XPathNodeList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XPathNodeList.cs index 11c01d42464f1..ab098fb8b771c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XPathNodeList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XPathNodeList.cs @@ -8,7 +8,7 @@ namespace System.Xml using System.Collections; using System.Collections.Generic; - internal class XPathNodeList : XmlNodeList + internal sealed class XPathNodeList : XmlNodeList { private readonly List _list; private readonly XPathNodeIterator _nodeIterator; @@ -83,7 +83,7 @@ public override IEnumerator GetEnumerator() } } - internal class XmlNodeListEnumerator : IEnumerator + internal sealed class XmlNodeListEnumerator : IEnumerator { private readonly XPathNodeList _list; private int _index; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlChildNodes.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlChildNodes.cs index d696646e9540e..c6e3b1e3ac629 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlChildNodes.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlChildNodes.cs @@ -5,7 +5,7 @@ namespace System.Xml { - internal class XmlChildNodes : XmlNodeList + internal sealed class XmlChildNodes : XmlNodeList { private readonly XmlNode _container; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlDomTextWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlDomTextWriter.cs index daa4156695673..1ccfffdd75261 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlDomTextWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlDomTextWriter.cs @@ -9,7 +9,7 @@ namespace System.Xml // Represents a writer that will make it possible to work with prefixes even // if the namespace is not specified. // This is not possible with XmlTextWriter. But this class inherits XmlTextWriter. - internal class XmlDOMTextWriter : XmlTextWriter + internal sealed class XmlDOMTextWriter : XmlTextWriter { public XmlDOMTextWriter(Stream w, Encoding? encoding) : base(w, encoding) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlElementList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlElementList.cs index a809e4b2f701a..1ba414135acae 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlElementList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlElementList.cs @@ -6,7 +6,7 @@ namespace System.Xml { - internal class XmlElementList : XmlNodeList + internal sealed class XmlElementList : XmlNodeList { private readonly string _asterisk = null!; private int _changeCount; //recording the total number that the dom tree has been changed ( insertion and deletion ) @@ -280,7 +280,7 @@ protected override void PrivateDisposeNodeList() Dispose(true); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (_listener != null) { @@ -294,7 +294,7 @@ protected virtual void Dispose(bool disposing) } } - internal class XmlElementListEnumerator : IEnumerator + internal sealed class XmlElementListEnumerator : IEnumerator { private readonly XmlElementList _list; private XmlNode? _curElem; @@ -334,7 +334,7 @@ public object? Current } } - internal class XmlEmptyElementListEnumerator : IEnumerator + internal sealed class XmlEmptyElementListEnumerator : IEnumerator { public XmlEmptyElementListEnumerator(XmlElementList list) { @@ -355,7 +355,7 @@ public object? Current } } - internal class XmlElementListListener + internal sealed class XmlElementListListener { private WeakReference? _elemList; private readonly XmlDocument _doc; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlLoader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlLoader.cs index 75145a14e671d..bb9ad265843a3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlLoader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlLoader.cs @@ -10,7 +10,7 @@ namespace System.Xml { - internal class XmlLoader + internal sealed class XmlLoader { private XmlDocument? _doc; private XmlReader? _reader; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNamedNodeMap.SmallXmlNodeList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNamedNodeMap.SmallXmlNodeList.cs index cb217eb5b6c22..021941038895b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNamedNodeMap.SmallXmlNodeList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNamedNodeMap.SmallXmlNodeList.cs @@ -140,7 +140,7 @@ public void Insert(int index, object value) } } - private class SingleObjectEnumerator : IEnumerator + private sealed class SingleObjectEnumerator : IEnumerator { private readonly object _loneValue; private int _position = -1; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNodeReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNodeReader.cs index 705ad125aafb2..0fceef11c1b1f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNodeReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlNodeReader.cs @@ -12,7 +12,7 @@ namespace System.Xml using System.Xml.Schema; using System.Globalization; - internal class XmlNodeReaderNavigator + internal sealed class XmlNodeReaderNavigator { private XmlNode _curNode; private XmlNode? _elemNode; @@ -1771,7 +1771,7 @@ public override void ResolveEntity() { if (!IsInReadingStates() || (_nodeType != XmlNodeType.EntityReference)) throw new InvalidOperationException(SR.Xnr_ResolveEntity); - _bResolveEntity = true; ; + _bResolveEntity = true; } // Parses the attribute value into one or more Text and/or diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlUnspecifiedAttribute.cs b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlUnspecifiedAttribute.cs index a98cd608e0d13..4e5ffc51f176f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlUnspecifiedAttribute.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlUnspecifiedAttribute.cs @@ -3,12 +3,12 @@ namespace System.Xml { - internal class XmlUnspecifiedAttribute : XmlAttribute + internal sealed class XmlUnspecifiedAttribute : XmlAttribute { private bool _fSpecified; - protected internal XmlUnspecifiedAttribute(string? prefix, string localName, string? namespaceURI, XmlDocument doc) + internal XmlUnspecifiedAttribute(string? prefix, string localName, string? namespaceURI, XmlDocument doc) : base(prefix, localName, namespaceURI, doc) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/HWStack.cs b/src/libraries/System.Private.Xml/src/System/Xml/HWStack.cs index 7ba3e31f4d514..c87e8a89e5bc0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/HWStack.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/HWStack.cs @@ -12,7 +12,7 @@ namespace System.Xml // so that next time Push() is called it simply returns the last // object that was already on the stack. - internal class HWStack : ICloneable + internal sealed class HWStack : ICloneable { internal HWStack(int GrowthRate) : this(GrowthRate, int.MaxValue) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/IXmlLineInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/IXmlLineInfo.cs index 87246bb3258e6..b0e643eda6ee1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/IXmlLineInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/IXmlLineInfo.cs @@ -30,7 +30,7 @@ public static PositionInfo GetPositionInfo(object o) } } - internal class ReaderPositionInfo : PositionInfo + internal sealed class ReaderPositionInfo : PositionInfo { private readonly IXmlLineInfo _lineInfo; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/MTNameTable.cs b/src/libraries/System.Private.Xml/src/System/Xml/MTNameTable.cs index b10c97ca25fce..2ba73b0e3de37 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/MTNameTable.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/MTNameTable.cs @@ -599,7 +599,7 @@ private static Int64 Hash(char[] key, int start, int len) { // A MTNameTable node. - internal class MTNameTableNode { + internal sealed class MTNameTableNode { internal String value; internal Int64 hash; internal Int64 counter; @@ -676,7 +676,7 @@ private Int64 Compare(Int64 hash, char[] key, int start, int len) { // Enumerates all the names (strings) of a MTNameTable - internal class MTNameTableEnumerator: IEnumerator { + internal sealed class MTNameTableEnumerator: IEnumerator { private ArrayList names; private int iName; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/NameTable.cs b/src/libraries/System.Private.Xml/src/System/Xml/NameTable.cs index a4fc696917b25..e2d7fb2dfa0cc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/NameTable.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/NameTable.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.InteropServices; - namespace System.Xml { /// @@ -15,13 +13,13 @@ public class NameTable : XmlNameTable // // Private types // - private class Entry + private sealed class Entry { internal string str; internal int hashCode; - internal Entry next; + internal Entry? next; - internal Entry(string str, int hashCode, Entry next) + internal Entry(string str, int hashCode, Entry? next) { this.str = str; this.hashCode = hashCode; @@ -32,7 +30,7 @@ internal Entry(string str, int hashCode, Entry next) // // Fields // - private Entry[] _entries; + private Entry?[] _entries; private int _count; private int _mask; @@ -44,7 +42,7 @@ internal Entry(string str, int hashCode, Entry next) public NameTable() { _mask = 31; - _entries = new Entry[_mask + 1]; + _entries = new Entry?[_mask + 1]; } // @@ -68,7 +66,7 @@ public override string Add(string key) int hashCode = ComputeHash32(key); - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) + for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next) { if (e.hashCode == hashCode && e.str.Equals(key)) { @@ -104,11 +102,11 @@ public override string Add(char[] key, int start, int len) throw new ArgumentOutOfRangeException(nameof(len)); } - int hashCode = ComputeHash32(key, start, len); + int hashCode = string.GetHashCode(key.AsSpan(start, len)); - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) + for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next) { - if (e.hashCode == hashCode && TextEquals(e.str, key, start, len)) + if (e.hashCode == hashCode && e.str.AsSpan().SequenceEqual(key.AsSpan(start, len))) { return e.str; } @@ -134,7 +132,7 @@ public override string Add(char[] key, int start, int len) int hashCode = ComputeHash32(value); - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) + for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next) { if (e.hashCode == hashCode && e.str.Equals(value)) { @@ -167,11 +165,11 @@ public override string Add(char[] key, int start, int len) return null; } - int hashCode = ComputeHash32(key, start, len); + int hashCode = string.GetHashCode(key.AsSpan(start, len)); - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) + for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next) { - if (e.hashCode == hashCode && TextEquals(e.str, key, start, len)) + if (e.hashCode == hashCode && e.str.AsSpan().SequenceEqual(key.AsSpan(start, len))) { return e.str; } @@ -182,7 +180,7 @@ public override string Add(char[] key, int start, int len) internal string GetOrAddEntry(string str, int hashCode) { - for (Entry e = _entries[hashCode & _mask]; e != null; e = e.next) + for (Entry? e = _entries[hashCode & _mask]; e != null; e = e.next) { if (e.hashCode == hashCode && e.str.Equals(str)) { @@ -223,17 +221,17 @@ private string AddEntry(string str, int hashCode) private void Grow() { int newMask = _mask * 2 + 1; - Entry[] oldEntries = _entries; - Entry[] newEntries = new Entry[newMask + 1]; + Entry?[] oldEntries = _entries; + Entry?[] newEntries = new Entry?[newMask + 1]; // use oldEntries.Length to eliminate the range check for (int i = 0; i < oldEntries.Length; i++) { - Entry e = oldEntries[i]; + Entry? e = oldEntries[i]; while (e != null) { int newIndex = e.hashCode & newMask; - Entry tmp = e.next; + Entry? tmp = e.next; e.next = newEntries[newIndex]; newEntries[newIndex] = e; e = tmp; @@ -243,30 +241,5 @@ private void Grow() _entries = newEntries; _mask = newMask; } - - private static bool TextEquals(string str1, char[] str2, int str2Start, int str2Length) - { - if (str1.Length != str2Length) - { - return false; - } - - // use array.Length to eliminate the range check - for (int i = 0; i < str1.Length; i++) - { - if (str1[i] != str2[str2Start + i]) - { - return false; - } - } - return true; - } - - private static int ComputeHash32(char[] key, int start, int len) - { - // We rely on string.GetHashCode(ROS) being randomized. - - return string.GetHashCode(key.AsSpan(start, len)); - } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Resolvers/XmlPreloadedResolver.cs b/src/libraries/System.Private.Xml/src/System/Xml/Resolvers/XmlPreloadedResolver.cs index de8e7d4a1d425..1ee3949536b80 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Resolvers/XmlPreloadedResolver.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Resolvers/XmlPreloadedResolver.cs @@ -48,7 +48,7 @@ internal virtual bool SupportsType(Type? type) // // XmlKnownDtdData class // - private class XmlKnownDtdData : PreloadedData + private sealed class XmlKnownDtdData : PreloadedData { internal string publicId; internal string systemId; @@ -68,7 +68,7 @@ internal override Stream AsStream() } } - private class ByteArrayChunk : PreloadedData + private sealed class ByteArrayChunk : PreloadedData { private readonly byte[] _array; private readonly int _offset; @@ -92,7 +92,7 @@ internal override Stream AsStream() } } - private class StringData : PreloadedData + private sealed class StringData : PreloadedData { private readonly string _str; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Asttree.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Asttree.cs index 277e7045124ad..e510b22373784 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Asttree.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Asttree.cs @@ -18,7 +18,7 @@ namespace System.Xml.Schema // stack element class // this one needn't change, even the parameter in methods - internal class AxisElement + internal sealed class AxisElement { internal DoubleLinkAxis curNode; // current under-checking node during navigating internal int rootDepth; // root depth -- contextDepth + 1 if ! isDss; context + {1...} if isDss @@ -136,7 +136,7 @@ internal bool MoveToChild(string name, string? URN, int depth, ForwardAxis paren } } - internal class AxisStack + internal sealed class AxisStack { // property private readonly ArrayList _stack; // of AxisElement @@ -404,7 +404,7 @@ public bool MoveToAttribute(string localname, string? URN) * ---------------------------------------------------------------------------------------------- */ // each node in the xpath tree - internal class DoubleLinkAxis : Axis + internal sealed class DoubleLinkAxis : Axis { internal Axis? next; @@ -444,7 +444,7 @@ internal DoubleLinkAxis(Axis axis, DoubleLinkAxis? inputaxis) // only keep axis, rootNode, isAttribute, isDss inside // act as an element tree for the Asttree - internal class ForwardAxis + internal sealed class ForwardAxis { // Axis tree private readonly DoubleLinkAxis _topNode; @@ -497,7 +497,7 @@ public ForwardAxis(DoubleLinkAxis axis, bool isdesorself) } // static, including an array of ForwardAxis (this is the whole picture) - internal class Asttree + internal sealed class Asttree { // set private then give out only get access, to keep it intact all along private ArrayList _fAxisArray = null!; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/AutoValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/AutoValidator.cs index eb87b34495f88..ff86563f69112 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/AutoValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/AutoValidator.cs @@ -8,7 +8,7 @@ namespace System.Xml.Schema #pragma warning disable 618 - internal class AutoValidator : BaseValidator + internal sealed class AutoValidator : BaseValidator { public AutoValidator(XmlValidatingReaderImpl reader, XmlSchemaCollection schemaCollection, IValidationEventHandling eventHandling) : base(reader, schemaCollection, eventHandling) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Chameleonkey.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Chameleonkey.cs index a147f5a964777..d90325692b779 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Chameleonkey.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Chameleonkey.cs @@ -11,7 +11,7 @@ namespace System.Xml.Schema { // Case insensitive file name key for use in a hashtable. - internal class ChameleonKey + internal sealed class ChameleonKey { internal string targetNS; internal Uri chameleonLocation; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/CompiledidEntityConstraint.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/CompiledidEntityConstraint.cs index 7370dd125e2a3..586c0a49d17ff 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/CompiledidEntityConstraint.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/CompiledidEntityConstraint.cs @@ -9,7 +9,7 @@ namespace System.Xml.Schema using System.Xml.XPath; using MS.Internal.Xml.XPath; - internal class CompiledIdentityConstraint + internal sealed class CompiledIdentityConstraint { internal XmlQualifiedName name = XmlQualifiedName.Empty; private readonly ConstraintRole _role; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ConstraintStruct.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ConstraintStruct.cs index 15148e530cddc..badf0548d9181 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ConstraintStruct.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ConstraintStruct.cs @@ -41,7 +41,7 @@ internal ConstraintStruct(CompiledIdentityConstraint constraint) } // ActiveAxis plus the location plus the state of matching in the constraint table : only for field - internal class LocatedActiveAxis : ActiveAxis + internal sealed class LocatedActiveAxis : ActiveAxis { private readonly int _column; // the column in the table (the field sequence) internal bool isMatched; // if it's matched, then fill value in the validator later @@ -76,7 +76,7 @@ internal void Reactivate(KeySequence ks) // 6. taking care of updating ConstraintStruct.axisFields // 7. remove constraintTable from ConstraintStruct // 8. still need centralized locatedactiveaxis for movetoattribute purpose - internal class SelectorActiveAxis : ActiveAxis + internal sealed class SelectorActiveAxis : ActiveAxis { private readonly ConstraintStruct _cs; // pointer of constraintstruct, to enable 6 private readonly ArrayList _KSs; // stack of KSStruct, will not become less @@ -145,7 +145,7 @@ public KeySequence PopKS() } } - internal class KSStruct + internal sealed class KSStruct { public int depth; // depth of selector when it matches public KeySequence ks; // ks of selector when it matches and assigned -- needs to new each time @@ -158,9 +158,9 @@ public KSStruct(KeySequence ks, int dim) } } - internal class TypedObject + internal sealed class TypedObject { - private class DecimalStruct + private sealed class DecimalStruct { private bool _isDecimal; // rare case it will be used... private readonly decimal[] _dvalue; // to accelerate equals operation. array <-> list @@ -386,7 +386,7 @@ public bool Equals(TypedObject other) } } - internal class KeySequence + internal sealed class KeySequence { private readonly TypedObject[] _ks; private readonly int _dim; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs index 5b3f63a96ee90..2e2baa402875a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/ContentValidator.cs @@ -15,7 +15,7 @@ namespace System.Xml.Schema /// /// UPA violations will throw this exception /// - internal class UpaException : Exception + internal sealed class UpaException : Exception { private readonly object? _particle1; private readonly object? _particle2; @@ -36,7 +36,7 @@ public UpaException(object? particle1, object? particle2) /// SymbolsDictionary always recognizes all the symbols - the last one is a true wildcard - /// both name and namespace can be anything that none of the other symbols matched. /// - internal class SymbolsDictionary + internal sealed class SymbolsDictionary { private int _last; private readonly Hashtable _names; @@ -253,7 +253,7 @@ public Position(int symbol, object? particle) } } - internal class Positions + internal sealed class Positions { private readonly ArrayList _positions = new ArrayList(); @@ -362,10 +362,10 @@ public override void Dump(StringBuilder bb, SymbolsDictionary symbols, Positions /// /// Temporary node to represent NamespaceList. Will be expended as a choice of symbols /// - internal class NamespaceListNode : SyntaxTreeNode + internal sealed class NamespaceListNode : SyntaxTreeNode { - protected NamespaceList namespaceList; - protected object particle; + private NamespaceList namespaceList; + private object particle; public NamespaceListNode(NamespaceList namespaceList, object particle) { @@ -373,7 +373,7 @@ public NamespaceListNode(NamespaceList namespaceList, object particle) this.particle = particle; } - public virtual ICollection GetResolvedSymbols(SymbolsDictionary symbols) + public ICollection GetResolvedSymbols(SymbolsDictionary symbols) { return symbols.GetNamespaceListSymbols(namespaceList); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs index 29310bc5b6282..e7523cb04ff51 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DataTypeImplementation.cs @@ -22,7 +22,7 @@ public enum XmlSchemaDatatypeVariety Union } - internal class XsdSimpleValue + internal sealed class XsdSimpleValue { //Wrapper to store XmlType and TypedValue together private readonly XmlSchemaSimpleType _xmlType; private readonly object _typedValue; @@ -75,7 +75,7 @@ internal enum XmlSchemaWhiteSpace Collapse, } - internal class RestrictionFacets + internal sealed class RestrictionFacets { internal int Length; internal int MinLength; @@ -716,7 +716,7 @@ internal string GetTypeName() private static readonly DatatypeImplementation s_yearMonthDuration = new Datatype_yearMonthDuration(); - private class SchemaDatatypeMap : IComparable + private sealed class SchemaDatatypeMap : IComparable { private readonly string _name; private readonly DatatypeImplementation _type; @@ -919,7 +919,7 @@ protected object GetValueToCheck(object value, IXmlNamespaceResolver nsmgr) { //List type - internal class Datatype_List : Datatype_anySimpleType + internal sealed class Datatype_List : Datatype_anySimpleType { private readonly DatatypeImplementation _itemType; private readonly int _minListSize; @@ -1178,7 +1178,7 @@ internal override RestrictionFlags ValidRestrictionFlags } //Union datatype - internal class Datatype_union : Datatype_anySimpleType + internal sealed class Datatype_union : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(object); private static readonly Type s_listValueType = typeof(object[]); @@ -1425,7 +1425,7 @@ internal override XmlValueConverter CreateValueConverter(XmlSchemaType schemaTyp public override XmlTypeCode TypeCode { get { return XmlTypeCode.AnyAtomicType; } } } - internal class Datatype_untypedAtomicType : Datatype_anyAtomicType + internal sealed class Datatype_untypedAtomicType : Datatype_anyAtomicType { internal override XmlValueConverter CreateValueConverter(XmlSchemaType schemaType) { @@ -1528,7 +1528,7 @@ internal override RestrictionFlags ValidRestrictionFlags */ - internal class Datatype_boolean : Datatype_anySimpleType + internal sealed class Datatype_boolean : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(bool); private static readonly Type s_listValueType = typeof(bool[]); @@ -1950,7 +1950,7 @@ internal override int Compare(object value1, object value2) } } - internal class Datatype_yearMonthDuration : Datatype_duration + internal sealed class Datatype_yearMonthDuration : Datatype_duration { internal override Exception? TryParseValue(string s, XmlNameTable? nameTable, IXmlNamespaceResolver? nsmgr, out object? typedValue) { @@ -1988,7 +1988,7 @@ internal class Datatype_yearMonthDuration : Datatype_duration public override XmlTypeCode TypeCode { get { return XmlTypeCode.YearMonthDuration; } } } - internal class Datatype_dayTimeDuration : Datatype_duration + internal sealed class Datatype_dayTimeDuration : Datatype_duration { internal override Exception? TryParseValue(string s, XmlNameTable? nameTable, IXmlNamespaceResolver? nsmgr, out object? typedValue) { @@ -2116,12 +2116,12 @@ internal override int Compare(object value1, object value2) } } - internal class Datatype_dateTimeNoTimeZone : Datatype_dateTimeBase + internal sealed class Datatype_dateTimeNoTimeZone : Datatype_dateTimeBase { internal Datatype_dateTimeNoTimeZone() : base(XsdDateTimeFlags.XdrDateTimeNoTz) { } } - internal class Datatype_dateTimeTimeZone : Datatype_dateTimeBase + internal sealed class Datatype_dateTimeTimeZone : Datatype_dateTimeBase { internal Datatype_dateTimeTimeZone() : base(XsdDateTimeFlags.XdrDateTime) { } } @@ -2152,17 +2152,17 @@ internal Datatype_dateTimeTimeZone() : base(XsdDateTimeFlags.XdrDateTime) { } */ - internal class Datatype_dateTime : Datatype_dateTimeBase + internal sealed class Datatype_dateTime : Datatype_dateTimeBase { internal Datatype_dateTime() : base(XsdDateTimeFlags.DateTime) { } } - internal class Datatype_timeNoTimeZone : Datatype_dateTimeBase + internal sealed class Datatype_timeNoTimeZone : Datatype_dateTimeBase { internal Datatype_timeNoTimeZone() : base(XsdDateTimeFlags.XdrTimeNoTz) { } } - internal class Datatype_timeTimeZone : Datatype_dateTimeBase + internal sealed class Datatype_timeTimeZone : Datatype_dateTimeBase { internal Datatype_timeTimeZone() : base(XsdDateTimeFlags.Time) { } } @@ -2193,7 +2193,7 @@ internal Datatype_timeTimeZone() : base(XsdDateTimeFlags.Time) { } */ - internal class Datatype_time : Datatype_dateTimeBase + internal sealed class Datatype_time : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Time; } } @@ -2226,7 +2226,7 @@ internal Datatype_time() : base(XsdDateTimeFlags.Time) { } */ - internal class Datatype_date : Datatype_dateTimeBase + internal sealed class Datatype_date : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Date; } } @@ -2259,7 +2259,7 @@ internal Datatype_date() : base(XsdDateTimeFlags.Date) { } */ - internal class Datatype_yearMonth : Datatype_dateTimeBase + internal sealed class Datatype_yearMonth : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.GYearMonth; } } @@ -2293,7 +2293,7 @@ internal Datatype_yearMonth() : base(XsdDateTimeFlags.GYearMonth) { } */ - internal class Datatype_year : Datatype_dateTimeBase + internal sealed class Datatype_year : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.GYear; } } @@ -2326,7 +2326,7 @@ internal Datatype_year() : base(XsdDateTimeFlags.GYear) { } */ - internal class Datatype_monthDay : Datatype_dateTimeBase + internal sealed class Datatype_monthDay : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.GMonthDay; } } @@ -2359,7 +2359,7 @@ internal Datatype_monthDay() : base(XsdDateTimeFlags.GMonthDay) { } */ - internal class Datatype_day : Datatype_dateTimeBase + internal sealed class Datatype_day : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.GDay; } } @@ -2393,7 +2393,7 @@ internal Datatype_day() : base(XsdDateTimeFlags.GDay) { } */ - internal class Datatype_month : Datatype_dateTimeBase + internal sealed class Datatype_month : Datatype_dateTimeBase { public override XmlTypeCode TypeCode { get { return XmlTypeCode.GMonth; } } @@ -2425,7 +2425,7 @@ internal Datatype_month() : base(XsdDateTimeFlags.GMonth) { } */ - internal class Datatype_hexBinary : Datatype_anySimpleType + internal sealed class Datatype_hexBinary : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(byte[]); private static readonly Type s_listValueType = typeof(byte[][]); @@ -2526,7 +2526,7 @@ internal override int Compare(object value1, object value2) */ - internal class Datatype_base64Binary : Datatype_anySimpleType + internal sealed class Datatype_base64Binary : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(byte[]); private static readonly Type s_listValueType = typeof(byte[][]); @@ -2626,7 +2626,7 @@ internal override int Compare(object value1, object value2) */ - internal class Datatype_anyURI : Datatype_anySimpleType + internal sealed class Datatype_anyURI : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(Uri); private static readonly Type s_listValueType = typeof(Uri[]); @@ -2724,7 +2724,7 @@ internal override int Compare(object value1, object value2) */ - internal class Datatype_QName : Datatype_anySimpleType + internal sealed class Datatype_QName : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(XmlQualifiedName); private static readonly Type s_listValueType = typeof(XmlQualifiedName[]); @@ -2858,7 +2858,7 @@ internal class Datatype_token : Datatype_normalizedString internal override XmlSchemaWhiteSpace BuiltInWhitespaceFacet { get { return XmlSchemaWhiteSpace.Collapse; } } } - internal class Datatype_tokenV1Compat : Datatype_normalizedStringV1Compat + internal sealed class Datatype_tokenV1Compat : Datatype_normalizedStringV1Compat { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Token; } } } @@ -2884,7 +2884,7 @@ and RFC 1766 */ - internal class Datatype_language : Datatype_token + internal sealed class Datatype_language : Datatype_token { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Language; } } } @@ -2990,7 +2990,7 @@ internal class Datatype_NCName : Datatype_Name */ - internal class Datatype_ID : Datatype_NCName + internal sealed class Datatype_ID : Datatype_NCName { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Id; } } @@ -3006,7 +3006,7 @@ internal class Datatype_ID : Datatype_NCName */ - internal class Datatype_IDREF : Datatype_NCName + internal sealed class Datatype_IDREF : Datatype_NCName { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Idref; } } @@ -3022,7 +3022,7 @@ internal class Datatype_IDREF : Datatype_NCName */ - internal class Datatype_ENTITY : Datatype_NCName + internal sealed class Datatype_ENTITY : Datatype_NCName { public override XmlTypeCode TypeCode { get { return XmlTypeCode.Entity; } } @@ -3060,7 +3060,7 @@ facet whose value is the name of a NOTATION declared in the */ - internal class Datatype_NOTATION : Datatype_anySimpleType + internal sealed class Datatype_NOTATION : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(XmlQualifiedName); private static readonly Type s_listValueType = typeof(XmlQualifiedName[]); @@ -3240,7 +3240,7 @@ internal override bool HasValueFacets */ - internal class Datatype_negativeInteger : Datatype_nonPositiveInteger + internal sealed class Datatype_negativeInteger : Datatype_nonPositiveInteger { private static readonly FacetsChecker s_numeric10FacetsChecker = new Numeric10FacetsChecker(decimal.MinValue, decimal.MinusOne); @@ -3443,7 +3443,7 @@ internal override int Compare(object value1, object value2) */ - internal class Datatype_byte : Datatype_short + internal sealed class Datatype_byte : Datatype_short { private static readonly Type s_atomicValueType = typeof(sbyte); private static readonly Type s_listValueType = typeof(sbyte[]); @@ -3698,7 +3698,7 @@ internal override int Compare(object value1, object value2) */ - internal class Datatype_unsignedByte : Datatype_unsignedShort + internal sealed class Datatype_unsignedByte : Datatype_unsignedShort { private static readonly Type s_atomicValueType = typeof(byte); private static readonly Type s_listValueType = typeof(byte[]); @@ -3753,7 +3753,7 @@ internal override int Compare(object value1, object value2) */ - internal class Datatype_positiveInteger : Datatype_nonNegativeInteger + internal sealed class Datatype_positiveInteger : Datatype_nonNegativeInteger { private static readonly FacetsChecker s_numeric10FacetsChecker = new Numeric10FacetsChecker(decimal.One, decimal.MaxValue); @@ -3765,7 +3765,7 @@ internal class Datatype_positiveInteger : Datatype_nonNegativeInteger /* XDR */ - internal class Datatype_doubleXdr : Datatype_double + internal sealed class Datatype_doubleXdr : Datatype_double { public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespaceResolver? nsmgr) { @@ -3787,7 +3787,7 @@ public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespa } } - internal class Datatype_floatXdr : Datatype_float + internal sealed class Datatype_floatXdr : Datatype_float { public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespaceResolver? nsmgr) { @@ -3809,7 +3809,7 @@ public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespa } } - internal class Datatype_QNameXdr : Datatype_anySimpleType + internal sealed class Datatype_QNameXdr : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(XmlQualifiedName); private static readonly Type s_listValueType = typeof(XmlQualifiedName[]); @@ -3846,12 +3846,12 @@ public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespa internal override Type ListValueType { get { return s_listValueType; } } } - internal class Datatype_ENUMERATION : Datatype_NMTOKEN + internal sealed class Datatype_ENUMERATION : Datatype_NMTOKEN { public override XmlTokenizedType TokenizedType { get { return XmlTokenizedType.ENUMERATION; } } } - internal class Datatype_char : Datatype_anySimpleType + internal sealed class Datatype_char : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(char); private static readonly Type s_listValueType = typeof(char[]); @@ -3903,7 +3903,7 @@ public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespa } } - internal class Datatype_fixed : Datatype_decimal + internal sealed class Datatype_fixed : Datatype_decimal { public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespaceResolver? nsmgr) { @@ -3953,7 +3953,7 @@ public override object ParseValue(string s, XmlNameTable? nameTable, IXmlNamespa } } - internal class Datatype_uuid : Datatype_anySimpleType + internal sealed class Datatype_uuid : Datatype_anySimpleType { private static readonly Type s_atomicValueType = typeof(Guid); private static readonly Type s_listValueType = typeof(Guid[]); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParser.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParser.cs index 8d467ace228d3..7e784726a97b0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParser.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParser.cs @@ -13,7 +13,7 @@ namespace System.Xml { - internal partial class DtdParser : IDtdParser + internal sealed partial class DtdParser : IDtdParser { // // Private types @@ -115,7 +115,7 @@ private enum LiteralType SystemOrPublicID } - private class UndeclaredNotation + private sealed class UndeclaredNotation { internal string name; internal int lineNo; @@ -1015,7 +1015,7 @@ private void ParseElementDecl() OnUnexpectedError(); } - private class ParseElementOnlyContent_LocalFrame + private sealed class ParseElementOnlyContent_LocalFrame { public ParseElementOnlyContent_LocalFrame(int startParentEntityIdParam) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParserAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParserAsync.cs index 01d09c0ea70b2..65c5f9bc615d3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParserAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdParserAsync.cs @@ -14,7 +14,7 @@ namespace System.Xml { - internal partial class DtdParser : IDtdParser + internal sealed partial class DtdParser : IDtdParser { // // IDtdParser interface diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdValidator.cs index 720a7ca83eeb1..57ba0c8906f6f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/DtdValidator.cs @@ -18,7 +18,7 @@ namespace System.Xml.Schema internal sealed class DtdValidator : BaseValidator { //required by ParseValue - private class NamespaceManager : XmlNamespaceManager + private sealed class NamespaceManager : XmlNamespaceManager { public override string LookupNamespace(string prefix) { return prefix; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/FacetChecker.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/FacetChecker.cs index 49a8beb01819a..02b452c945d73 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/FacetChecker.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/FacetChecker.cs @@ -993,7 +993,7 @@ internal static decimal Power(int x, int y) } - internal class Numeric10FacetsChecker : FacetsChecker + internal sealed class Numeric10FacetsChecker : FacetsChecker { private readonly decimal _maxValue; private readonly decimal _minValue; @@ -1127,7 +1127,7 @@ internal bool MatchEnumeration(decimal value, ArrayList enumeration, XmlValueCon } - internal class Numeric2FacetsChecker : FacetsChecker + internal sealed class Numeric2FacetsChecker : FacetsChecker { internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { @@ -1203,7 +1203,7 @@ private bool MatchEnumeration(double value, ArrayList enumeration, XmlValueConve } } - internal class DurationFacetsChecker : FacetsChecker + internal sealed class DurationFacetsChecker : FacetsChecker { internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { @@ -1274,7 +1274,7 @@ private bool MatchEnumeration(TimeSpan value, ArrayList enumeration) } } - internal class DateTimeFacetsChecker : FacetsChecker + internal sealed class DateTimeFacetsChecker : FacetsChecker { internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { @@ -1348,7 +1348,7 @@ private bool MatchEnumeration(DateTime value, ArrayList enumeration, XmlSchemaDa } } - internal class StringFacetsChecker : FacetsChecker + internal sealed class StringFacetsChecker : FacetsChecker { //All types derived from string & anyURI private static Regex? s_languagePattern; @@ -1505,7 +1505,7 @@ private bool MatchEnumeration(string value, ArrayList enumeration, XmlSchemaData } } - internal class QNameFacetsChecker : FacetsChecker + internal sealed class QNameFacetsChecker : FacetsChecker { internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { @@ -1573,11 +1573,11 @@ private bool MatchEnumeration(XmlQualifiedName value, ArrayList enumeration) } } - internal class MiscFacetsChecker : FacetsChecker + internal sealed class MiscFacetsChecker : FacetsChecker { //For bool, anySimpleType } - internal class BinaryFacetsChecker : FacetsChecker + internal sealed class BinaryFacetsChecker : FacetsChecker { //hexBinary & Base64Binary internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { @@ -1642,7 +1642,7 @@ private bool MatchEnumeration(byte[] value, ArrayList enumeration, XmlSchemaData } } - internal class ListFacetsChecker : FacetsChecker + internal sealed class ListFacetsChecker : FacetsChecker { internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { @@ -1703,7 +1703,7 @@ internal override bool MatchEnumeration(object value, ArrayList enumeration, Xml } } - internal class UnionFacetsChecker : FacetsChecker + internal sealed class UnionFacetsChecker : FacetsChecker { internal override Exception? CheckValueFacets(object value, XmlSchemaDatatype datatype) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/NamespaceList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/NamespaceList.cs index 2114b9e40248e..6ed6ff98e6a91 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/NamespaceList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/NamespaceList.cs @@ -385,7 +385,7 @@ private void RemoveNamespace(string tns) } }; - internal class NamespaceListV1Compat : NamespaceList + internal sealed class NamespaceListV1Compat : NamespaceList { public NamespaceListV1Compat(string namespaces, string targetNamespace) : base(namespaces, targetNamespace) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Preprocessor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Preprocessor.cs index 550495e6aadea..9056db9b5323e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/Preprocessor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/Preprocessor.cs @@ -19,7 +19,7 @@ internal enum Compositor Redefine }; - internal class RedefineEntry + internal sealed class RedefineEntry { internal XmlSchemaRedefine redefine; internal XmlSchema schemaToUpdate; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaInfo.cs index afd5c7840d4ed..55f07c28b5804 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaInfo.cs @@ -22,7 +22,7 @@ internal enum AttributeMatchState ValidateAttributeInvalidCall, } - internal class SchemaInfo : IDtdInfo + internal sealed class SchemaInfo : IDtdInfo { private readonly Dictionary _elementDecls = new Dictionary(); private readonly Dictionary _undeclaredElementDecls = new Dictionary(); @@ -182,7 +182,7 @@ internal bool Contains(string ns) SchemaAttDef? attdef = null; if (ed != null) { - attdef = ed.GetAttDef(qname); ; + attdef = ed.GetAttDef(qname); if (attdef == null) { if (!ed.ContentValidator!.IsOpen || qname.Namespace.Length == 0) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaNamespacemanager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaNamespacemanager.cs index 9c3b9055f3027..709182055367d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaNamespacemanager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/SchemaNamespacemanager.cs @@ -8,7 +8,7 @@ namespace System.Xml.Schema using System.Collections; using System.Collections.Generic; - internal class SchemaNamespaceManager : XmlNamespaceManager + internal sealed class SchemaNamespaceManager : XmlNamespaceManager { private readonly XmlSchemaObject _node; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlAtomicValue.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlAtomicValue.cs index 0ba7f5af19a21..939862db6b2ed 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlAtomicValue.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlAtomicValue.cs @@ -37,7 +37,7 @@ private struct Union public DateTime dtVal; } - private class NamespacePrefixForQName : IXmlNamespaceResolver + private sealed class NamespacePrefixForQName : IXmlNamespaceResolver { public string prefix; public string ns; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchema.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchema.cs index 309fb8ffa402e..68e372c8901cf 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchema.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchema.cs @@ -10,6 +10,7 @@ namespace System.Xml.Schema using System.Threading; using System.Diagnostics; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; [XmlRoot("schema", Namespace = XmlSchema.Namespace)] public class XmlSchema : XmlSchemaObject @@ -88,11 +89,13 @@ public XmlSchema() { } return parser.XmlSchema; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Write(Stream stream) { Write(stream, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Write(Stream stream, XmlNamespaceManager? namespaceManager) { XmlTextWriter xmlWriter = new XmlTextWriter(stream, null); @@ -100,11 +103,13 @@ public void Write(Stream stream, XmlNamespaceManager? namespaceManager) Write(xmlWriter, namespaceManager); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Write(TextWriter writer) { Write(writer, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Write(TextWriter writer, XmlNamespaceManager? namespaceManager) { XmlTextWriter xmlWriter = new XmlTextWriter(writer); @@ -112,11 +117,17 @@ public void Write(TextWriter writer, XmlNamespaceManager? namespaceManager) Write(xmlWriter, namespaceManager); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Write(XmlWriter writer) { Write(writer, null); } + [DynamicDependency(TrimmerConstants.PublicMembers, typeof(XmlSchema))] + // This method may be safe given the above Dynamic Dependency but it is not yet fully understood if just preserving + // all of XmlSchema public members is enough in order to be safe in all cases, so we have opted to keep the RequiresUnreferencedCode + // attribute for now. This can be removed in the future if it is determined that the above is enough for all scenarios to be trim-safe. + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Write(XmlWriter writer, XmlNamespaceManager? namespaceManager) { XmlSerializer serializer = new XmlSerializer(typeof(XmlSchema)); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaObjectTable.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaObjectTable.cs index 21abf9461d549..efb67b70d84cb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaObjectTable.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaObjectTable.cs @@ -154,7 +154,7 @@ public XmlSchemaObjectEntry(XmlQualifiedName name, XmlSchemaObject value) } } - internal class NamesCollection : ICollection + internal sealed class NamesCollection : ICollection { private readonly List _entries; private readonly int _size; @@ -209,7 +209,7 @@ public IEnumerator GetEnumerator() } //ICollection for Values - internal class ValuesCollection : ICollection + internal sealed class ValuesCollection : ICollection { private readonly List _entries; private readonly int _size; @@ -334,7 +334,7 @@ public void Reset() } } - internal class XSODictionaryEnumerator : XSOEnumerator, IDictionaryEnumerator + internal sealed class XSODictionaryEnumerator : XSOEnumerator, IDictionaryEnumerator { internal XSODictionaryEnumerator(List entries, int size, EnumeratorType enumType) : base(entries, size, enumType) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaParticle.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaParticle.cs index 926b9ee6c92b2..ffb187db50c2d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaParticle.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaParticle.cs @@ -160,7 +160,7 @@ internal XmlQualifiedName GetQualifiedName() return XmlQualifiedName.Empty; //If ever called on other particles } - private class EmptyParticle : XmlSchemaParticle + private sealed class EmptyParticle : XmlSchemaParticle { internal override bool IsEmpty { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaSubstitutionGroup.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaSubstitutionGroup.cs index 645d77e41cc2a..fbd403feeb63f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaSubstitutionGroup.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaSubstitutionGroup.cs @@ -25,7 +25,7 @@ internal XmlQualifiedName Examplar } } - internal class XmlSchemaSubstitutionGroupV1Compat : XmlSchemaSubstitutionGroup + internal sealed class XmlSchemaSubstitutionGroupV1Compat : XmlSchemaSubstitutionGroup { private readonly XmlSchemaChoice _choice = new XmlSchemaChoice(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs index b4b0488ea36f7..dc2ee89c99bb5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlSchemaValidator.cs @@ -44,7 +44,7 @@ internal enum ValidatorState SkipToEndElement, Finish, } - internal class IdRefNode + internal sealed class IdRefNode { internal string Id; internal int LineNo; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlUntypedStringConverter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlUntypedStringConverter.cs index e8855cff7431f..50f0d2d53390c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlUntypedStringConverter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlUntypedStringConverter.cs @@ -13,7 +13,7 @@ namespace System.Xml.Schema { // This is an atomic value converted for Silverlight XML core that knows only how to convert to and from string. // It does not recognize XmlAtomicValue or XPathItemType. - internal class XmlUntypedStringConverter + internal sealed class XmlUntypedStringConverter { // Fields private readonly bool _listsAllowed; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlValueConverter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlValueConverter.cs index 08c6d660d189e..ff507ae1ab293 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlValueConverter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XmlValueConverter.cs @@ -862,9 +862,9 @@ protected static DateTimeOffset UntypedAtomicToDateTimeOffset(string value) } } - internal class XmlNumeric10Converter : XmlBaseConverter + internal sealed class XmlNumeric10Converter : XmlBaseConverter { - protected XmlNumeric10Converter(XmlSchemaType schemaType) : base(schemaType) + private XmlNumeric10Converter(XmlSchemaType schemaType) : base(schemaType) { } @@ -1128,9 +1128,9 @@ private object ChangeTypeWildcardSource(object value, Type destinationType, IXml #endregion } - internal class XmlNumeric2Converter : XmlBaseConverter + internal sealed class XmlNumeric2Converter : XmlBaseConverter { - protected XmlNumeric2Converter(XmlSchemaType schemaType) : base(schemaType) + private XmlNumeric2Converter(XmlSchemaType schemaType) : base(schemaType) { } @@ -1299,9 +1299,9 @@ public override object ChangeType(object value, Type destinationType, IXmlNamesp #endregion } - internal class XmlDateTimeConverter : XmlBaseConverter + internal sealed class XmlDateTimeConverter : XmlBaseConverter { - protected XmlDateTimeConverter(XmlSchemaType schemaType) : base(schemaType) + private XmlDateTimeConverter(XmlSchemaType schemaType) : base(schemaType) { } @@ -1531,9 +1531,9 @@ public override object ChangeType(object value, Type destinationType, IXmlNamesp #endregion } - internal class XmlBooleanConverter : XmlBaseConverter + internal sealed class XmlBooleanConverter : XmlBaseConverter { - protected XmlBooleanConverter(XmlSchemaType schemaType) : base(schemaType) + private XmlBooleanConverter(XmlSchemaType schemaType) : base(schemaType) { } @@ -1686,9 +1686,9 @@ public override object ChangeType(object value, Type destinationType, IXmlNamesp #endregion } - internal class XmlMiscConverter : XmlBaseConverter + internal sealed class XmlMiscConverter : XmlBaseConverter { - protected XmlMiscConverter(XmlSchemaType schemaType) : base(schemaType) + private XmlMiscConverter(XmlSchemaType schemaType) : base(schemaType) { } @@ -1915,9 +1915,9 @@ private object ChangeTypeWildcardSource(object value, Type destinationType, IXml #endregion } - internal class XmlStringConverter : XmlBaseConverter + internal sealed class XmlStringConverter : XmlBaseConverter { - protected XmlStringConverter(XmlSchemaType schemaType) : base(schemaType) + private XmlStringConverter(XmlSchemaType schemaType) : base(schemaType) { } @@ -1982,15 +1982,15 @@ public override object ChangeType(object value, Type destinationType, IXmlNamesp #endregion } - internal class XmlUntypedConverter : XmlListConverter + internal sealed class XmlUntypedConverter : XmlListConverter { private readonly bool _allowListToList; - protected XmlUntypedConverter() : base(DatatypeImplementation.UntypedAtomicType) + private XmlUntypedConverter() : base(DatatypeImplementation.UntypedAtomicType) { } - protected XmlUntypedConverter(XmlUntypedConverter atomicConverter, bool allowListToList) + private XmlUntypedConverter(XmlUntypedConverter atomicConverter, bool allowListToList) : base(atomicConverter, allowListToList ? StringArrayType : StringType) { _allowListToList = allowListToList; @@ -2508,9 +2508,9 @@ private bool SupportsType(Type clrType) } } - internal class XmlAnyConverter : XmlBaseConverter + internal sealed class XmlAnyConverter : XmlBaseConverter { - protected XmlAnyConverter(XmlTypeCode typeCode) : base(typeCode) + private XmlAnyConverter(XmlTypeCode typeCode) : base(typeCode) { } @@ -2839,9 +2839,9 @@ private XPathNavigator ToNavigator(XPathNavigator nav) } } - internal class XmlAnyListConverter : XmlListConverter + internal sealed class XmlAnyListConverter : XmlListConverter { - protected XmlAnyListConverter(XmlBaseConverter atomicConverter) : base(atomicConverter) + private XmlAnyListConverter(XmlBaseConverter atomicConverter) : base(atomicConverter) { } @@ -3113,12 +3113,12 @@ private string ListAsString(IEnumerable list, IXmlNamespaceResolver? nsResolver) } } - internal class XmlUnionConverter : XmlBaseConverter + internal sealed class XmlUnionConverter : XmlBaseConverter { private readonly XmlValueConverter[] _converters; private readonly bool _hasAtomicMember, _hasListMember; - protected XmlUnionConverter(XmlSchemaType schemaType) : base(schemaType) + private XmlUnionConverter(XmlSchemaType schemaType) : base(schemaType) { // Skip restrictions. It is safe to do that because this is a union, so it's not a built-in type while (schemaType.DerivedBy == XmlSchemaDerivationMethod.Restriction) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdBuilder.cs index 4f9ac921ea1c6..16e39c96e2674 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Schema/XsdBuilder.cs @@ -115,7 +115,7 @@ public XsdEntry(SchemaNames.Token n, }; //required for Parsing QName - private class BuilderNamespaceManager : XmlNamespaceManager + private sealed class BuilderNamespaceManager : XmlNamespaceManager { private readonly XmlNamespaceManager _nsMgr; private readonly XmlReader _reader; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs index 41f3f51feb212..c5be96f600577 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeGenerator.cs @@ -21,7 +21,7 @@ namespace System.Xml.Serialization { - internal class CodeGenerator + internal sealed class CodeGenerator { internal const BindingFlags InstancePublicBindingFlags = BindingFlags.Instance | BindingFlags.Public; internal const BindingFlags InstanceBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; @@ -475,6 +475,7 @@ private static bool IsStruct(Type objType) return objType.IsValueType && !objType.IsPrimitive; } + [RequiresUnreferencedCode("calls LoadMember")] internal Type LoadMember(object obj, MemberInfo memberInfo) { if (GetVariableType(obj).IsValueType) @@ -484,6 +485,7 @@ internal Type LoadMember(object obj, MemberInfo memberInfo) return LoadMember(memberInfo); } + [RequiresUnreferencedCode("GetProperty on PropertyInfo type's base type")] private static MethodInfo? GetPropertyMethodFromBaseType(PropertyInfo propertyInfo, bool isGetter) { // we only invoke this when the propertyInfo does not have a GET or SET method on it @@ -522,6 +524,7 @@ internal Type LoadMember(object obj, MemberInfo memberInfo) return result; } + [RequiresUnreferencedCode("calls GetPropertyMethodFromBaseType")] internal Type LoadMember(MemberInfo memberInfo) { Type? memberType = null; @@ -560,6 +563,7 @@ internal Type LoadMember(MemberInfo memberInfo) return memberType; } + [RequiresUnreferencedCode("calls GetPropertyMethodFromBaseType")] internal Type LoadMemberAddress(MemberInfo memberInfo) { Type? memberType = null; @@ -602,6 +606,7 @@ internal Type LoadMemberAddress(MemberInfo memberInfo) return memberType; } + [RequiresUnreferencedCode("calls GetPropertyMethodFromBaseType")] internal void StoreMember(MemberInfo memberInfo) { if (memberInfo is FieldInfo) @@ -1337,7 +1342,7 @@ internal void GotoMethodEnd() Br(_methodEndLabel); } - internal class WhileState + internal sealed class WhileState { public Label StartLabel; public Label CondLabel; @@ -1405,7 +1410,7 @@ internal void WhileEndCondition() } - internal class ArgBuilder + internal sealed class ArgBuilder { internal string Name; internal int Index; @@ -1418,7 +1423,7 @@ internal ArgBuilder(string name, int index, Type argType) } } - internal class ForState + internal sealed class ForState { private readonly LocalBuilder _indexVar; private readonly Label _beginLabel; @@ -1476,7 +1481,7 @@ internal enum Cmp : int GreaterThanOrEqualTo } - internal class IfState + internal sealed class IfState { private Label _elseBegin; private Label _endIf; @@ -1506,7 +1511,7 @@ internal Label ElseBegin } } - internal class LocalScope + internal sealed class LocalScope { public readonly LocalScope? parent; private readonly Dictionary _locals; @@ -1581,7 +1586,7 @@ public void AddToFreeLocals(Dictionary<(Type, string), Queue> free } } - internal class MethodBuilderInfo + internal sealed class MethodBuilderInfo { public readonly MethodBuilder MethodBuilder; public readonly Type[] ParameterTypes; @@ -1605,7 +1610,7 @@ public void Validate(Type? returnType, Type[] parameterTypes, MethodAttributes a } } - internal class CodeGeneratorConversionException : Exception + internal sealed class CodeGeneratorConversionException : Exception { private readonly Type _sourceType; private readonly Type _targetType; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs index ebbac5ed2e4e3..54f1e2c0aea32 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifiers.cs @@ -8,7 +8,7 @@ namespace System.Xml.Serialization using System.IO; using System.Globalization; - internal class CaseInsensitiveKeyComparer : CaseInsensitiveComparer, IEqualityComparer + internal sealed class CaseInsensitiveKeyComparer : CaseInsensitiveComparer, IEqualityComparer { public CaseInsensitiveKeyComparer() : base(CultureInfo.CurrentCulture) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs index f6be0bfb5afbc..a3b6334d08b5f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs @@ -14,7 +14,7 @@ namespace System.Xml.Serialization { - internal class TempAssembly + internal sealed class TempAssembly { internal const string GeneratedAssemblyNamespace = "Microsoft.Xml.Serialization.GeneratedAssembly"; private readonly Assembly? _assembly; @@ -23,7 +23,7 @@ internal class TempAssembly private IDictionary? _readerMethods; private TempMethodDictionary? _methods; - internal class TempMethod + internal sealed class TempMethod { internal MethodInfo? writeMethod; internal MethodInfo? readMethod; @@ -44,6 +44,7 @@ internal TempAssembly(XmlMapping[] xmlMappings, Assembly assembly, XmlSerializer _contract = contract; } + [RequiresUnreferencedCode("calls GenerateRefEmitAssembly")] internal TempAssembly(XmlMapping[] xmlMappings, Type?[] types, string? defaultNamespace, string? location) { bool containsSoapMapping = false; @@ -101,6 +102,7 @@ internal static bool UseLegacySerializerGeneration internal XmlSerializerImplementation Contract { + [RequiresUnreferencedCode("calls GetTypeFromAssembly")] get { if (_contract == null) @@ -137,6 +139,7 @@ internal void InitAssemblyMethods(XmlMapping[] xmlMappings) /// // SxS: This method does not take any resource name and does not expose any resources to the caller. // It's OK to suppress the SxS warning. + [RequiresUnreferencedCode("calls LoadFile")] internal static Assembly? LoadGeneratedAssembly(Type type, string? defaultNamespace, out XmlSerializerImplementation? contract) { Assembly? serializer = null; @@ -276,6 +279,7 @@ private static string GenerateAssemblyId(Type type) return sb.ToString(); } + [RequiresUnreferencedCode("calls GenerateBegin")] internal static bool GenerateSerializerToStream(XmlMapping[] xmlMappings, Type?[] types, string? defaultNamespace, Assembly? assembly, Hashtable assemblies, Stream stream) { var compiler = new Compiler(); @@ -420,6 +424,7 @@ internal static bool GenerateSerializerToStream(XmlMapping[] xmlMappings, Type?[ } } + [RequiresUnreferencedCode("calls GenerateElement")] internal static Assembly GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type?[]? types, string? defaultNamespace) { var scopeTable = new Dictionary(); @@ -497,7 +502,8 @@ internal static Assembly GenerateRefEmitAssembly(XmlMapping[] xmlMappings, Type? return writerType.Assembly; } - private static MethodInfo GetMethodFromType(Type type, string methodName) + private static MethodInfo GetMethodFromType( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string methodName) { MethodInfo? method = type.GetMethod(methodName); if (method != null) @@ -508,6 +514,7 @@ private static MethodInfo GetMethodFromType(Type type, string methodName) throw missingMethod; } + [RequiresUnreferencedCode("calls GetType")] internal static Type GetTypeFromAssembly(Assembly assembly, string typeName) { typeName = GeneratedAssemblyNamespace + "." + typeName; @@ -557,6 +564,7 @@ internal bool CanRead(XmlMapping mapping, XmlReader xmlReader) return encodingStyle; } + [RequiresUnreferencedCode("calls Contract")] internal object? InvokeReader(XmlMapping mapping, XmlReader xmlReader, XmlDeserializationEvents events, string? encodingStyle) { XmlSerializationReader? reader = null; @@ -591,6 +599,7 @@ internal bool CanRead(XmlMapping mapping, XmlReader xmlReader) } } + [RequiresUnreferencedCode("calls Contract")] internal void InvokeWriter(XmlMapping mapping, XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? namespaces, string? encodingStyle, string? id) { XmlSerializationWriter? writer = null; @@ -630,7 +639,7 @@ internal sealed class TempMethodDictionary : Dictionary } } - internal class TempAssemblyCacheKey + internal sealed class TempAssemblyCacheKey { private readonly string? _ns; private readonly object _type; @@ -655,7 +664,7 @@ public override int GetHashCode() } } - internal class TempAssemblyCache + internal sealed class TempAssemblyCache { private Dictionary _cache = new Dictionary(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs index 890628ad68afc..20522dca69f2e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compiler.cs @@ -7,15 +7,17 @@ using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Serialization { - internal class Compiler + internal sealed class Compiler { private readonly StringWriter _writer = new StringWriter(CultureInfo.InvariantCulture); // SxS: This method does not take any resource name and does not expose any resources to the caller. // It's OK to suppress the SxS warning. + [RequiresUnreferencedCode("Reflects against input Type DeclaringType")] internal void AddImport(Type? type, Hashtable types) { if (type == null) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ImportContext.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ImportContext.cs index cba6418f6827f..61acd28393cbd 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ImportContext.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ImportContext.cs @@ -11,6 +11,7 @@ namespace System.Xml.Serialization using System.Collections; using System.Collections.Specialized; using System.Reflection; + using System.Diagnostics.CodeAnalysis; public class ImportContext { @@ -79,7 +80,7 @@ public StringCollection Warnings } } - internal class SchemaObjectCache + internal sealed class SchemaObjectCache { private Hashtable? _graph; private Hashtable? _hash; @@ -214,6 +215,7 @@ private int CompositeHash(XmlSchemaObject o, int hash) return (int)tmp; } + [RequiresUnreferencedCode("creates SchemaGraph")] internal void GenerateSchemaGraph(XmlSchemas schemas) { SchemaGraph graph = new SchemaGraph(Graph, schemas); @@ -252,13 +254,14 @@ private string ToString(XmlSchemaObject o, SchemaObjectWriter writer) } } - internal class SchemaGraph + internal sealed class SchemaGraph { private readonly ArrayList _empty = new ArrayList(); private readonly XmlSchemas _schemas; private readonly Hashtable _scope; private readonly int _items; + [RequiresUnreferencedCode("Calls Compile")] internal SchemaGraph(Hashtable scope, XmlSchemas schemas) { _scope = scope; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs index 1a2c90c9457e7..b6c770d672e3e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Mappings.cs @@ -139,7 +139,7 @@ internal string ToString(string? defaultNs) } } - internal class ElementAccessor : Accessor + internal sealed class ElementAccessor : Accessor { private bool _nullable; private bool _isSoap; @@ -180,7 +180,7 @@ internal ElementAccessor Clone() } } - internal class ChoiceIdentifierAccessor : Accessor + internal sealed class ChoiceIdentifierAccessor : Accessor { private string? _memberName; private string[]? _memberIds; @@ -205,15 +205,15 @@ internal MemberInfo? MemberInfo } } - internal class TextAccessor : Accessor + internal sealed class TextAccessor : Accessor { } - internal class XmlnsAccessor : Accessor + internal sealed class XmlnsAccessor : Accessor { } - internal class AttributeAccessor : Accessor + internal sealed class AttributeAccessor : Accessor { private bool _isSpecial; private bool _isList; @@ -359,7 +359,7 @@ internal override bool IsList } } - internal class NullableMapping : TypeMapping + internal sealed class NullableMapping : TypeMapping { private TypeMapping? _baseMapping; @@ -375,7 +375,7 @@ internal override string DefaultElementName } } - internal class ArrayMapping : TypeMapping + internal sealed class ArrayMapping : TypeMapping { private ElementAccessor[]? _elements; private ElementAccessor[]? _sortedElements; @@ -417,7 +417,7 @@ internal StructMapping? TopLevelMapping } } - internal class EnumMapping : PrimitiveMapping + internal sealed class EnumMapping : PrimitiveMapping { private ConstantMapping[]? _constants; private bool _isFlags; @@ -435,7 +435,7 @@ internal ConstantMapping[]? Constants } } - internal class ConstantMapping : Mapping + internal sealed class ConstantMapping : Mapping { private string? _xmlName; private string? _name; @@ -462,7 +462,7 @@ internal long Value } } - internal class StructMapping : TypeMapping, INameScope + internal sealed class StructMapping : TypeMapping, INameScope { private MemberMapping[]? _members; private StructMapping? _baseMapping; @@ -765,7 +765,7 @@ internal static void SortMostToLeastDerived(ElementAccessor[] elements) Array.Sort(elements, new AccessorComparer()); } - internal class AccessorComparer : IComparer + internal sealed class AccessorComparer : IComparer { public int Compare(object? o1, object? o2) { @@ -880,7 +880,7 @@ internal bool Match(AccessorMapping mapping) } } - internal class MemberMappingComparer : IComparer + internal sealed class MemberMappingComparer : IComparer { public int Compare(object? o1, object? o2) { @@ -911,7 +911,7 @@ public int Compare(object? o1, object? o2) } } - internal class MemberMapping : AccessorMapping + internal sealed class MemberMapping : AccessorMapping { private string? _name; private bool _checkShouldPersist; @@ -1004,7 +1004,7 @@ internal MemberMapping Clone() } } - internal class MembersMapping : TypeMapping + internal sealed class MembersMapping : TypeMapping { private MemberMapping[]? _members; private bool _hasWrapperElement = true; @@ -1054,9 +1054,10 @@ internal bool NamedAny } } - internal class SerializableMapping : SpecialMapping + internal sealed class SerializableMapping : SpecialMapping { private XmlSchema? _schema; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] private Type? _type; private bool _needSchema = true; @@ -1172,6 +1173,7 @@ internal SerializableMapping? Next set { _next = value; } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] internal Type? Type { get { return _type; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Models.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Models.cs index 2e84e276bfc0b..f81cf455a7b5e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Models.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Models.cs @@ -8,13 +8,14 @@ namespace System.Xml.Serialization using System.Collections; using System.Diagnostics; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; // These classes define the abstract serialization model, e.g. the rules for WHAT is serialized. // The answer of HOW the values are serialized is answered by a particular reflection importer // by looking for a particular set of custom attributes specific to the serialization format // and building an appropriate set of accessors/mappings. - internal class ModelScope + internal sealed class ModelScope { private readonly TypeScope _typeScope; private readonly Dictionary _models = new Dictionary(); @@ -30,11 +31,13 @@ internal TypeScope TypeScope get { return _typeScope; } } + [RequiresUnreferencedCode("calls GetTypeModel")] internal TypeModel GetTypeModel(Type type) { return GetTypeModel(type, true); } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal TypeModel GetTypeModel(Type type, bool directReference) { TypeModel? model; @@ -70,6 +73,7 @@ internal TypeModel GetTypeModel(Type type, bool directReference) return model; } + [RequiresUnreferencedCode("calls GetArrayTypeDesc")] internal ArrayModel GetArrayModel(Type type) { TypeModel? model; @@ -90,16 +94,21 @@ internal ArrayModel GetArrayModel(Type type) internal abstract class TypeModel { private readonly TypeDesc _typeDesc; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private readonly Type _type; private readonly ModelScope _scope; - protected TypeModel(Type type, TypeDesc typeDesc, ModelScope scope) + protected TypeModel( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + TypeDesc typeDesc, + ModelScope scope) { _scope = scope; _type = type; _typeDesc = typeDesc; } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] internal Type Type { get { return _type; } @@ -116,29 +125,36 @@ internal TypeDesc TypeDesc } } - internal class ArrayModel : TypeModel + internal sealed class ArrayModel : TypeModel { - internal ArrayModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } + internal ArrayModel( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } internal TypeModel Element { + [RequiresUnreferencedCode("Calls GetTypeModel")] get { return ModelScope.GetTypeModel(TypeScope.GetArrayElementType(Type, null)!); } } } - internal class PrimitiveModel : TypeModel + internal sealed class PrimitiveModel : TypeModel { - internal PrimitiveModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } + internal PrimitiveModel( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } } - internal class SpecialModel : TypeModel + internal sealed class SpecialModel : TypeModel { - internal SpecialModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } + internal SpecialModel( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } } - internal class StructModel : TypeModel + internal sealed class StructModel : TypeModel { - internal StructModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } + internal StructModel( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } internal MemberInfo[] GetMemberInfos() { @@ -168,6 +184,7 @@ internal MemberInfo[] GetMemberInfos() return fieldsAndProps; } + [RequiresUnreferencedCode("calls GetFieldModel")] internal FieldModel? GetFieldModel(MemberInfo memberInfo) { FieldModel? model = null; @@ -199,6 +216,7 @@ private void CheckSupportedMember(TypeDesc? typeDesc, MemberInfo member, Type ty CheckSupportedMember(typeDesc.ArrayElementTypeDesc, member, type); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private FieldModel? GetFieldModel(FieldInfo fieldInfo) { if (fieldInfo.IsStatic) return null; @@ -212,6 +230,7 @@ private void CheckSupportedMember(TypeDesc? typeDesc, MemberInfo member, Type ty return new FieldModel(fieldInfo, fieldInfo.FieldType, typeDesc); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private FieldModel? GetPropertyModel(PropertyInfo propertyInfo) { if (propertyInfo.DeclaringType != Type) return null; @@ -247,7 +266,7 @@ internal enum SpecifiedAccessor ReadWrite, } - internal class FieldModel + internal sealed class FieldModel { private readonly SpecifiedAccessor _checkSpecified = SpecifiedAccessor.None; private readonly MemberInfo? _memberInfo; @@ -274,6 +293,7 @@ internal FieldModel(string name, Type fieldType, TypeDesc fieldTypeDesc, bool ch _readOnly = readOnly; } + [RequiresUnreferencedCode("Calls GetField on MemberInfo type")] internal FieldModel(MemberInfo memberInfo, Type fieldType, TypeDesc fieldTypeDesc) { _name = memberInfo.Name; @@ -369,7 +389,7 @@ internal bool IsProperty } } - internal class ConstantModel + internal sealed class ConstantModel { private readonly FieldInfo _fieldInfo; private readonly long _value; @@ -396,11 +416,13 @@ internal FieldInfo FieldInfo } } - internal class EnumModel : TypeModel + internal sealed class EnumModel : TypeModel { private ConstantModel[]? _constants; - internal EnumModel(Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } + internal EnumModel( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type, TypeDesc typeDesc, ModelScope scope) : base(type, typeDesc, scope) { } internal ConstantModel[] Constants { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs index 3ddda81e40268..c3ecf346d0926 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/NameTable.cs @@ -10,7 +10,7 @@ namespace System.Xml.Serialization using System.Collections; using System.Collections.Generic; - internal class NameKey + internal sealed class NameKey { private readonly string? _ns; private readonly string? _name; @@ -37,7 +37,7 @@ internal interface INameScope { object? this[string? name, string? ns] { get; set; } } - internal class NameTable : INameScope + internal sealed class NameTable : INameScope { private readonly Dictionary _table = new Dictionary(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs index 81fa7b25f0d10..e06311de6d61c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/PrimitiveXmlSerializers.cs @@ -2,10 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Serialization { - internal class XmlSerializationPrimitiveWriter : System.Xml.Serialization.XmlSerializationWriter + internal sealed class XmlSerializationPrimitiveWriter : System.Xml.Serialization.XmlSerializationWriter { internal void Write_string(object? o) { @@ -224,12 +225,13 @@ internal void Write_QName(object? o) WriteNullableQualifiedNameLiteral(@"QName", @"", ((global::System.Xml.XmlQualifiedName)o)); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected override void InitCallbacks() { } } - internal class XmlSerializationPrimitiveReader : System.Xml.Serialization.XmlSerializationReader + internal sealed class XmlSerializationPrimitiveReader : System.Xml.Serialization.XmlSerializationReader { internal object? Read_string() { @@ -708,6 +710,7 @@ internal class XmlSerializationPrimitiveReader : System.Xml.Serialization.XmlSer return (object?)o; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected override void InitCallbacks() { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs index dfc4e0a3fc6f1..ab2daeeab7dde 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationReader.cs @@ -8,26 +8,37 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using System.Xml.Extensions; using System.Xml.Schema; +// UnconditionalSuppressMessage that specify a Target need to be at the assembly or module level for now. Also, +// they won't consider Target unless you also specify Scope to be either "member" or "type" +[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:MakeGenericMethod", + Target = "M:System.Xml.Serialization.ReflectionXmlSerializationReader.#cctor", + Justification = "The reason why this warns is because the two static properties call GetTypeDesc() which internally will call " + + "ImportTypeDesc() when the passed in type is not considered a primitive type. That said, for both properties here we are passing in string " + + "and XmlQualifiedName which are considered primitive, so they are trim safe.", + Scope = "member")] + namespace System.Xml.Serialization { internal delegate void UnknownNodeAction(object? o); - internal class ReflectionXmlSerializationReader : XmlSerializationReader + internal sealed class ReflectionXmlSerializationReader : XmlSerializationReader { - private static TypeDesc StringTypeDesc { get; set; } = (new TypeScope()).GetTypeDesc(typeof(string)); - private static TypeDesc QnameTypeDesc { get; set; } = (new TypeScope()).GetTypeDesc(typeof(XmlQualifiedName)); - private readonly XmlMapping _mapping; + internal static TypeDesc StringTypeDesc { get; set; } = (new TypeScope()).GetTypeDesc(typeof(string)); + internal static TypeDesc QnameTypeDesc { get; set; } = (new TypeScope()).GetTypeDesc(typeof(XmlQualifiedName)); + public ReflectionXmlSerializationReader(XmlMapping mapping, XmlReader xmlReader, XmlDeserializationEvents events, string? encodingStyle) { Init(xmlReader, events, encodingStyle, tempAssembly: null); _mapping = mapping; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected override void InitCallbacks() { TypeScope scope = _mapping.Scope!; @@ -50,6 +61,7 @@ protected override void InitIDs() { } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public object? ReadObject() { XmlMapping xmlMapping = _mapping; @@ -73,6 +85,7 @@ protected override void InitIDs() } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object GenerateMembersElement(XmlMembersMapping xmlMembersMapping) { if (xmlMembersMapping.Accessor.IsSoap) @@ -85,6 +98,7 @@ private object GenerateMembersElement(XmlMembersMapping xmlMembersMapping) } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object GenerateLiteralMembersElement(XmlMembersMapping xmlMembersMapping) { ElementAccessor element = xmlMembersMapping.Accessor; @@ -128,6 +142,7 @@ private object GenerateLiteralMembersElement(XmlMembersMapping xmlMembersMapping return p; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private bool GenerateLiteralMembersElementInternal(MemberMapping[] mappings, bool hasWrapperElement, object?[] p) { Member? anyText = null; @@ -312,6 +327,7 @@ private void InitializeValueTypes(object?[] p, MemberMapping[] mappings) } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object GenerateEncodedMembersElement(XmlMembersMapping xmlMembersMapping) { ElementAccessor element = xmlMembersMapping.Accessor; @@ -399,17 +415,19 @@ private object GenerateEncodedMembersElement(XmlMembersMapping xmlMembersMapping } else { - unrecognizedElementSource = (_) => - { - if (Reader.GetAttribute("id", null) != null) - { - ReadReferencedElement(); - } - else - { - UnknownNode(p); - }; - }; + unrecognizedElementSource = Wrapper; + [RequiresUnreferencedCode("calls ReadReferencedElement")] + void Wrapper(object? _) + { + if (Reader.GetAttribute("id", null) != null) + { + ReadReferencedElement(); + } + else + { + UnknownNode(p); + } + } } WriteMemberElements(members, unrecognizedElementSource, (_) => UnknownNode(p), null, null, fixup: fixup, checkTypeHrefsSource: checkTypeHrefSource); @@ -452,6 +470,7 @@ private object GenerateEncodedMembersElement(XmlMembersMapping xmlMembersMapping return p; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? GenerateTypeElement(XmlTypeMapping xmlTypeMapping) { ElementAccessor element = xmlTypeMapping.Accessor; @@ -481,6 +500,7 @@ private object GenerateEncodedMembersElement(XmlMembersMapping xmlMembersMapping return o; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteMemberElements(Member[] expectedMembers, UnknownNodeAction elementElseAction, UnknownNodeAction elseAction, Member? anyElement, Member? anyText, Fixup? fixup = null, List? checkTypeHrefsSource = null) { bool checkType = checkTypeHrefsSource != null; @@ -510,6 +530,7 @@ private void WriteMemberElements(Member[] expectedMembers, UnknownNodeAction ele } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteMemberElementsCheckType(List checkTypeHrefsSource) { object? RefElememnt = ReadReferencingElement(null, null, true, out string? refElemId); @@ -533,6 +554,7 @@ private void ProcessUnknownNode(UnknownNodeAction action) action?.Invoke(null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteMembers(ref object? o, Member[] members, UnknownNodeAction elementElseAction, UnknownNodeAction elseAction, Member? anyElement, Member? anyText) { Reader.MoveToContent(); @@ -544,7 +566,8 @@ private void WriteMembers(ref object? o, Member[] members, UnknownNodeAction ele } } - private void SetCollectionObjectWithCollectionMember([NotNull] ref object? collection, CollectionMember collectionMember, Type collectionType) + private void SetCollectionObjectWithCollectionMember([NotNull] ref object? collection, CollectionMember collectionMember, + [DynamicallyAccessedMembers(TrimmerConstants.AllMethods)] Type collectionType) { if (collectionType.IsArray) { @@ -577,7 +600,8 @@ private void SetCollectionObjectWithCollectionMember([NotNull] ref object? colle } } - private static void AddObjectsIntoTargetCollection(object targetCollection, List sourceCollection, Type targetCollectionType) + private static void AddObjectsIntoTargetCollection(object targetCollection, List sourceCollection, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type targetCollectionType) { if (targetCollection is IList targetList) { @@ -605,6 +629,7 @@ private static void AddObjectsIntoTargetCollection(object targetCollection, List private static readonly ConcurrentDictionary<(Type, string), ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate> s_setMemberValueDelegateCache = new ConcurrentDictionary<(Type, string), ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate>(); + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private static ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate GetSetMemberValueDelegate(object o, string memberName) { Debug.Assert(o != null, "Object o should not be null"); @@ -652,6 +677,7 @@ private static ReflectionXmlSerializationReaderHelper.SetMemberValueDelegate Get throw new InvalidOperationException(SR.XmlInternalError); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private bool WriteMemberText(Member anyText) { object? value; @@ -714,6 +740,7 @@ private bool IsSequence(Member[] members) return false; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementMember, UnknownNodeAction elementElseAction, Fixup? fixup = null, CheckTypeSource? checkTypeSource = null) { bool checkType = checkTypeSource != null; @@ -822,6 +849,7 @@ private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementM } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteElement(ElementAccessor element, bool checkSpecified, bool checkForNull, bool readOnly, string? defaultNamespace, int fixupIndex = -1, int elementIndex = -1, Fixup? fixup = null, Member? member = null) { object? value = null; @@ -994,11 +1022,14 @@ private void WriteMemberElementsIf(Member[] expectedMembers, Member? anyElementM return value; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private XmlSerializationReadCallback CreateXmlSerializationReadCallback(TypeMapping mapping) { if (mapping is StructMapping structMapping) { - return () => WriteStructMethod(structMapping, mapping.TypeDesc!.IsNullable, true, defaultNamespace: null); + [RequiresUnreferencedCode("calls WriteStructMethod")] + object? WriteStruct() => WriteStructMethod(structMapping, mapping.TypeDesc!.IsNullable, true, defaultNamespace: null); + return WriteStruct; } else if (mapping is EnumMapping enumMapping) { @@ -1006,7 +1037,9 @@ private XmlSerializationReadCallback CreateXmlSerializationReadCallback(TypeMapp } else if (mapping is NullableMapping nullableMapping) { - return () => WriteNullableMethod(nullableMapping, false, null); + [RequiresUnreferencedCode("calls WriteNullableMethod")] + object? Wrapper() => WriteNullableMethod(nullableMapping, false, null); + return Wrapper; } return DummyReadArrayMethod; @@ -1050,6 +1083,7 @@ private static bool IsWildcard(SpecialMapping mapping) return mapping.TypeDesc!.CanBeElementValue; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteArray(ArrayMapping arrayMapping, bool readOnly, bool isNullable, string? defaultNamespace, int fixupIndex = -1, Fixup? fixup = null, Member? member = null) { object? o = null; @@ -1141,6 +1175,7 @@ private static bool IsWildcard(SpecialMapping mapping) return o; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object WritePrimitive(TypeMapping mapping, Func readFunc, object funcState) { if (mapping is EnumMapping enumMapping) @@ -1202,6 +1237,7 @@ private object WritePrimitive(TypeMapping mapping, Func readFunc } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteStructMethod(StructMapping mapping, bool isNullable, bool checkType, string? defaultNamespace) { if (mapping.IsSoap) @@ -1210,6 +1246,7 @@ private object WritePrimitive(TypeMapping mapping, Func readFunc return WriteLiteralStructMethod(mapping, isNullable, checkType, defaultNamespace); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteNullableMethod(NullableMapping nullableMapping, bool checkType, string? defaultNamespace) { object? o = Activator.CreateInstance(nullableMapping.TypeDesc!.Type!); @@ -1272,7 +1309,8 @@ private Hashtable WriteHashtable(EnumMapping mapping, string name) return h; } - private object? ReflectionCreateObject(Type type) + private object? ReflectionCreateObject( + [DynamicallyAccessedMembers(TrimmerConstants.AllMethods)] Type type) { object? obj; if (type.IsArray) @@ -1295,10 +1333,13 @@ private Hashtable WriteHashtable(EnumMapping mapping, string name) return obj; } - private ConstructorInfo? GetDefaultConstructor(Type type) => + private ConstructorInfo? GetDefaultConstructor( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) => type.IsValueType ? null : type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null); + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteEncodedStructMethod(StructMapping structMapping) { if (structMapping.TypeDesc!.IsRoot) @@ -1324,7 +1365,12 @@ private Hashtable WriteHashtable(EnumMapping mapping, string name) TypeDesc td = member.Mapping.TypeDesc!; if (td.IsCollection || td.IsEnumerable) { - member.Source = (value) => WriteAddCollectionFixup(o!, member, value!); + member.Source = Wrapper; + [RequiresUnreferencedCode("Calls WriteAddCollectionFixup")] + void Wrapper(object? value) + { + WriteAddCollectionFixup(o!, member, value!); + } } else if (!member.Mapping.ReadOnly) { @@ -1413,6 +1459,7 @@ private XmlSerializationFixupCallback CreateWriteFixupMethod(Member[] members) }; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteAddCollectionFixup(object o, Member member, object memberValue) { TypeDesc typeDesc = member.Mapping.TypeDesc!; @@ -1423,6 +1470,7 @@ private void WriteAddCollectionFixup(object o, Member member, object memberValue WriteAddCollectionFixup(getSource, setSource, memberValue, typeDesc, readOnly); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteAddCollectionFixup(Func getSource, Action setSource, object memberValue, TypeDesc typeDesc, bool readOnly) { object? memberSource = getSource(); @@ -1446,9 +1494,12 @@ private void WriteAddCollectionFixup(object o, Member member, object memberValue return memberSource; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCallback(Type collectionType) { - return (collection, collectionItems) => + return Wrapper; + [RequiresUnreferencedCode("Calls AddObjectsIntoTargetCollection")] + void Wrapper(object? collection, object? collectionItems) { if (collectionItems == null) return; @@ -1470,9 +1521,10 @@ private XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCall } AddObjectsIntoTargetCollection(collection, listOfItems, collectionType); - }; + } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private object? WriteLiteralStructMethod(StructMapping structMapping, bool isNullable, bool checkType, string? defaultNamespace) { XmlQualifiedName? xsiType = checkType ? GetXsiType() : null; @@ -1578,7 +1630,10 @@ private XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCall if (mapping.Attribute != null) { - member.Source = (value) => SetOrAddValueToMember(o!, value!, member.Mapping.MemberInfo!); + member.Source = Wrapper; + [RequiresUnreferencedCode("calls SetOrAddValueToMember")] + void Wrapper(object? value) { SetOrAddValueToMember(o!, value!, member.Mapping.MemberInfo!); } + if (mapping.Attribute.Any) { anyAttribute = member; @@ -1668,7 +1723,9 @@ private XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCall if (member.Mapping.CheckSpecified == SpecifiedAccessor.ReadWrite) { - member.CheckSpecifiedSource = (_) => + member.CheckSpecifiedSource = Wrapper; + [RequiresUnreferencedCode("calls GetType on object")] + void Wrapper(object? _) { string specifiedMemberName = member.Mapping.Name + "Specified"; MethodInfo? specifiedMethodInfo = o!.GetType().GetMethod("set_" + specifiedMemberName); @@ -1682,7 +1739,9 @@ private XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCall ChoiceIdentifierAccessor? choice = mapping.ChoiceIdentifier; if (choice != null && o != null) { - member.ChoiceSource = (elementNameObject) => + member.ChoiceSource = Wrapper; + [RequiresUnreferencedCode("Calls SetOrAddValueToMember")] + void Wrapper(object elementNameObject) { string? elementName = elementNameObject as string; foreach (var name in choice.MemberIds!) @@ -1752,6 +1811,7 @@ private XmlSerializationCollectionFixupCallback GetCreateCollectionOfObjectsCall } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private bool WriteEnumAndArrayTypes(out object? o, StructMapping mapping, XmlQualifiedName xsiType, string? defaultNamespace) { foreach (var m in _mapping.Scope!.TypeMappings) @@ -1790,6 +1850,7 @@ private bool WriteEnumAndArrayTypes(out object? o, StructMapping mapping, XmlQua return false; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private bool WriteDerivedTypes(out object? o, StructMapping mapping, XmlQualifiedName xsiType, string? defaultNamespace, bool checkType, bool isNullable) { for (StructMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) @@ -1810,6 +1871,7 @@ private bool WriteDerivedTypes(out object? o, StructMapping mapping, XmlQualifie return false; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteAttributes(Member[] members, Member? anyAttribute, UnknownNodeAction elseCall, ref object? o) { Member? xmlnsMember = null; @@ -1896,6 +1958,7 @@ private void WriteAttributes(Member[] members, Member? anyAttribute, UnknownNode } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void WriteAttribute(Member member, object? attr = null) { AttributeAccessor attribute = member.Mapping.Attribute!; @@ -1943,6 +2006,7 @@ private void WriteAttribute(Member member, object? attr = null) } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void SetOrAddValueToMember(object o, object value, MemberInfo memberInfo) { Type memberType = GetMemberType(memberInfo); @@ -1963,6 +2027,7 @@ private void SetOrAddValueToMember(object o, object value, MemberInfo memberInfo } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void AddItemInArrayMember(object o, MemberInfo memberInfo, Type memberType, object item) { var currentArray = (Array?)GetMemberValue(o, memberInfo); @@ -2003,11 +2068,11 @@ private void CreateUnknownNodeException(object? o) CreateUnknownNodeException(); } - internal class CollectionMember : List + internal sealed class CollectionMember : List { } - internal class Member + internal sealed class Member { public MemberMapping Mapping; public CollectionMember? Collection; @@ -2026,7 +2091,7 @@ public Member(MemberMapping mapping) } } - internal class CheckTypeSource + internal sealed class CheckTypeSource { public string? Id { get; set; } public bool IsObject { get; set; } @@ -2034,7 +2099,7 @@ internal class CheckTypeSource public object? RefObject { get; set; } } - internal class ObjectHolder + internal sealed class ObjectHolder { public object? Object; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs index 96fbd5a7ad3b9..3ed294b0ac692 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs @@ -4,19 +4,17 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text; using System.Xml.Schema; namespace System.Xml.Serialization { - internal class ReflectionXmlSerializationWriter : XmlSerializationWriter + internal sealed class ReflectionXmlSerializationWriter : XmlSerializationWriter { private readonly XmlMapping _mapping; - internal static TypeDesc StringTypeDesc { get; private set; } = (new TypeScope()).GetTypeDesc(typeof(string)); - internal static TypeDesc QnameTypeDesc { get; private set; } = (new TypeScope()).GetTypeDesc(typeof(XmlQualifiedName)); - public ReflectionXmlSerializationWriter(XmlMapping xmlMapping, XmlWriter xmlWriter, XmlSerializerNamespaces namespaces, string? encodingStyle, string? id) { Init(xmlWriter, namespaces, encodingStyle, id, null); @@ -36,6 +34,7 @@ public ReflectionXmlSerializationWriter(XmlMapping xmlMapping, XmlWriter xmlWrit } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected override void InitCallbacks() { TypeScope scope = _mapping.Scope!; @@ -55,6 +54,7 @@ protected override void InitCallbacks() } } + [RequiresUnreferencedCode("calls WriteObjectOfTypeElement")] public void WriteObject(object? o) { XmlMapping xmlMapping = _mapping; @@ -68,11 +68,13 @@ public void WriteObject(object? o) } } + [RequiresUnreferencedCode("calls GenerateTypeElement")] private void WriteObjectOfTypeElement(object? o, XmlTypeMapping mapping) { GenerateTypeElement(o, mapping); } + [RequiresUnreferencedCode("calls WriteReferencedElements")] private void GenerateTypeElement(object? o, XmlTypeMapping xmlMapping) { ElementAccessor element = xmlMapping.Accessor; @@ -113,6 +115,7 @@ private void GenerateTypeElement(object? o, XmlTypeMapping xmlMapping) } } + [RequiresUnreferencedCode("calls WriteElements")] private void WriteMember(object? o, object? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc memberTypeDesc, bool writeAccessors) { if (memberTypeDesc.IsArrayLike && @@ -126,6 +129,7 @@ private void WriteMember(object? o, object? choiceSource, ElementAccessor[] elem } } + [RequiresUnreferencedCode("calls WriteArrayItems")] private void WriteArray(object o, object? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc) { if (elements.Length == 0 && text == null) @@ -149,6 +153,7 @@ private void WriteArray(object o, object? choiceSource, ElementAccessor[] elemen WriteArrayItems(elements, text, choice, arrayTypeDesc, o); } + [RequiresUnreferencedCode("calls WriteElements")] private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc? arrayTypeDesc, object o) { var arr = o as IList; @@ -178,6 +183,7 @@ private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, Cho } } + [RequiresUnreferencedCode("calls CreateUnknownTypeException")] private void WriteElements(object? o, object? enumSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, bool writeAccessors, bool isNullable) { if (elements.Length == 0 && text == null) @@ -309,6 +315,7 @@ private void WriteText(object o, TextAccessor text) } } + [RequiresUnreferencedCode("calls WritePotentiallyReferencingElement")] private void WriteElement(object? o, ElementAccessor element, bool writeAccessor) { string name = writeAccessor ? element.Name : element.Mapping!.TypeName!; @@ -386,7 +393,7 @@ private void WriteElement(object? o, ElementAccessor element, bool writeAccessor else if (element.Mapping is PrimitiveMapping) { var mapping = element.Mapping as PrimitiveMapping; - if (mapping!.TypeDesc == QnameTypeDesc) + if (mapping!.TypeDesc == ReflectionXmlSerializationReader.QnameTypeDesc) { WriteQualifiedNameElement(name, ns!, element.Default, (XmlQualifiedName)o!, element.IsNullable, mapping.IsSoap, mapping); } @@ -437,14 +444,17 @@ private void WriteElement(object? o, ElementAccessor element, bool writeAccessor } } + [RequiresUnreferencedCode("calls WriteStructMethod")] private XmlSerializationWriteCallback CreateXmlSerializationWriteCallback(TypeMapping mapping, string name, string? ns, bool isNullable) { if (mapping is StructMapping structMapping) { - return (o) => + return Wrapper; + [RequiresUnreferencedCode("calls WriteStructMethod")] + void Wrapper(object o) { WriteStructMethod(structMapping, name, ns, o, isNullable, needType: false); - }; + } } else if (mapping is EnumMapping enumMapping) { @@ -489,6 +499,7 @@ private void WriteQualifiedNameElement(string name, string ns, object? defaultVa } } + [RequiresUnreferencedCode("calls WriteTypedPrimitive")] private void WriteStructMethod(StructMapping mapping, string n, string? ns, object? o, bool isNullable, bool needType) { if (mapping.IsSoap && mapping.TypeDesc!.IsRoot) return; @@ -635,6 +646,7 @@ private void WriteStructMethod(StructMapping mapping, string n, string? ns, obje } } + [RequiresUnreferencedCode("Calls GetType on object")] private object? GetMemberValue(object o, string memberName) { MemberInfo memberInfo = ReflectionXmlSerializationHelper.GetMember(o.GetType(), memberName); @@ -642,6 +654,7 @@ private void WriteStructMethod(StructMapping mapping, string n, string? ns, obje return memberValue; } + [RequiresUnreferencedCode("calls WriteMember")] private bool WriteEnumAndArrayTypes(StructMapping structMapping, object o, string n, string? ns) { if (o is Enum) @@ -876,7 +889,7 @@ private bool CanOptimizeWriteListSequence(TypeDesc? listElementTypeDesc) // check to see if we can write values of the attribute sequentially // currently we have only one data type (XmlQualifiedName) that we can not write "inline", // because we need to output xmlns:qx="..." for each of the qnames - return (listElementTypeDesc != null && listElementTypeDesc != QnameTypeDesc); + return (listElementTypeDesc != null && listElementTypeDesc != ReflectionXmlSerializationReader.QnameTypeDesc); } private void WriteAttribute(object memberValue, AttributeAccessor attribute, object? container) @@ -913,6 +926,7 @@ private int FindXmlnsIndex(MemberMapping[] members) return -1; } + [RequiresUnreferencedCode("calls WriteStructMethod")] private bool WriteDerivedTypes(StructMapping mapping, string n, string? ns, object o, bool isNullable) { Type t = o.GetType(); @@ -1075,7 +1089,7 @@ private bool IsDefaultValue(TypeMapping mapping, object o, object value, bool is private bool WritePrimitiveValue(TypeDesc typeDesc, object? o, bool isElement, out string? stringValue) { - if (typeDesc == StringTypeDesc || typeDesc.FormatterName == "String") + if (typeDesc == ReflectionXmlSerializationReader.StringTypeDesc || typeDesc.FormatterName == "String") { stringValue = (string?)o; return true; @@ -1114,7 +1128,7 @@ private bool WritePrimitiveValue(TypeDesc typeDesc, object? o, bool isElement, o throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Invalid DateTime")); } } - else if (typeDesc == QnameTypeDesc) + else if (typeDesc == ReflectionXmlSerializationReader.QnameTypeDesc) { stringValue = FromXmlQualifiedName((XmlQualifiedName?)o); return true; @@ -1186,6 +1200,7 @@ private string ConvertPrimitiveToString(object o, TypeDesc typeDesc) return stringValue; } + [RequiresUnreferencedCode("calls WritePotentiallyReferencingElement")] private void GenerateMembersElement(object o, XmlMembersMapping xmlMembersMapping) { ElementAccessor element = xmlMembersMapping.Accessor; @@ -1338,8 +1353,9 @@ private enum WritePrimitiveMethodRequirement } } - internal class ReflectionXmlSerializationHelper + internal static class ReflectionXmlSerializationHelper { + [RequiresUnreferencedCode("Reflects over base members")] public static MemberInfo GetMember(Type declaringType, string memberName) { MemberInfo[] memberInfos = declaringType.GetMember(memberName); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaImporter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaImporter.cs index 2df97cbc0e73a..7e5a4ac6572a6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaImporter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaImporter.cs @@ -7,6 +7,7 @@ namespace System.Xml.Serialization using System.Xml.Schema; using System.Collections; using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Configuration; using System.Xml.Serialization.Configuration; @@ -26,6 +27,7 @@ public abstract class SchemaImporter private NameTable? _typesInUse; private NameTable? _groupsInUse; + [RequiresUnreferencedCode("calls SetCache")] internal SchemaImporter(XmlSchemas schemas, CodeGenerationOptions options, ImportContext context) { if (!schemas.Contains(XmlSchema.Namespace)) @@ -114,6 +116,7 @@ internal CodeGenerationOptions Options get { return _options; } } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal void MakeDerived(StructMapping structMapping, Type? baseType, bool baseTypeCanBeIndirect) { structMapping.ReferencedByTopLevelElement = true; @@ -144,6 +147,7 @@ internal string GenerateUniqueTypeName(string typeName) return TypeIdentifiers.AddUnique(typeName, typeName); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private StructMapping CreateRootMapping() { TypeDesc typeDesc = Scope.GetTypeDesc(typeof(object)); @@ -157,6 +161,7 @@ private StructMapping CreateRootMapping() return mapping; } + [RequiresUnreferencedCode("calls CreateRootMapping")] internal StructMapping GetRootMapping() { if (_root == null) @@ -164,6 +169,7 @@ internal StructMapping GetRootMapping() return _root; } + [RequiresUnreferencedCode("calls GetRootMapping")] internal StructMapping ImportRootMapping() { if (!_rootImported) @@ -174,6 +180,7 @@ internal StructMapping ImportRootMapping() return GetRootMapping(); } + [RequiresUnreferencedCode("calls ImportType")] internal abstract void ImportDerivedTypes(XmlQualifiedName baseName); internal void AddReference(XmlQualifiedName name, NameTable references, string error) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaObjectWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaObjectWriter.cs index e5ac5adefa025..49585a4ec4630 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaObjectWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SchemaObjectWriter.cs @@ -13,7 +13,7 @@ namespace System.Xml.Serialization using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; - internal class XmlAttributeComparer : IComparer + internal sealed class XmlAttributeComparer : IComparer { public int Compare(object? o1, object? o2) { @@ -28,7 +28,7 @@ public int Compare(object? o1, object? o2) } } - internal class XmlFacetComparer : IComparer + internal sealed class XmlFacetComparer : IComparer { public int Compare(object? o1, object? o2) { @@ -38,7 +38,7 @@ public int Compare(object? o1, object? o2) } } - internal class QNameComparer : IComparer + internal sealed class QNameComparer : IComparer { public int Compare(object? o1, object? o2) { @@ -53,7 +53,7 @@ public int Compare(object? o1, object? o2) } } - internal class XmlSchemaObjectComparer : IComparer + internal sealed class XmlSchemaObjectComparer : IComparer { private readonly QNameComparer _comparer = new QNameComparer(); public int Compare(object? o1, object? o2) @@ -149,7 +149,7 @@ internal static XmlQualifiedName NameOf(XmlSchemaObjectCollection items) } } - internal class SchemaObjectWriter + internal sealed class SchemaObjectWriter { private readonly StringBuilder _w = new StringBuilder(); private int _indentLevel = -1; @@ -161,7 +161,7 @@ private void WriteIndent() _w.Append(' '); } } - protected void WriteAttribute(string localName, string ns, string? value) + private void WriteAttribute(string localName, string ns, string? value) { if (value == null || value.Length == 0) return; @@ -173,32 +173,32 @@ protected void WriteAttribute(string localName, string ns, string? value) _w.Append('='); _w.Append(value); } - protected void WriteAttribute(string localName, string ns, XmlQualifiedName value) + private void WriteAttribute(string localName, string ns, XmlQualifiedName value) { if (value.IsEmpty) return; WriteAttribute(localName, ns, value.ToString()); } - protected void WriteStartElement(string name) + private void WriteStartElement(string name) { NewLine(); _indentLevel++; _w.Append('['); _w.Append(name); } - protected void WriteEndElement() + private void WriteEndElement() { _w.Append(']'); _indentLevel--; } - protected void NewLine() + private void NewLine() { _w.Append(Environment.NewLine); WriteIndent(); } - protected string GetString() + private string GetString() { return _w.ToString(); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs index 2878eeb604c19..39310b9a26f63 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SoapReflectionImporter.cs @@ -12,6 +12,7 @@ namespace System.Xml.Serialization using System.Threading; using System.Xml; using System.Xml.Serialization; + using System.Diagnostics.CodeAnalysis; public class SoapReflectionImporter { @@ -47,11 +48,13 @@ public SoapReflectionImporter(SoapAttributeOverrides? attributeOverrides, string _modelScope = new ModelScope(_typeScope); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void IncludeTypes(ICustomAttributeProvider provider) { IncludeTypes(provider, new RecursionLimiter()); } + [RequiresUnreferencedCode("calls IncludeType")] private void IncludeTypes(ICustomAttributeProvider provider, RecursionLimiter limiter) { object[] attrs = provider.GetCustomAttributes(typeof(SoapIncludeAttribute), false); @@ -61,21 +64,25 @@ private void IncludeTypes(ICustomAttributeProvider provider, RecursionLimiter li } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void IncludeType(Type type) { IncludeType(type, new RecursionLimiter()); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private void IncludeType(Type type, RecursionLimiter limiter) { ImportTypeMapping(_modelScope.GetTypeModel(type), limiter); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(Type type) { return ImportTypeMapping(type, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(Type type, string? defaultNamespace) { ElementAccessor element = new ElementAccessor(); @@ -91,21 +98,25 @@ public XmlTypeMapping ImportTypeMapping(Type type, string? defaultNamespace) return xmlMapping; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members) { return ImportMembersMapping(elementName, ns, members, true, true, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors) { return ImportMembersMapping(elementName, ns, members, hasWrapperElement, writeAccessors, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate) { return ImportMembersMapping(elementName, ns, members, hasWrapperElement, writeAccessors, validate, XmlMappingAccess.Read | XmlMappingAccess.Write); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate, XmlMappingAccess access) { ElementAccessor element = new ElementAccessor(); @@ -141,11 +152,13 @@ private SoapAttributes GetAttributes(MemberInfo memberInfo) return new SoapAttributes(memberInfo); } + [RequiresUnreferencedCode("calls ImportTypeMapping")] private TypeMapping ImportTypeMapping(TypeModel model, RecursionLimiter limiter) { return ImportTypeMapping(model, string.Empty, limiter); } + [RequiresUnreferencedCode("Calls TypeDesc")] private TypeMapping ImportTypeMapping(TypeModel model, string dataType, RecursionLimiter limiter) { if (dataType.Length > 0) @@ -206,6 +219,7 @@ private TypeMapping ImportTypeMapping(TypeModel model, string dataType, Recursio } } + [RequiresUnreferencedCode("calls GetTypeDesc")] private StructMapping CreateRootMapping() { TypeDesc typeDesc = _typeScope.GetTypeDesc(typeof(object)); @@ -219,6 +233,7 @@ private StructMapping CreateRootMapping() return mapping; } + [RequiresUnreferencedCode("calls CreateRootMapping")] private StructMapping GetRootMapping() { if (_root == null) @@ -238,7 +253,8 @@ private StructMapping GetRootMapping() return mapping; } - private NullableMapping CreateNullableMapping(TypeMapping baseMapping, Type type) + private NullableMapping CreateNullableMapping(TypeMapping baseMapping, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { TypeDesc typeDesc = baseMapping.TypeDesc!.GetNullableTypeDesc(type); TypeMapping? existingMapping = (TypeMapping?)_nullables[baseMapping.TypeName!, baseMapping.Namespace]; @@ -275,6 +291,7 @@ private NullableMapping CreateNullableMapping(TypeMapping baseMapping, Type type return mapping; } + [RequiresUnreferencedCode("calls GetRootMapping")] private StructMapping ImportStructLikeMapping(StructModel model, RecursionLimiter limiter) { if (model.TypeDesc.Kind == TypeKind.Root) return GetRootMapping(); @@ -332,7 +349,7 @@ private StructMapping ImportStructLikeMapping(StructModel model, RecursionLimite return mapping; } - + [RequiresUnreferencedCode("calls GetTypeModel")] private bool InitializeStructMembers(StructMapping mapping, StructModel model, RecursionLimiter limiter) { if (mapping.IsFullyInitialized) @@ -397,7 +414,7 @@ private bool InitializeStructMembers(StructMapping mapping, StructModel model, R return true; } - + [RequiresUnreferencedCode("calls IncludeTypes")] private ArrayMapping ImportArrayLikeMapping(ArrayModel model, RecursionLimiter limiter) { ArrayMapping mapping = new ArrayMapping(); @@ -534,6 +551,7 @@ private PrimitiveMapping ImportPrimitiveMapping(PrimitiveModel model, string dat return mapping; } + [RequiresUnreferencedCode("calls XsdTypeName")] private EnumMapping ImportEnumMapping(EnumModel model) { SoapAttributes a = GetAttributes(model.Type); @@ -585,6 +603,7 @@ private EnumMapping ImportEnumMapping(EnumModel model) return constant; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, string? ns, bool hasWrapperElement, bool writeAccessors, bool validateWrapperElement, RecursionLimiter limiter) { MembersMapping members = new MembersMapping(); @@ -625,6 +644,7 @@ private MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionM return members; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private MemberMapping? ImportMemberMapping(XmlReflectionMember xmlReflectionMember, string? ns, XmlReflectionMember[] xmlReflectionMembers, XmlSchemaForm form, RecursionLimiter limiter) { SoapAttributes a = xmlReflectionMember.SoapAttributes; @@ -643,6 +663,7 @@ private MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionM return member; } + [RequiresUnreferencedCode("calls ImportAccessorMapping")] private MemberMapping? ImportFieldMapping(FieldModel model, SoapAttributes a, string ns, RecursionLimiter limiter) { if (a.SoapIgnore) return null; @@ -659,6 +680,7 @@ private MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionM return member; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private void ImportAccessorMapping(MemberMapping accessor, FieldModel model, SoapAttributes a, string? ns, XmlSchemaForm form, RecursionLimiter limiter) { Type accessorType = model.FieldType; @@ -714,6 +736,7 @@ private static ElementAccessor CreateElementAccessor(TypeMapping mapping, string return element; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private object? GetDefaultValue(TypeDesc fieldTypeDesc, SoapAttributes a) { if (a.SoapDefaultValue == null || a.SoapDefaultValue == DBNull.Value) return null; @@ -736,6 +759,7 @@ private static ElementAccessor CreateElementAccessor(TypeMapping mapping, string return a.SoapDefaultValue; } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal string XsdTypeName(Type type) { if (type == typeof(object)) return Soap.UrType; @@ -744,6 +768,8 @@ internal string XsdTypeName(Type type) return typeDesc.DataType.Name; return XsdTypeName(type, GetAttributes(type), typeDesc.Name); } + + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] internal string XsdTypeName(Type type, SoapAttributes a, string name) { string typeName = name; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs index eb99da1a1686d..5b4e7fec3459e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/SourceInfo.cs @@ -12,7 +12,7 @@ namespace System.Xml.Serialization { - internal class SourceInfo + internal sealed class SourceInfo { //a[ia] //((global::System.Xml.Serialization.XmlSerializerNamespaces)p[0]) @@ -32,10 +32,13 @@ internal class SourceInfo public string Source; public readonly string Arg; public readonly MemberInfo? MemberInfo; + + [DynamicallyAccessedMembers(TrimmerConstants.AllMethods)] public readonly Type? Type; public readonly CodeGenerator ILG; - public SourceInfo(string source, string? arg, MemberInfo? memberInfo, Type? type, CodeGenerator ilg) + public SourceInfo(string source, string? arg, MemberInfo? memberInfo, + [DynamicallyAccessedMembers(TrimmerConstants.AllMethods)] Type? type, CodeGenerator ilg) { this.Source = source; this.Arg = arg ?? source; @@ -49,16 +52,19 @@ public SourceInfo CastTo(TypeDesc td) return new SourceInfo("((" + td.CSharpName + ")" + Source + ")", Arg, MemberInfo, td.Type!, ILG); } + [RequiresUnreferencedCode("calls InternalLoad")] public void LoadAddress(Type? elementType) { InternalLoad(elementType, asAddress: true); } + [RequiresUnreferencedCode("calls InternalLoad")] public void Load(Type? elementType) { InternalLoad(elementType); } + [RequiresUnreferencedCode("calls LoadMemberAddress")] private void InternalLoad(Type? elementType, bool asAddress = false) { Match match = s_regex.Match(Arg); @@ -209,7 +215,9 @@ private void Convert(Type sourceType, Type? targetType, bool asAddress) } } - private void ConvertNullableValue(Type nullableType, Type targetType) + private void ConvertNullableValue( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods + | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type nullableType, Type targetType) { System.Diagnostics.Debug.Assert(targetType == nullableType || targetType.IsAssignableFrom(nullableType.GetGenericArguments()[0])); if (targetType != nullableType) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs index f84d014c93ae6..77b150b7626b3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/TypeExtensions.cs @@ -4,13 +4,17 @@ namespace System.Xml.Serialization { using System; + using System.Diagnostics.CodeAnalysis; using System.Reflection; internal static class TypeExtensions { private const string ImplicitCastOperatorName = "op_Implicit"; - public static bool TryConvertTo(this Type targetType, object? data, out object? returnValue) + public static bool TryConvertTo( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + this Type targetType, + object? data, out object? returnValue) { if (targetType == null) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs index d9c83f45719a8..e2e92b3334f1a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Types.cs @@ -7,6 +7,7 @@ namespace System.Xml.Serialization using System.Collections; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text; using System.Xml; @@ -62,7 +63,22 @@ internal enum TypeFlags Unsupported = 0x100000, } - internal class TypeDesc + // Shorthands for common trimmer constants + internal static class TrimmerConstants + { + public const DynamicallyAccessedMemberTypes AllMethods = DynamicallyAccessedMemberTypes.PublicMethods + | DynamicallyAccessedMemberTypes.NonPublicMethods + | DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors; + public const DynamicallyAccessedMemberTypes PublicMembers = DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.PublicMethods + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.PublicNestedTypes + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.PublicEvents; + } + + internal sealed class TypeDesc { private readonly string _name; private readonly string _fullName; @@ -72,6 +88,7 @@ internal class TypeDesc private TypeDesc? _nullableTypeDesc; private readonly TypeKind _kind; private readonly XmlSchemaType? _dataType; + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] private Type? _type; private TypeDesc? _baseTypeDesc; private TypeFlags _flags; @@ -105,13 +122,15 @@ internal TypeDesc(string name, string fullName, TypeKind kind, TypeDesc? baseTyp : this(name, fullName, (XmlSchemaType?)null, kind, baseTypeDesc, flags, null) { } - internal TypeDesc(Type type, bool isXsdType, XmlSchemaType dataType, string formatterName, TypeFlags flags) + internal TypeDesc( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool isXsdType, XmlSchemaType dataType, string formatterName, TypeFlags flags) : this(type!.Name, type.FullName!, dataType, TypeKind.Primitive, (TypeDesc?)null, flags, formatterName) { _isXsdType = isXsdType; _type = type; } - internal TypeDesc(Type? type, string name, string fullName, TypeKind kind, TypeDesc? baseTypeDesc, TypeFlags flags, TypeDesc? arrayElementTypeDesc) + internal TypeDesc( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type? type, string name, string fullName, TypeKind kind, TypeDesc? baseTypeDesc, TypeFlags flags, TypeDesc? arrayElementTypeDesc) : this(name, fullName, null, kind, baseTypeDesc, flags, null) { _arrayElementTypeDesc = arrayElementTypeDesc; @@ -165,6 +184,7 @@ internal XmlSchemaType? DataType get { return _dataType; } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] internal Type? Type { get { return _type; } @@ -347,7 +367,8 @@ internal Exception? Exception set { _exception = value; } } - internal TypeDesc GetNullableTypeDesc(Type type) + internal TypeDesc GetNullableTypeDesc( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { if (IsOptionalValue) return this; @@ -456,7 +477,7 @@ internal bool IsDerivedFrom(TypeDesc baseTypeDesc) } } - internal class TypeScope + internal sealed class TypeScope { private readonly Hashtable _typeDescs = new Hashtable(); private readonly Hashtable _arrayTypeDescs = new Hashtable(); @@ -622,7 +643,9 @@ private static void AddSoapEncodedTypes(string ns) AddSoapEncodedPrimitive(typeof(byte[]), "base64", ns, "ByteArrayBase64", new XmlQualifiedName("base64Binary", XmlSchema.Namespace), TypeFlags.CanBeAttributeValue | TypeFlags.CanBeElementValue | TypeFlags.IgnoreDefault | TypeFlags.Reference); } - private static void AddPrimitive(Type type, string dataTypeName, string formatterName, TypeFlags flags) + private static void AddPrimitive( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + string dataTypeName, string formatterName, TypeFlags flags) { XmlSchemaSimpleType dataType = new XmlSchemaSimpleType(); dataType.Name = dataTypeName; @@ -633,7 +656,9 @@ private static void AddPrimitive(Type type, string dataTypeName, string formatte s_primitiveNames.Add(dataTypeName, XmlSchema.Namespace, typeDesc); } - private static void AddNonXsdPrimitive(Type type, string dataTypeName, string ns, string formatterName, XmlQualifiedName baseTypeName, XmlSchemaFacet[] facets, TypeFlags flags) + private static void AddNonXsdPrimitive( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + string dataTypeName, string ns, string formatterName, XmlQualifiedName baseTypeName, XmlSchemaFacet[] facets, TypeFlags flags) { XmlSchemaSimpleType dataType = new XmlSchemaSimpleType(); dataType.Name = dataTypeName; @@ -651,7 +676,9 @@ private static void AddNonXsdPrimitive(Type type, string dataTypeName, string ns s_primitiveNames.Add(dataTypeName, ns, typeDesc); } - private static void AddSoapEncodedPrimitive(Type type, string dataTypeName, string ns, string formatterName, XmlQualifiedName baseTypeName, TypeFlags flags) + private static void AddSoapEncodedPrimitive( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, + string dataTypeName, string ns, string formatterName, XmlQualifiedName baseTypeName, TypeFlags flags) { AddNonXsdPrimitive(type, dataTypeName, ns, formatterName, baseTypeName, Array.Empty(), flags); } @@ -679,16 +706,19 @@ private static void AddSoapEncodedPrimitive(Type type, string dataTypeName, stri return (TypeDesc?)s_primitiveDataTypes[dataType]; } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal TypeDesc GetTypeDesc(Type type) { return GetTypeDesc(type, null, true, true); } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal TypeDesc GetTypeDesc(Type type, MemberInfo? source, bool directReference) { return GetTypeDesc(type, source, directReference, true); } + [RequiresUnreferencedCode("calls ImportTypeDesc")] internal TypeDesc GetTypeDesc(Type type, MemberInfo? source, bool directReference, bool throwOnError) { if (type.ContainsGenericParameters) @@ -711,6 +741,7 @@ internal TypeDesc GetTypeDesc(Type type, MemberInfo? source, bool directReferenc return typeDesc; } + [RequiresUnreferencedCode("calls ImportTypeDesc")] internal TypeDesc GetArrayTypeDesc(Type type) { TypeDesc? typeDesc = (TypeDesc?)_arrayTypeDescs[type]; @@ -747,6 +778,7 @@ internal TypeDesc GetArrayTypeDesc(Type type) return null; } + [RequiresUnreferencedCode("calls GetEnumeratorElementType")] private TypeDesc ImportTypeDesc(Type type, MemberInfo? memberInfo, bool directReference) { TypeDesc? typeDesc = null; @@ -1012,6 +1044,7 @@ internal static string TypeName(Type t) return t.Name; } + [RequiresUnreferencedCode("calls GetEnumeratorElementType")] internal static Type? GetArrayElementType(Type type, string? memberInfo) { if (type.IsArray) @@ -1160,7 +1193,13 @@ private static void PopulateMemberInfos(StructMapping structMapping, MemberMappi } } - private static bool ShouldBeReplaced(MemberInfo memberInfoToBeReplaced, Type derivedType, out MemberInfo replacedInfo) + private static bool ShouldBeReplaced( + MemberInfo memberInfoToBeReplaced, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields)] Type derivedType, + out MemberInfo replacedInfo) { replacedInfo = memberInfoToBeReplaced; Type currentType = derivedType; @@ -1214,7 +1253,10 @@ private static bool ShouldBeReplaced(MemberInfo memberInfoToBeReplaced, Type der return false; } - private static TypeFlags GetConstructorFlags(Type type, ref Exception? exception) + private static TypeFlags GetConstructorFlags( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type, + ref Exception? exception) { ConstructorInfo? ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, Type.EmptyTypes); if (ctor != null) @@ -1239,6 +1281,7 @@ private static TypeFlags GetConstructorFlags(Type type, ref Exception? exception return 0; } + [RequiresUnreferencedCode("Needs to mark members on the return type of the GetEnumerator method")] private static Type? GetEnumeratorElementType(Type type, ref TypeFlags flags) { if (typeof(IEnumerable).IsAssignableFrom(type)) @@ -1299,7 +1342,8 @@ private static TypeFlags GetConstructorFlags(Type type, ref Exception? exception } } - internal static PropertyInfo GetDefaultIndexer(Type type, string? memberInfo) + internal static PropertyInfo GetDefaultIndexer( + [DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type, string? memberInfo) { if (typeof(IDictionary).IsAssignableFrom(type)) { @@ -1349,7 +1393,9 @@ internal static PropertyInfo GetDefaultIndexer(Type type, string? memberInfo) } return indexer; } - private static Type GetCollectionElementType(Type type, string? memberInfo) + private static Type GetCollectionElementType( + [DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type, + string? memberInfo) { return GetDefaultIndexer(type, memberInfo).PropertyType; } @@ -1413,33 +1459,29 @@ internal ICollection TypeMappings internal static Hashtable PrimtiveTypes { get { return s_primitiveTypes; } } } - internal class Soap + internal static class Soap { - private Soap() { } internal const string Encoding = "http://schemas.xmlsoap.org/soap/encoding/"; internal const string UrType = "anyType"; internal const string Array = "Array"; internal const string ArrayType = "arrayType"; } - internal class Soap12 + internal static class Soap12 { - private Soap12() { } internal const string Encoding = "http://www.w3.org/2003/05/soap-encoding"; internal const string RpcNamespace = "http://www.w3.org/2003/05/soap-rpc"; internal const string RpcResult = "result"; } - internal class Wsdl + internal static class Wsdl { - private Wsdl() { } internal const string Namespace = "http://schemas.xmlsoap.org/wsdl/"; internal const string ArrayType = "arrayType"; } - internal class UrtTypes + internal static class UrtTypes { - private UrtTypes() { } internal const string Namespace = "http://microsoft.com/wsdl/types/"; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs index 06c999cc2b86a..0a0cbd0649867 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlAttributes.cs @@ -41,7 +41,6 @@ public class XmlAttributes private XmlTypeAttribute? _xmlType; private XmlAnyAttributeAttribute? _xmlAnyAttribute; private readonly XmlChoiceIdentifierAttribute? _xmlChoiceIdentifier; - private static volatile Type? s_ignoreAttributeType; /// @@ -72,22 +71,6 @@ internal XmlAttributeFlags XmlFlags } } - private static Type IgnoreAttribute - { - get - { - if (s_ignoreAttributeType == null) - { - s_ignoreAttributeType = typeof(object).Assembly.GetType("System.XmlIgnoreMemberAttribute"); - if (s_ignoreAttributeType == null) - { - s_ignoreAttributeType = typeof(XmlIgnoreAttribute); - } - } - return s_ignoreAttributeType; - } - } - /// /// [To be supplied.] /// @@ -99,7 +82,7 @@ public XmlAttributes(ICustomAttributeProvider provider) XmlAnyElementAttribute? wildcard = null; for (int i = 0; i < attrs.Length; i++) { - if (attrs[i] is XmlIgnoreAttribute || attrs[i] is ObsoleteAttribute || attrs[i].GetType() == IgnoreAttribute) + if (attrs[i] is XmlIgnoreAttribute || attrs[i] is ObsoleteAttribute) { _xmlIgnore = true; break; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs index 702388f4581f4..e84d5994f776b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlReflectionImporter.cs @@ -15,6 +15,8 @@ namespace System.Xml.Serialization using System.Xml.Extensions; using System.Xml; using System.Xml.Serialization; + using System.Diagnostics.CodeAnalysis; + using System.Runtime.CompilerServices; /// /// @@ -86,11 +88,13 @@ public XmlReflectionImporter(XmlAttributeOverrides? attributeOverrides, string? /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void IncludeTypes(ICustomAttributeProvider provider) { IncludeTypes(provider, new RecursionLimiter()); } + [RequiresUnreferencedCode("calls IncludeType")] private void IncludeTypes(ICustomAttributeProvider provider, RecursionLimiter limiter) { object[] attrs = provider.GetCustomAttributes(typeof(XmlIncludeAttribute), false); @@ -104,11 +108,13 @@ private void IncludeTypes(ICustomAttributeProvider provider, RecursionLimiter li /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void IncludeType(Type type) { IncludeType(type, new RecursionLimiter()); } + [RequiresUnreferencedCode("calls ImportTypeMapping")] private void IncludeType(Type type, RecursionLimiter limiter) { int previousNestingLevel = _arrayNestingLevel; @@ -132,6 +138,7 @@ private void IncludeType(Type type, RecursionLimiter limiter) /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(Type type) { return ImportTypeMapping(type, null, null); @@ -140,6 +147,7 @@ public XmlTypeMapping ImportTypeMapping(Type type) /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(Type type, string? defaultNamespace) { return ImportTypeMapping(type, null, defaultNamespace); @@ -148,6 +156,7 @@ public XmlTypeMapping ImportTypeMapping(Type type, string? defaultNamespace) /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(Type type, XmlRootAttribute? root) { return ImportTypeMapping(type, root, null); @@ -156,6 +165,7 @@ public XmlTypeMapping ImportTypeMapping(Type type, XmlRootAttribute? root) /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(Type type, XmlRootAttribute? root, string? defaultNamespace) { if (type == null) @@ -169,6 +179,7 @@ public XmlTypeMapping ImportTypeMapping(Type type, XmlRootAttribute? root, strin /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement) { return ImportMembersMapping(elementName, ns, members, hasWrapperElement, false); @@ -177,6 +188,7 @@ public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, X /// /// [To be supplied.] /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc) { return ImportMembersMapping(elementName, ns, members, hasWrapperElement, rpc, false); @@ -186,6 +198,7 @@ public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, X /// [To be supplied.] /// /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, bool openModel) { return ImportMembersMapping(elementName, ns, members, hasWrapperElement, rpc, openModel, XmlMappingAccess.Read | XmlMappingAccess.Write); @@ -195,6 +208,7 @@ public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, X /// [To be supplied.] /// /// + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string? elementName, string? ns, XmlReflectionMember[] members, bool hasWrapperElement, bool rpc, bool openModel, XmlMappingAccess access) { ElementAccessor element = new ElementAccessor(); @@ -242,6 +256,7 @@ private XmlAttributes GetAttributes(MemberInfo memberInfo) return new XmlAttributes(memberInfo); } + [RequiresUnreferencedCode("calls ImportTypeMapping")] private ElementAccessor ImportElement(TypeModel model, XmlRootAttribute? root, string? defaultNamespace, RecursionLimiter limiter) { XmlAttributes a = GetAttributes(model.Type, true); @@ -366,11 +381,13 @@ private Exception CreateMemberReflectionException(FieldModel model, Exception e) return new InvalidOperationException(SR.Format(model.IsProperty ? SR.XmlPropertyReflectionError : SR.XmlFieldReflectionError, model.Name), e); } + [RequiresUnreferencedCode("calls ImportTypeMapping")] private TypeMapping ImportTypeMapping(TypeModel model, string? ns, ImportContext context, string dataType, XmlAttributes? a, RecursionLimiter limiter) { return ImportTypeMapping(model, ns, context, dataType, a, false, false, limiter); } + [RequiresUnreferencedCode("calls ImportEnumMapping")] private TypeMapping ImportTypeMapping(TypeModel model, string? ns, ImportContext context, string dataType, XmlAttributes? a, bool repeats, bool openModel, RecursionLimiter limiter) { try @@ -460,7 +477,8 @@ private TypeMapping ImportTypeMapping(TypeModel model, string? ns, ImportContext } } - internal static MethodInfo? GetMethodFromSchemaProvider(XmlSchemaProviderAttribute provider, Type type) + internal static MethodInfo? GetMethodFromSchemaProvider(XmlSchemaProviderAttribute provider, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type) { if (provider.IsAny) { @@ -484,6 +502,7 @@ private TypeMapping ImportTypeMapping(TypeModel model, string? ns, ImportContext return getMethod; } + [RequiresUnreferencedCode("calls IncludeTypes")] private SpecialMapping ImportSpecialMapping(Type type, TypeDesc typeDesc, string? ns, ImportContext context, RecursionLimiter limiter) { if (_specials == null) @@ -609,6 +628,7 @@ private static Exception UnsupportedException(TypeDesc typeDesc, ImportContext c return new InvalidOperationException(SR.Format(SR.XmlIllegalTypeContext, typeDesc.FullName, GetContextName(context))); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private StructMapping CreateRootMapping() { TypeDesc typeDesc = _typeScope.GetTypeDesc(typeof(object)); @@ -621,7 +641,8 @@ private StructMapping CreateRootMapping() return mapping; } - private NullableMapping CreateNullableMapping(TypeMapping baseMapping, Type type) + private NullableMapping CreateNullableMapping(TypeMapping baseMapping, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type) { TypeDesc typeDesc = baseMapping.TypeDesc!.GetNullableTypeDesc(type); TypeMapping? existingMapping; @@ -675,6 +696,7 @@ private NullableMapping CreateNullableMapping(TypeMapping baseMapping, Type type return mapping; } + [RequiresUnreferencedCode("calls CreateRootMapping")] private StructMapping GetRootMapping() { if (_root == null) @@ -699,6 +721,7 @@ private StructMapping GetRootMapping() return mapping; } + [RequiresUnreferencedCode("calls GetRootMapping")] private StructMapping ImportStructLikeMapping(StructModel model, string? ns, bool openModel, XmlAttributes? a, RecursionLimiter limiter) { if (model.TypeDesc.Kind == TypeKind.Root) return GetRootMapping(); @@ -764,6 +787,7 @@ private StructMapping ImportStructLikeMapping(StructModel model, string? ns, boo return mapping; } + [RequiresUnreferencedCode("calls GetTypeModel")] private bool InitializeStructMembers(StructMapping mapping, StructModel model, bool openModel, string? typeName, RecursionLimiter limiter) { if (mapping.IsFullyInitialized) @@ -926,6 +950,7 @@ private static bool IsAnonymousType(XmlAttributes a, string? contextNs) return false; } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal string XsdTypeName(Type type) { if (type == typeof(object)) return Soap.UrType; @@ -935,6 +960,7 @@ internal string XsdTypeName(Type type) return XsdTypeName(type, GetAttributes(type, false), typeDesc.Name); } + [RequiresUnreferencedCode("Calls XsdTypeName")] internal string XsdTypeName(Type type, XmlAttributes a, string name) { string typeName = name; @@ -971,6 +997,7 @@ private static int CountAtLevel(XmlArrayItemAttributes attributes, int level) return sum; } + [RequiresUnreferencedCode("calls XsdTypeName")] private void SetArrayMappingType(ArrayMapping mapping, string? defaultNs, Type type) { XmlAttributes a = GetAttributes(type, false); @@ -1061,6 +1088,7 @@ private void SetArrayMappingType(ArrayMapping mapping, string? defaultNs, Type t mapping.Namespace = ns; } + [RequiresUnreferencedCode("calls SetArrayMappingType")] private ArrayMapping ImportArrayLikeMapping(ArrayModel model, string? ns, RecursionLimiter limiter) { ArrayMapping mapping = new ArrayMapping(); @@ -1156,6 +1184,7 @@ private PrimitiveMapping ImportPrimitiveMapping(PrimitiveModel model, ImportCont return mapping; } + [RequiresUnreferencedCode("calls XsdTypeName")] private EnumMapping ImportEnumMapping(EnumModel model, string? ns, bool repeats) { XmlAttributes a = GetAttributes(model.Type, false); @@ -1214,6 +1243,7 @@ private EnumMapping ImportEnumMapping(EnumModel model, string? ns, bool repeats) return constant; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, string? ns, bool hasWrapperElement, bool rpc, bool openModel, RecursionLimiter limiter) { MembersMapping members = new MembersMapping(); @@ -1291,6 +1321,7 @@ private MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionM return members; } + [RequiresUnreferencedCode("Calls TypeScope.GetTypeDesc(Type) and XmlReflectionImporter.ImportAccessorMapping both of which RequireUnreferencedCode")] private MemberMapping ImportMemberMapping(XmlReflectionMember xmlReflectionMember, string? ns, XmlReflectionMember[] xmlReflectionMembers, bool rpc, bool openModel, RecursionLimiter limiter) { XmlSchemaForm form = rpc ? XmlSchemaForm.Unqualified : XmlSchemaForm.Qualified; @@ -1371,6 +1402,7 @@ private MemberMapping ImportMemberMapping(XmlReflectionMember xmlReflectionMembe return null; } + [RequiresUnreferencedCode("calls ImportAccessorMapping")] private MemberMapping ImportFieldMapping(StructModel parent, FieldModel model, XmlAttributes a, string? ns, RecursionLimiter limiter) { MemberMapping member = new MemberMapping(); @@ -1428,6 +1460,7 @@ private Type GetChoiceIdentifierType(XmlChoiceIdentifierAttribute choice, XmlRef throw new InvalidOperationException(SR.Format(SR.XmlChoiceIdentiferMemberMissing, choice.MemberName, accessorName)); } + [RequiresUnreferencedCode("calls GetFieldModel")] private Type GetChoiceIdentifierType(XmlChoiceIdentifierAttribute choice, StructModel structModel, bool isArrayLike, string accessorName) { // check that the choice field exists @@ -1463,6 +1496,7 @@ private Type GetChoiceIdentifierType(XmlChoiceIdentifierAttribute choice, Struct return enumType; } + [RequiresUnreferencedCode("calls ImportTypeMapping")] private void CreateArrayElementsFromAttributes(ArrayMapping arrayMapping, XmlArrayItemAttributes attributes, Type arrayElementType, string? arrayElementNs, RecursionLimiter limiter) { NameTable arrayItemElements = new NameTable(); // xmlelementname + xmlns -> ElementAccessor @@ -1487,6 +1521,7 @@ private void CreateArrayElementsFromAttributes(ArrayMapping arrayMapping, XmlArr arrayMapping.Elements = (ElementAccessor[])arrayItemElements.ToArray(typeof(ElementAccessor)); } + [RequiresUnreferencedCode("calls GetArrayElementType")] private void ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, string? ns, Type? choiceIdentifierType, bool rpc, bool openModel, RecursionLimiter limiter) { XmlSchemaForm elementFormDefault = XmlSchemaForm.Qualified; @@ -2219,6 +2254,7 @@ private static ElementAccessor CreateElementAccessor(TypeMapping mapping, string } // will create a shallow type mapping for a top-level type + [RequiresUnreferencedCode("Calls TypeScope.GetTypeDesc(Type) which has RequiresUnreferencedCode")] internal static XmlTypeMapping GetTopLevelMapping(Type type, string? defaultNamespace) { defaultNamespace = defaultNamespace ?? string.Empty; @@ -2247,7 +2283,7 @@ internal static XmlTypeMapping GetTopLevelMapping(Type type, string? defaultName return mapping; } } - internal class ImportStructWorkItem + internal sealed class ImportStructWorkItem { private readonly StructModel _model; private readonly StructMapping _mapping; @@ -2262,7 +2298,7 @@ internal ImportStructWorkItem(StructModel model, StructMapping mapping) internal StructMapping Mapping { get { return _mapping; } } } - internal class WorkItems + internal sealed class WorkItems { private readonly ArrayList _list = new ArrayList(); @@ -2312,7 +2348,7 @@ internal void RemoveAt(int index) } } - internal class RecursionLimiter + internal sealed class RecursionLimiter { private readonly int _maxDepth; private int _depth; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs index be3bc25ec1f6d..9aaf0a36c814a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemaImporter.cs @@ -8,6 +8,7 @@ namespace System.Xml.Serialization using System.Collections; using System.Collections.Generic; using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Xml.Serialization.Configuration; using System.Collections.Specialized; @@ -19,10 +20,13 @@ namespace System.Xml.Serialization public class XmlSchemaImporter : SchemaImporter { + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSchemaImporter(XmlSchemas schemas) : base(schemas, CodeGenerationOptions.GenerateProperties, new ImportContext()) { } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSchemaImporter(XmlSchemas schemas, CodeIdentifiers? typeIdentifiers) : base(schemas, CodeGenerationOptions.GenerateProperties, new ImportContext(typeIdentifiers, false)) { } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportDerivedTypeMapping(XmlQualifiedName name, Type? baseType) { return ImportDerivedTypeMapping(name, baseType, false); @@ -42,6 +46,7 @@ internal TypeMapping GetDefaultMapping(TypeFlags flags) return mapping; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportDerivedTypeMapping(XmlQualifiedName name, Type? baseType, bool baseTypeCanBeIndirect) { ElementAccessor element = ImportElement(name, typeof(TypeMapping), baseType); @@ -67,16 +72,19 @@ public XmlTypeMapping ImportDerivedTypeMapping(XmlQualifiedName name, Type? base return new XmlTypeMapping(Scope, element); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportSchemaType(XmlQualifiedName typeName) { return ImportSchemaType(typeName, null, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportSchemaType(XmlQualifiedName typeName, Type? baseType) { return ImportSchemaType(typeName, baseType, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportSchemaType(XmlQualifiedName typeName, Type? baseType, bool baseTypeCanBeIndirect) { TypeMapping typeMapping = ImportType(typeName, typeof(TypeMapping), baseType, TypeFlags.CanBeElementValue, true)!; @@ -114,16 +122,19 @@ public XmlTypeMapping ImportSchemaType(XmlQualifiedName typeName, Type? baseType return new XmlTypeMapping(Scope, accessor); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlTypeMapping ImportTypeMapping(XmlQualifiedName name) { return ImportDerivedTypeMapping(name, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(XmlQualifiedName name) { return new XmlMembersMapping(Scope, ImportElement(name, typeof(MembersMapping), null), XmlMappingAccess.Read | XmlMappingAccess.Write); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping? ImportAnyType(XmlQualifiedName typeName, string elementName) { TypeMapping? typeMapping = ImportType(typeName, typeof(MembersMapping), null, TypeFlags.CanBeElementValue, true); @@ -163,11 +174,13 @@ public XmlMembersMapping ImportMembersMapping(XmlQualifiedName name) return members; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(XmlQualifiedName[] names) { return ImportMembersMapping(names, null, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(XmlQualifiedName[] names, Type? baseType, bool baseTypeCanBeIndirect) { CodeIdentifiers memberScope = new CodeIdentifiers(); @@ -196,6 +209,7 @@ public XmlMembersMapping ImportMembersMapping(XmlQualifiedName[] names, Type? ba return new XmlMembersMapping(Scope, element, XmlMappingAccess.Read | XmlMappingAccess.Write); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlMembersMapping ImportMembersMapping(string name, string? ns, SoapSchemaMember[] members) { XmlSchemaComplexType type = new XmlSchemaComplexType(); @@ -219,6 +233,7 @@ public XmlMembersMapping ImportMembersMapping(string name, string? ns, SoapSchem return new XmlMembersMapping(Scope, accessor, XmlMappingAccess.Read | XmlMappingAccess.Write); } + [RequiresUnreferencedCode("calls ImportElement")] private ElementAccessor ImportElement(XmlQualifiedName name, Type desiredMappingType, Type? baseType) { XmlSchemaElement element = FindElement(name); @@ -234,6 +249,7 @@ private ElementAccessor ImportElement(XmlQualifiedName name, Type desiredMapping return accessor; } + [RequiresUnreferencedCode("calls ImportElementType")] private ElementAccessor ImportElement(XmlSchemaElement element, string identifier, Type desiredMappingType, Type? baseType, string? ns, bool topLevelElement) { if (!element.RefName.IsEmpty) @@ -292,6 +308,7 @@ private ElementAccessor ImportElement(XmlSchemaElement element, string identifie return accessor; } + [RequiresUnreferencedCode("calls ImportMembersType")] private TypeMapping ImportElementType(XmlSchemaElement element, string identifier, Type desiredMappingType, Type? baseType, string? ns) { TypeMapping? mapping; @@ -366,6 +383,7 @@ private string GenerateUniqueTypeName(string desiredName, string? ns) return TypeIdentifiers.AddUnique(typeName, typeName); } + [RequiresUnreferencedCode("calls ImportType")] internal override void ImportDerivedTypes(XmlQualifiedName baseName) { foreach (XmlSchema schema in Schemas) @@ -387,6 +405,7 @@ internal override void ImportDerivedTypes(XmlQualifiedName baseName) } } + [RequiresUnreferencedCode("calls FindType")] private TypeMapping? ImportType(XmlQualifiedName name, Type desiredMappingType, Type? baseType, TypeFlags flags, bool addref) { if (name.Name == Soap.UrType && name.Namespace == XmlSchema.Namespace) @@ -414,6 +433,7 @@ internal override void ImportDerivedTypes(XmlQualifiedName baseName) return mapping; } + [RequiresUnreferencedCode("calls ImportMembersType")] private TypeMapping? ImportType(XmlSchemaComplexType type, string? typeNs, string identifier, Type desiredMappingType, Type? baseType, TypeFlags flags) { if (type.Redefined != null) @@ -447,6 +467,7 @@ internal override void ImportDerivedTypes(XmlQualifiedName baseName) throw new ArgumentException(SR.XmlInternalError, nameof(desiredMappingType)); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private MembersMapping ImportMembersType(XmlSchemaType type, string? typeNs, string identifier) { if (!type.DerivedFrom.IsEmpty) throw new InvalidOperationException(SR.XmlMembersDeriveError); @@ -461,6 +482,7 @@ private MembersMapping ImportMembersType(XmlSchemaType type, string? typeNs, str return mappings; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private StructMapping ImportStructType(XmlSchemaType type, string? typeNs, string identifier, Type? baseType, bool arrayLike) { TypeDesc? baseTypeDesc = null; @@ -570,6 +592,7 @@ private bool IsAllGroup(XmlSchemaType type) return (items.Particle != null) && (items.Particle is XmlSchemaAll); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private StructMapping ImportStructDataType(XmlSchemaSimpleType dataType, string? typeNs, string identifier, Type baseType) { identifier = Accessor.UnescapeName(identifier); @@ -591,7 +614,7 @@ private StructMapping ImportStructDataType(XmlSchemaSimpleType dataType, string? return structMapping; } - private class TypeItems + private sealed class TypeItems { internal XmlSchemaObjectCollection Attributes = new XmlSchemaObjectCollection(); internal XmlSchemaAnyAttribute? AnyAttribute; @@ -600,6 +623,7 @@ private class TypeItems internal bool IsUnbounded; } + [RequiresUnreferencedCode("calls FindType")] private MemberMapping[] ImportTypeMembers(XmlSchemaType type, string? typeNs, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, ref bool needExplicitOrder, bool order, bool allowUnboundedElements) { TypeItems items = GetTypeItems(type); @@ -718,6 +742,7 @@ private TypeItems GetTypeItems(XmlSchemaType type) return items; } + [RequiresUnreferencedCode("calls ImportChoiceGroup")] private void ImportGroup(XmlSchemaGroupBase group, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, string? ns, bool mixed, ref bool needExplicitOrder, bool allowDuplicates, bool groupRepeats, bool allowUnboundedElements) { if (group is XmlSchemaChoice) @@ -731,6 +756,7 @@ private void ImportGroup(XmlSchemaGroupBase group, string identifier, CodeIdenti } } + [RequiresUnreferencedCode("calls GetTypeDesc")] private MemberMapping ImportChoiceGroup(XmlSchemaGroupBase group, string identifier, CodeIdentifiers? members, CodeIdentifiers? membersScope, INameScope? elementsScope, string? ns, bool groupRepeats, ref bool needExplicitOrder, bool allowDuplicates) { NameTable choiceElements = new NameTable(); @@ -872,11 +898,13 @@ private bool IsNeedXmlSerializationAttributes(ArrayMapping arrayMapping) return false; } + [RequiresUnreferencedCode("calls GatherGroupChoices")] private bool GatherGroupChoices(XmlSchemaGroup group, NameTable choiceElements, string identifier, string? ns, ref bool needExplicitOrder, bool allowDuplicates) { return GatherGroupChoices(group.Particle, choiceElements, identifier, ns, ref needExplicitOrder, allowDuplicates); } + [RequiresUnreferencedCode("Calls ImportAny")] private bool GatherGroupChoices(XmlSchemaParticle? particle, NameTable choiceElements, string identifier, string? ns, ref bool needExplicitOrder, bool allowDuplicates) { if (particle is XmlSchemaGroupRef) @@ -980,6 +1008,7 @@ private void AddScopeElements(INameScope? scope, ElementAccessor[] elements, ref } } + [RequiresUnreferencedCode("calls ImportChoiceGroup")] private void ImportGroupMembers(XmlSchemaParticle? particle, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, string? ns, bool groupRepeats, ref bool mixed, ref bool needExplicitOrder, bool allowDuplicates, bool allowUnboundedElements) { if (particle is XmlSchemaGroupRef) @@ -1057,6 +1086,7 @@ private XmlSchemaElement[] GetEquivalentElements(XmlSchemaElement element) return (XmlSchemaElement[])equivalentElements.ToArray(typeof(XmlSchemaElement)); } + [RequiresUnreferencedCode("calls ImportChoiceGroup")] private bool ImportSubstitutionGroupMember(XmlSchemaElement element, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, string? ns, bool repeats, ref bool needExplicitOrder, bool allowDuplicates) { XmlSchemaElement[] elements = GetEquivalentElements(element); @@ -1076,6 +1106,7 @@ private bool ImportSubstitutionGroupMember(XmlSchemaElement element, string iden return true; } + [RequiresUnreferencedCode("calls ImportType")] private void ImportTextMember(CodeIdentifiers members, CodeIdentifiers membersScope, XmlQualifiedName? simpleContentType) { TypeMapping? mapping; @@ -1128,6 +1159,7 @@ private void ImportTextMember(CodeIdentifiers members, CodeIdentifiers membersSc members.Add(member.Name, member); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private MemberMapping ImportAnyMember(XmlSchemaAny any, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, string? ns, ref bool mixed, ref bool needExplicitOrder, bool allowDuplicates) { ElementAccessor[] accessors = ImportAny(any, !mixed, ns); @@ -1160,6 +1192,8 @@ private MemberMapping ImportAnyMember(XmlSchemaAny any, string identifier, CodeI } return member; } + + [RequiresUnreferencedCode("calls GetTypeDesc")] private ElementAccessor[] ImportAny(XmlSchemaAny any, bool makeElement, string? targetNamespace) { SpecialMapping mapping = new SpecialMapping(); @@ -1205,6 +1239,7 @@ private ElementAccessor[] ImportAny(XmlSchemaAny any, bool makeElement, string? return new ElementAccessor[] { anyAccessor }; } + [RequiresUnreferencedCode("calls ImportArrayMapping")] private ElementAccessor? ImportArray(XmlSchemaElement element, string identifier, string? ns, bool repeats) { if (repeats) return null; @@ -1223,6 +1258,7 @@ private ElementAccessor[] ImportAny(XmlSchemaAny any, bool makeElement, string? return arrayAccessor; } + [RequiresUnreferencedCode("calls ImportChoiceGroup")] private ArrayMapping? ImportArrayMapping(XmlSchemaType type, string identifier, string? ns, bool repeats) { if (!(type is XmlSchemaComplexType)) return null; @@ -1327,6 +1363,7 @@ private bool IsCyclicReferencedType(XmlSchemaElement element, List ident return false; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private SpecialMapping? ImportAnyMapping(XmlSchemaType? type, string identifier, string? ns, bool repeats) { if (type == null) return null; @@ -1373,6 +1410,7 @@ private bool IsCyclicReferencedType(XmlSchemaElement element, List ident return mapping; } + [RequiresUnreferencedCode("calls ImportSubstitutionGroupMember")] private void ImportElementMember(XmlSchemaElement element, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, INameScope elementsScope, string? ns, bool repeats, ref bool needExplicitOrder, bool allowDuplicates, bool allowUnboundedElements) { repeats = repeats | element.IsMultipleOccurrence; @@ -1429,6 +1467,7 @@ private void ImportElementMember(XmlSchemaElement element, string identifier, Co } } + [RequiresUnreferencedCode("calls ImportAttribute")] private void ImportAttributeMember(XmlSchemaAttribute attribute, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, string? ns) { AttributeAccessor? accessor = ImportAttribute(attribute, identifier, ns, attribute); @@ -1453,6 +1492,7 @@ private void ImportAttributeMember(XmlSchemaAttribute attribute, string identifi } } + [RequiresUnreferencedCode("calls GetTypeDesc")] private void ImportAnyAttributeMember(XmlSchemaAnyAttribute any, CodeIdentifiers members, CodeIdentifiers membersScope) { SpecialMapping mapping = new SpecialMapping(); @@ -1509,6 +1549,7 @@ private bool KeepXmlnsDeclarations(XmlSchemaType type, out string? xmlnsMemberNa return false; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private void ImportXmlnsDeclarationsMember(XmlSchemaType type, CodeIdentifiers members, CodeIdentifiers membersScope) { string? xmlnsMemberName; @@ -1536,6 +1577,7 @@ private void ImportXmlnsDeclarationsMember(XmlSchemaType type, CodeIdentifiers m member.Ignore = true; } + [RequiresUnreferencedCode("calls ImportAnyAttributeMember")] private void ImportAttributeGroupMembers(XmlSchemaAttributeGroup group, string identifier, CodeIdentifiers members, CodeIdentifiers membersScope, string ns) { for (int i = 0; i < group.Attributes.Count; i++) @@ -1550,6 +1592,7 @@ private void ImportAttributeGroupMembers(XmlSchemaAttributeGroup group, string i ImportAnyAttributeMember(group.AnyAttribute, members, membersScope); } + [RequiresUnreferencedCode("calls GetTypeDesc")] private AttributeAccessor ImportSpecialAttribute(XmlQualifiedName name, string identifier) { PrimitiveMapping mapping = new PrimitiveMapping(); @@ -1563,6 +1606,7 @@ private AttributeAccessor ImportSpecialAttribute(XmlQualifiedName name, string i return accessor; } + [RequiresUnreferencedCode("calls ImportSpecialAttribute")] private AttributeAccessor? ImportAttribute(XmlSchemaAttribute attribute, string identifier, string? ns, XmlSchemaAttribute defaultValueProvider) { if (attribute.Use == XmlSchemaUse.Prohibited) return null; @@ -1626,6 +1670,7 @@ private AttributeAccessor ImportSpecialAttribute(XmlQualifiedName name, string i return accessor; } + [RequiresUnreferencedCode("calls ImportStructDataType")] private TypeMapping? ImportDataType(XmlSchemaSimpleType dataType, string? typeNs, string identifier, Type? baseType, TypeFlags flags, bool isList) { if (baseType != null) @@ -1688,6 +1733,7 @@ private AttributeAccessor ImportSpecialAttribute(XmlQualifiedName name, string i return ImportPrimitiveDataType(dataType, flags); } + [RequiresUnreferencedCode("calls FindType")] private TypeMapping? ImportEnumeratedDataType(XmlSchemaSimpleType dataType, string? typeNs, string identifier, TypeFlags flags, bool isList) { TypeMapping? mapping = (TypeMapping?)ImportedMappings[dataType]; @@ -1750,7 +1796,7 @@ private AttributeAccessor ImportSpecialAttribute(XmlQualifiedName name, string i return enumMapping; } - internal class ElementComparer : IComparer + internal sealed class ElementComparer : IComparer { public int Compare(object? o1, object? o2) { @@ -1789,6 +1835,7 @@ private EnumMapping ImportEnumeratedChoice(ElementAccessor[] choice, string? typ return enumMapping; } + [RequiresUnreferencedCode("calls GetDataTypeSource")] private PrimitiveMapping ImportPrimitiveDataType(XmlSchemaSimpleType dataType, TypeFlags flags) { TypeDesc sourceTypeDesc = GetDataTypeSource(dataType, flags); @@ -1855,6 +1902,7 @@ internal static XmlQualifiedName BaseTypeName(XmlSchemaSimpleType dataType) return new XmlQualifiedName("string", XmlSchema.Namespace); } + [RequiresUnreferencedCode("calls FindDataType")] private TypeDesc GetDataTypeSource(XmlSchemaSimpleType dataType, TypeFlags flags) { TypeDesc? typeDesc = null; @@ -1872,6 +1920,7 @@ private TypeDesc GetDataTypeSource(XmlSchemaSimpleType dataType, TypeFlags flags return typeDesc; } + [RequiresUnreferencedCode("calls GetTypeDesc")] private XmlSchemaSimpleType? FindDataType(XmlQualifiedName? name, TypeFlags flags) { if (name == null || name.IsEmpty) @@ -1906,6 +1955,7 @@ private TypeDesc GetDataTypeSource(XmlSchemaSimpleType dataType, TypeFlags flags } } + [RequiresUnreferencedCode("calls GetTypeDesc")] private XmlSchemaType? FindType(XmlQualifiedName? name, TypeFlags flags) { if (name == null || name.IsEmpty) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemas.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemas.cs index 78ea35dc32cbb..fc4804211b704 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemas.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSchemas.cs @@ -343,6 +343,7 @@ public static bool IsDataSet(XmlSchema schema) return false; } + [RequiresUnreferencedCode("calls Merge")] private void Merge(XmlSchema schema) { if (MergedSchemas[schema] != null) @@ -382,6 +383,7 @@ private void AddImport(IList schemas, string? ns) } } + [RequiresUnreferencedCode("Calls MergeFailedMessage")] private void Merge(IList originals, XmlSchema schema) { foreach (XmlSchema s in originals) @@ -593,6 +595,7 @@ internal static XmlQualifiedName GetParentName(XmlSchemaObject item) return item; } + [RequiresUnreferencedCode("Creates XmlSerializer")] private static string Dump(XmlSchemaObject o) { XmlWriterSettings settings = new XmlWriterSettings(); @@ -606,6 +609,8 @@ private static string Dump(XmlSchemaObject o) s.Serialize(xmlWriter, o, ns); return sw.ToString(); } + + [RequiresUnreferencedCode("calls Dump")] private static string MergeFailedMessage(XmlSchemaObject src, XmlSchemaObject dest, string? ns) { string err = SR.Format(SR.XmlSerializableMergeItem, ns, GetSchemaItem(src, ns, null)); @@ -640,6 +645,7 @@ public bool IsCompiled get { return _isCompiled; } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public void Compile(ValidationEventHandler? handler, bool fullCompile) { if (_isCompiled) @@ -779,6 +785,7 @@ private static XmlSchema CreateFakeXsdSchema(string ns, string name) return schema; } + [RequiresUnreferencedCode("calls GenerateSchemaGraph")] internal void SetCache(SchemaObjectCache cache, bool shareTypes) { _shareTypes = shareTypes; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationEventSource.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationEventSource.cs index d9fe51c556258..03fcce6fc8477 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationEventSource.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationEventSource.cs @@ -8,7 +8,7 @@ namespace System.Xml.Serialization [EventSource( Name = "System.Xml.Serialzation.XmlSerialization", LocalizationResources = "FxResources.System.Private.Xml.SR")] - internal class XmlSerializationEventSource : EventSource + internal sealed class XmlSerializationEventSource : EventSource { internal static XmlSerializationEventSource Log = new XmlSerializationEventSource(); @@ -18,7 +18,7 @@ internal void XmlSerializerExpired(string serializerName, string type) WriteEvent(EventIds.XmlSerializerExpired, serializerName, type); } - public class EventIds + public static class EventIds { public const int XmlSerializerExpired = 1; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs index 4874478670347..60b8eb8a40819 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationGeneratedCode.cs @@ -11,6 +11,7 @@ namespace System.Xml.Serialization using System.Reflection; using System.Security; using System.Globalization; + using System.Diagnostics.CodeAnalysis; /// public abstract class XmlSerializationGeneratedCode @@ -39,6 +40,7 @@ internal class XmlSerializationCodeGen private int _references; private readonly Hashtable _generatedMethods = new Hashtable(); + [RequiresUnreferencedCode("Calls GetTypeDesc")] internal XmlSerializationCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) { _writer = writer; @@ -64,8 +66,10 @@ internal XmlSerializationCodeGen(IndentedWriter writer, TypeScope[] scopes, stri internal Hashtable MethodNames { get { return _methodNames; } } internal Hashtable GeneratedMethods { get { return _generatedMethods; } } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal virtual void GenerateMethod(TypeMapping mapping) { } + [RequiresUnreferencedCode("calls GenerateMethod")] internal void GenerateReferencedMethods() { while (_references > 0) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationILGen.cs index 6ff443ee3c6f9..dfee0a19dbdb3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationILGen.cs @@ -35,6 +35,7 @@ internal class XmlSerializationILGen protected TypeBuilder typeBuilder = null!; protected CodeGenerator ilg = null!; + [RequiresUnreferencedCode("Calls GetTypeDesc")] internal XmlSerializationILGen(TypeScope[] scopes, string access, string className) { _scopes = scopes; @@ -108,8 +109,10 @@ internal MethodBuilderInfo GetMethodBuilder(string methodName) System.Diagnostics.Debug.Assert(_methodBuilders.ContainsKey(methodName)); return _methodBuilders[methodName]; } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal virtual void GenerateMethod(TypeMapping mapping) { } + [RequiresUnreferencedCode("calls GenerateMethod")] internal void GenerateReferencedMethods() { while (_references > 0) @@ -147,6 +150,7 @@ private TypeMapping[] EnsureArrayIndex(TypeMapping[]? a, int index) return ReflectionAwareILGen.GetCSharpString(value); } + [RequiresUnreferencedCode("calls LoadMember")] internal FieldBuilder GenerateHashtableGetBegin(string privateName, string publicName, TypeBuilder serializerContractTypeBuilder) { FieldBuilder fieldBuilder = serializerContractTypeBuilder.DefineField( @@ -187,6 +191,7 @@ internal FieldBuilder GenerateHashtableGetBegin(string privateName, string publi return fieldBuilder; } + [RequiresUnreferencedCode("calls LoadMember")] internal void GenerateHashtableGetEnd(FieldBuilder fieldBuilder) { ilg!.Ldarg(0); @@ -208,6 +213,8 @@ internal void GenerateHashtableGetEnd(FieldBuilder fieldBuilder) ilg.EndMethod(); } + + [RequiresUnreferencedCode("calls GenerateHashtableGetBegin")] internal FieldBuilder GeneratePublicMethods(string privateName, string publicName, string[] methods, XmlMapping[] xmlMappings, TypeBuilder serializerContractTypeBuilder) { FieldBuilder fieldBuilder = GenerateHashtableGetBegin(privateName, publicName, serializerContractTypeBuilder); @@ -268,6 +275,7 @@ internal void GenerateSupportedTypes(Type[] types, TypeBuilder serializerContrac ilg.EndMethod(); } + [RequiresUnreferencedCode("Uses CreatedTypes Dictionary")] internal string GenerateBaseSerializer(string baseSerializer, string readerClass, string writerClass, CodeIdentifiers classes) { baseSerializer = CodeIdentifier.MakeValid(baseSerializer); @@ -313,6 +321,7 @@ internal string GenerateBaseSerializer(string baseSerializer, string readerClass return baseSerializer; } + [RequiresUnreferencedCode("uses CreatedTypes dictionary")] internal string GenerateTypedSerializer(string readMethod, string writeMethod, XmlMapping mapping, CodeIdentifiers classes, string baseSerializer, string readerClass, string writerClass) { string serializerName = CodeIdentifier.MakeValid(Accessor.UnescapeName(mapping.Accessor.Mapping!.TypeDesc!.Name)); @@ -409,6 +418,7 @@ internal string GenerateTypedSerializer(string readMethod, string writeMethod, X return typedSerializerType.Name; } + [RequiresUnreferencedCode("calls GetConstructor")] private FieldBuilder GenerateTypedSerializers(Dictionary serializers, TypeBuilder serializerContractTypeBuilder) { string privateName = "typedSerializers"; @@ -435,6 +445,7 @@ private FieldBuilder GenerateTypedSerializers(Dictionary seriali } //GenerateGetSerializer(serializers, xmlMappings); + [RequiresUnreferencedCode("Uses CreatedTypes Dictionary")] private void GenerateGetSerializer(Dictionary serializers, XmlMapping[] xmlMappings, TypeBuilder serializerContractTypeBuilder) { ilg = new CodeGenerator(serializerContractTypeBuilder); @@ -480,6 +491,7 @@ private void GenerateGetSerializer(Dictionary serializers, XmlMa ilg.EndMethod(); } + [RequiresUnreferencedCode("calls GenerateTypedSerializers")] internal void GenerateSerializerContract(string className, XmlMapping[] xmlMappings, Type[] types, string readerType, string[] readMethods, string writerType, string[] writerMethods, Dictionary serializers) { TypeBuilder serializerContractTypeBuilder = CodeGenerator.CreateTypeBuilder( @@ -572,10 +584,14 @@ internal static bool IsWildcard(SpecialMapping mapping) return ((SerializableMapping)mapping).IsAny; return mapping.TypeDesc!.CanBeElementValue; } + + [RequiresUnreferencedCode("calls ILGenLoad")] internal void ILGenLoad(string source) { ILGenLoad(source, null); } + + [RequiresUnreferencedCode("calls LoadMember")] internal void ILGenLoad(string source, Type? type) { if (source.StartsWith("o.@", StringComparison.Ordinal)) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs index e29f7f8d079f3..73e92ab645b96 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs @@ -1557,6 +1557,7 @@ protected void FixupArrayRefs(object fixup) } } + [RequiresUnreferencedCode("calls GetArrayElementType")] private object? ReadArray(string? typeName, string? typeNs) { SoapArrayInfo arrayInfo; @@ -1756,8 +1757,10 @@ protected void FixupArrayRefs(object fixup) return array; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected abstract void InitCallbacks(); + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void ReadReferencedElements() { _r.MoveToContent(); @@ -1772,28 +1775,33 @@ protected void ReadReferencedElements() HandleUnreferencedObjects(); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected object? ReadReferencedElement() { return ReadReferencedElement(null, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected object? ReadReferencedElement(string? name, string? ns) { string? dummy; return ReadReferencingElement(name, ns, out dummy); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected object? ReadReferencingElement(out string? fixupReference) { return ReadReferencingElement(null, null, out fixupReference); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected object? ReadReferencingElement(string? name, string? ns, out string? fixupReference) { return ReadReferencingElement(name, ns, false, out fixupReference); } [MemberNotNull(nameof(_callbacks))] + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected object? ReadReferencingElement(string? name, string? ns, bool elementCanBeType, out string? fixupReference) { object? o = null; @@ -1832,6 +1840,7 @@ protected void ReadReferencedElements() } [MemberNotNull(nameof(_callbacks))] + [RequiresUnreferencedCode("calls InitCallbacks")] internal void EnsureCallbackTables() { if (_callbacks == null) @@ -2033,7 +2042,7 @@ public object CollectionItems /// public delegate object? XmlSerializationReadCallback(); - internal class XmlSerializationReaderCodeGen : XmlSerializationCodeGen + internal sealed class XmlSerializationReaderCodeGen : XmlSerializationCodeGen { private readonly Hashtable _idNames = new Hashtable(); private Hashtable? _enums; @@ -2053,7 +2062,7 @@ internal Hashtable Enums } } - private class CreateCollectionInfo + private sealed class CreateCollectionInfo { private readonly string _name; private readonly TypeDesc _td; @@ -2073,7 +2082,7 @@ internal TypeDesc TypeDesc get { return _td; } } } - private class Member + private sealed class Member { private readonly string _source; private readonly string _arrayName; @@ -2226,10 +2235,12 @@ internal string? ChoiceArraySource } } + [RequiresUnreferencedCode("creates XmlSerializationCodeGen")] internal XmlSerializationReaderCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) : base(writer, scopes, access, className) { } + [RequiresUnreferencedCode("calls WriteReflectionInit")] internal void GenerateBegin() { Writer.Write(Access); @@ -2267,6 +2278,7 @@ internal void GenerateBegin() } } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal override void GenerateMethod(TypeMapping mapping) { if (GeneratedMethods.Contains(mapping)) @@ -2287,6 +2299,7 @@ internal override void GenerateMethod(TypeMapping mapping) } } + [RequiresUnreferencedCode("calls GenerateReferencedMethods")] internal void GenerateEnd(string?[] methods, XmlMapping[] xmlMappings, Type?[]? types) { GenerateReferencedMethods(); 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 5e9de6e59784e..010d537da38b6 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 @@ -15,8 +15,9 @@ namespace System.Xml.Serialization using System.Xml; using System.Xml.Schema; using System.Xml.Extensions; + using System.Diagnostics.CodeAnalysis; - internal class XmlSerializationReaderILGen : XmlSerializationILGen + internal sealed class XmlSerializationReaderILGen : XmlSerializationILGen { private readonly Dictionary _idNames = new Dictionary(); // Mapping name->id_XXXNN field @@ -36,7 +37,7 @@ internal Dictionary Enums } } - private class Member + private sealed class Member { private readonly string _source; private readonly string _arrayName; @@ -181,11 +182,13 @@ internal string? ChoiceArraySource } } + [RequiresUnreferencedCode("Creates XmlSerializationILGen")] internal XmlSerializationReaderILGen(TypeScope[] scopes, string access, string className) : base(scopes, access, className) { } + [RequiresUnreferencedCode("calls WriteReflectionInit")] internal void GenerateBegin() { this.typeBuilder = CodeGenerator.CreateTypeBuilder( @@ -205,6 +208,7 @@ internal void GenerateBegin() } } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal override void GenerateMethod(TypeMapping mapping) { if (!GeneratedMethods.Add(mapping)) @@ -224,6 +228,7 @@ internal override void GenerateMethod(TypeMapping mapping) } } + [RequiresUnreferencedCode("calls GenerateReferencedMethods")] internal void GenerateEnd(string[] methods, XmlMapping[] xmlMappings, Type[] types) { GenerateReferencedMethods(); @@ -266,6 +271,7 @@ internal void GenerateEnd(string[] methods, XmlMapping[] xmlMappings, Type[] typ CreatedTypes.Add(readerType.Name, readerType); } + [RequiresUnreferencedCode("calls GenerateMembersElement")] internal string? GenerateElement(XmlMapping xmlMapping) { if (!xmlMapping.IsReadable) @@ -280,6 +286,7 @@ internal void GenerateEnd(string[] methods, XmlMapping[] xmlMappings, Type[] typ throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping)); } + [RequiresUnreferencedCode("calls LoadMember")] private void WriteIsStartTag(string? name, string? ns) { WriteID(name); @@ -304,6 +311,7 @@ private void WriteIsStartTag(string? name, string? ns) ilg.If(); } + [RequiresUnreferencedCode("XmlSerializationReader methods have RequiresUnreferencedCode")] private void WriteUnknownNode(string func, string node, ElementAccessor? e, bool anyIfs) { if (anyIfs) @@ -350,6 +358,7 @@ private void GenerateInitCallbacksMethod() ilg.EndMethod(); } + [RequiresUnreferencedCode("calls GenerateLiteralMembersElement")] private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping) { return GenerateLiteralMembersElement(xmlMembersMapping); @@ -384,6 +393,7 @@ private string GetChoiceIdentifierSource(MemberMapping mapping, string parent, T return RaCodeGen.GetStringForMember(parent, mapping.ChoiceIdentifier.MemberName, parentTypeDesc); } + [RequiresUnreferencedCode("calls InitializeValueTypes")] private string GenerateLiteralMembersElement(XmlMembersMapping xmlMembersMapping) { ElementAccessor element = xmlMembersMapping.Accessor; @@ -613,6 +623,7 @@ private string GenerateLiteralMembersElement(XmlMembersMapping xmlMembersMapping return methodName; } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] private void InitializeValueTypes(string arrayName, MemberMapping[] mappings) { for (int i = 0; i < mappings.Length; i++) @@ -628,6 +639,7 @@ private void InitializeValueTypes(string arrayName, MemberMapping[] mappings) } } + [RequiresUnreferencedCode("calls WriteMemberElements")] private string GenerateTypeElement(XmlTypeMapping xmlTypeMapping) { ElementAccessor element = xmlTypeMapping.Accessor; @@ -683,6 +695,7 @@ private string NextIdName(string name) return "id" + (++_nextIdNumber).ToString(CultureInfo.InvariantCulture) + "_" + CodeIdentifier.MakeValidInternal(name); } + [RequiresUnreferencedCode("XmlSerializationReader methods have RequiresUnreferencedCode")] private void WritePrimitive(TypeMapping mapping, string source) { System.Diagnostics.Debug.Assert(source == "Reader.ReadElementString()" || source == "Reader.ReadString()" @@ -936,13 +949,14 @@ private void WritePrimitive(TypeMapping mapping, string source) { i++; uniqueName = name + i.ToString(CultureInfo.InvariantCulture); - m = Enums[uniqueName]; + Enums.TryGetValue(uniqueName, out m); } } Enums.Add(uniqueName, mapping); return uniqueName; } + [RequiresUnreferencedCode("calls LoadMember")] private string WriteHashtable(EnumMapping mapping, string typeName, out MethodBuilder? get_TableName) { get_TableName = null; @@ -1020,6 +1034,7 @@ private string WriteHashtable(EnumMapping mapping, string typeName, out MethodBu return propName; } + [RequiresUnreferencedCode("calls WriteHashtable")] private void WriteEnumMethod(EnumMapping mapping) { MethodBuilder? get_TableName = null; @@ -1136,6 +1151,7 @@ private void WriteEnumMethod(EnumMapping mapping) ilg.EndMethod(); } + [RequiresUnreferencedCode("calls WriteQNameEqual")] private void WriteDerivedTypes(StructMapping mapping, bool isTypedReturn, string returnTypeName) { for (StructMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) @@ -1175,6 +1191,7 @@ private void WriteDerivedTypes(StructMapping mapping, bool isTypedReturn, string } } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] private void WriteEnumAndArrayTypes() { foreach (TypeScope scope in Scopes) @@ -1281,6 +1298,7 @@ private void WriteEnumAndArrayTypes() } } + [RequiresUnreferencedCode("calls WriteElement")] private void WriteNullableMethod(NullableMapping nullableMapping) { string? methodName; @@ -1326,11 +1344,13 @@ private void WriteNullableMethod(NullableMapping nullableMapping) ilg.EndMethod(); } + [RequiresUnreferencedCode("calls WriteLiteralStructMethod")] private void WriteStructMethod(StructMapping structMapping) { WriteLiteralStructMethod(structMapping); } + [RequiresUnreferencedCode("calls WriteEnumAndArrayTypes")] private void WriteLiteralStructMethod(StructMapping structMapping) { string? methodName; @@ -1701,6 +1721,7 @@ private void WriteLiteralStructMethod(StructMapping structMapping) ilg.EndMethod(); } + [RequiresUnreferencedCode("calls LoadMember")] private void WriteQNameEqual(string source, string? name, string? ns) { WriteID(name); @@ -1736,10 +1757,12 @@ private void WriteQNameEqual(string source, string? name, string? ns) ilg.MarkLabel(labelEnd); } + [RequiresUnreferencedCode("XmlSerializationReader methods have RequiresUnreferencedCode")] private void WriteXmlNodeEqual(string source, string name, string? ns) { WriteXmlNodeEqual(source, name, ns, true); } + [RequiresUnreferencedCode("XmlSerializationReader methods have RequiresUnreferencedCode")] private void WriteXmlNodeEqual(string source, string name, string? ns, bool doAndIf) { bool isNameNullOrEmpty = string.IsNullOrEmpty(name); @@ -1814,6 +1837,7 @@ private void WriteID(string? name) } } + [RequiresUnreferencedCode("calls WriteSourceEnd")] private void WriteAttributes(Member[] members, Member? anyAttribute, string elseCall, LocalBuilder firstParam) { int count = 0; @@ -2044,6 +2068,7 @@ private void WriteAttributes(Member[] members, Member? anyAttribute, string else ilg.WhileEnd(); } + [RequiresUnreferencedCode("calls WriteSourceEnd")] private void WriteAttribute(Member member) { AttributeAccessor attribute = member.Mapping.Attribute!; @@ -2136,6 +2161,7 @@ private void WriteAttribute(Member member) } } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] private void WriteMemberBegin(Member[] members) { for (int i = 0; i < members.Length; i++) @@ -2242,6 +2268,7 @@ private string ExpectedElements(Member[] members) return ReflectionAwareILGen.GetQuotedCSharpString(qnames); } + [RequiresUnreferencedCode("calls WriteMemberElementsIf")] private void WriteMemberElements(Member[] members, string elementElseString, string elseString, Member? anyElement, Member? anyText) { if (anyText != null) @@ -2277,6 +2304,7 @@ private void WriteMemberElements(Member[] members, string elementElseString, str ilg.EndIf(); } + [RequiresUnreferencedCode("calls WriteText")] private void WriteMemberText(Member anyText, string elseString) { ilg.InitElseIf(); @@ -2328,6 +2356,7 @@ private void WriteMemberText(Member anyText, string elseString) Debug.Assert(anyText != null); } + [RequiresUnreferencedCode("calls WriteSourceEnd")] private void WriteText(Member member) { TextAccessor text = member.Mapping.Text!; @@ -2435,7 +2464,7 @@ private void WriteText(Member member) } } - + [RequiresUnreferencedCode("calls WriteElement")] private void WriteMemberElementsElse(Member? anyElement, string elementElseString) { if (anyElement != null) @@ -2466,6 +2495,8 @@ private bool IsSequence(Member[] members) } return false; } + + [RequiresUnreferencedCode("calls WriteElement")] private void WriteMemberElementsIf(Member[] members, Member? anyElement, string elementElseString) { int count = 0; @@ -2630,12 +2661,13 @@ private string GetArraySource(TypeDesc typeDesc, string arrayName, bool multiRef } } - + [RequiresUnreferencedCode("calls WriteMemberEnd")] private void WriteMemberEnd(Member[] members) { WriteMemberEnd(members, false); } + [RequiresUnreferencedCode("calls WriteSourceEnd")] private void WriteMemberEnd(Member[] members, bool soapRefs) { for (int i = 0; i < members.Length; i++) @@ -2780,10 +2812,12 @@ private void WriteSourceBegin(string source) throw Globals.NotSupported("Unexpected: " + source); } + [RequiresUnreferencedCode("calls WriteSourceEnd")] private void WriteSourceEnd(string source, Type elementType) { WriteSourceEnd(source, elementType, elementType); } + [RequiresUnreferencedCode("string-based IL generation")] private void WriteSourceEnd(string source, Type elementType, Type stackType) { object? variable; @@ -2864,6 +2898,7 @@ private void WriteSourceEnd(string source, Type elementType, Type stackType) throw Globals.NotSupported("Unexpected: " + source); } + [RequiresUnreferencedCode("calls WriteMemberBegin")] private void WriteArray(string source, string? arrayName, ArrayMapping arrayMapping, bool readOnly, bool isNullable, int fixupIndex, int elementIndex) { MethodInfo XmlSerializationReader_ReadNull = typeof(XmlSerializationReader).GetMethod( @@ -2976,6 +3011,7 @@ private void WriteArray(string source, string? arrayName, ArrayMapping arrayMapp ilg.EndIf(); } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] private void WriteElement(string source, string? arrayName, string? choiceSource, ElementAccessor element, ChoiceIdentifierAccessor? choice, string? checkSpecified, bool checkForNull, bool readOnly, int fixupIndex, int elementIndex) { if (checkSpecified != null && checkSpecified.Length > 0) @@ -3285,6 +3321,7 @@ private void WriteElement(string source, string? arrayName, string? choiceSource } } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] private void WriteDerivedSerializable(SerializableMapping head, SerializableMapping? mapping, string source, bool isWrappedAny) { if (mapping == null) @@ -3432,6 +3469,7 @@ private void WriteParamsRead(int length) ilg.Stloc(paramsRead); } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] private void WriteCreateMapping(TypeMapping mapping, string local) { string fullTypeName = mapping.TypeDesc!.CSharpName; @@ -3482,14 +3520,19 @@ private void WriteCatchException(Type exceptionType) ilg.Pop(); } + [RequiresUnreferencedCode("calls WriteArrayLocalDecl")] private void WriteArrayLocalDecl(string typeName, string variableName, string initValue, TypeDesc arrayTypeDesc) { RaCodeGen.WriteArrayLocalDecl(typeName, variableName, new SourceInfo(initValue, initValue, null, arrayTypeDesc.Type, ilg), arrayTypeDesc); } + + [RequiresUnreferencedCode("calls WriteCreateInstance")] private void WriteCreateInstance(string source, bool ctorInaccessible, Type type) { RaCodeGen.WriteCreateInstance(source, ctorInaccessible, type, ilg); } + + [RequiresUnreferencedCode("calls WriteLocalDecl")] private void WriteLocalDecl(string variableName, SourceInfo initValue) { RaCodeGen.WriteLocalDecl(variableName, initValue); @@ -3600,6 +3643,8 @@ private void ILGenElementElseString(string elementElseString) } throw Globals.NotSupported("Unexpected: " + elementElseString); } + + [RequiresUnreferencedCode("calls WriteSourceEnd")] private void ILGenSet(string source, object value) { WriteSourceBegin(source); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs index cd532800f897f..35bda1cbb11f2 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs @@ -174,11 +174,13 @@ protected void WriteXsiType(string name, string? ns) WriteAttribute("type", XmlSchema.InstanceNamespace, GetQualifiedName(name, ns)); } + [RequiresUnreferencedCode("calls GetPrimitiveTypeName")] private XmlQualifiedName GetPrimitiveTypeName(Type type) { return GetPrimitiveTypeName(type, true)!; } + [RequiresUnreferencedCode("calls CreateUnknownTypeException")] private XmlQualifiedName? GetPrimitiveTypeName(Type type, bool throwIfUnknown) { XmlQualifiedName? qname = GetPrimitiveTypeNameInternal(type); @@ -236,6 +238,7 @@ private XmlQualifiedName GetPrimitiveTypeName(Type type) return new XmlQualifiedName(typeName, typeNs); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void WriteTypedPrimitive(string? name, string? ns, object o, bool xsiType) { string? value = null; @@ -770,11 +773,13 @@ private void WriteElement(XmlNode node, string name, string? ns, bool isNullable _w.WriteEndElement(); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected Exception CreateUnknownTypeException(object o) { return CreateUnknownTypeException(o.GetType()); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected Exception CreateUnknownTypeException(Type type) { if (typeof(IXmlSerializable).IsAssignableFrom(type)) return new InvalidOperationException(SR.Format(SR.XmlInvalidSerializable, type.FullName)); @@ -1145,6 +1150,7 @@ protected void AddWriteCallback(Type type, string typeName, string? typeNs, XmlS _typeEntries![type] = entry; } + [RequiresUnreferencedCode("calls GetArrayElementType")] private void WriteArray(string name, string? ns, object o, Type type) { Type elementType = TypeScope.GetArrayElementType(type, null)!; @@ -1269,21 +1275,25 @@ private void WriteArray(string name, string? ns, object o, Type type) } _w.WriteEndElement(); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void WritePotentiallyReferencingElement(string? n, string? ns, object? o) { WritePotentiallyReferencingElement(n, ns, o, null, false, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void WritePotentiallyReferencingElement(string? n, string? ns, object? o, Type? ambientType) { WritePotentiallyReferencingElement(n, ns, o, ambientType, false, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void WritePotentiallyReferencingElement(string n, string? ns, object? o, Type? ambientType, bool suppressReference) { WritePotentiallyReferencingElement(n, ns, o, ambientType, suppressReference, false); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void WritePotentiallyReferencingElement(string? n, string? ns, object? o, Type? ambientType, bool suppressReference, bool isNullable) { if (o == null) @@ -1332,12 +1342,13 @@ protected void WritePotentiallyReferencingElement(string? n, string? ns, object? } } - + [RequiresUnreferencedCode("calls WriteReferencedElement")] private void WriteReferencedElement(object o, Type? ambientType) { WriteReferencedElement(null, null, o, ambientType); } + [RequiresUnreferencedCode("calls WriteArray")] private void WriteReferencedElement(string? name, string? ns, object o, Type? ambientType) { if (name == null) name = string.Empty; @@ -1358,6 +1369,7 @@ private void WriteReferencedElement(string? name, string? ns, object o, Type? am } } + [RequiresUnreferencedCode("calls InitCallbacks")] private TypeEntry? GetTypeEntry(Type t) { if (_typeEntries == null) @@ -1368,8 +1380,10 @@ private void WriteReferencedElement(string? name, string? ns, object o, Type? am return (TypeEntry?)_typeEntries[t]; } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected abstract void InitCallbacks(); + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] protected void WriteReferencedElements() { if (_referencesToWrite == null) return; @@ -1424,7 +1438,7 @@ private string NextPrefix() return _aliasBase + _tempNamespacePrefix; } - internal class TypeEntry + internal sealed class TypeEntry { internal XmlSerializationWriteCallback? callback; internal string? typeNs; @@ -1532,7 +1546,7 @@ internal static void Add(Assembly a) } } - internal class ReflectionAwareCodeGen + internal sealed class ReflectionAwareCodeGen { private const string arrayMemberKey = "0"; // reflectionVariables holds mapping between a reflection entity @@ -1559,6 +1573,7 @@ internal ReflectionAwareCodeGen(IndentedWriter writer) _writer = writer; } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal void WriteReflectionInit(TypeScope scope) { foreach (Type type in scope.Types) @@ -1569,6 +1584,7 @@ internal void WriteReflectionInit(TypeScope scope) } } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] private string WriteTypeInfo(TypeScope scope, TypeDesc typeDesc, Type type) { InitTheFirstTime(); @@ -1643,7 +1659,8 @@ private void InitTheFirstTime() } } - private void WriteMappingInfo(TypeMapping mapping, string typeVariable, Type type) + private void WriteMappingInfo(TypeMapping mapping, string typeVariable, + [DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type) { string typeFullName = mapping.TypeDesc!.CSharpName; if (mapping is StructMapping) @@ -1679,7 +1696,8 @@ private void WriteMappingInfo(TypeMapping mapping, string typeVariable, Type typ } } } - private void WriteCollectionInfo(string typeVariable, TypeDesc typeDesc, Type type) + private void WriteCollectionInfo(string typeVariable, TypeDesc typeDesc, + [DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type) { string typeFullName = CodeIdentifier.GetCSharpName(type); string elementTypeFullName = typeDesc.ArrayElementTypeDesc!.CSharpName; @@ -1720,7 +1738,8 @@ private string WriteAssemblyInfo(Type type) return assemblyVariable; } - private string WriteMemberInfo(Type type, string escapedName, string typeVariable, string memberName) + private string WriteMemberInfo( + [DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type, string escapedName, string typeVariable, string memberName) { MemberInfo[] memberInfos = type.GetMember(memberName); for (int i = 0; i < memberInfos.Length; i++) @@ -1781,7 +1800,8 @@ private string WriteMethodInfo(string escapedName, string typeVariable, string m return methodVariable; } - private string WriteDefaultIndexerInit(Type type, string escapedName, bool collectionUseReflection, bool elementUseReflection) + private string WriteDefaultIndexerInit( + [DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type, string escapedName, bool collectionUseReflection, bool elementUseReflection) { string itemVariable = GenerateVariableName("item", escapedName); PropertyInfo defaultIndexer = TypeScope.GetDefaultIndexer(type, null); @@ -2187,12 +2207,14 @@ public XSArrayInfo({4} propInfo){{ "; } - internal class XmlSerializationWriterCodeGen : XmlSerializationCodeGen + internal sealed class XmlSerializationWriterCodeGen : XmlSerializationCodeGen { + [RequiresUnreferencedCode("creates XmlSerializationCodeGen")] internal XmlSerializationWriterCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) : base(writer, scopes, access, className) { } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal void GenerateBegin() { Writer.Write(Access); @@ -2232,6 +2254,7 @@ internal void GenerateBegin() } } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal override void GenerateMethod(TypeMapping mapping) { if (GeneratedMethods.Contains(mapping)) @@ -2247,6 +2270,8 @@ internal override void GenerateMethod(TypeMapping mapping) WriteEnumMethod((EnumMapping)mapping); } } + + [RequiresUnreferencedCode("calls GenerateReferencedMethods")] internal void GenerateEnd() { GenerateReferencedMethods(); @@ -2255,6 +2280,7 @@ internal void GenerateEnd() Writer.WriteLine("}"); } + [RequiresUnreferencedCode("calls GenerateMembersElement")] internal string? GenerateElement(XmlMapping xmlMapping) { if (!xmlMapping.IsWriteable) @@ -2303,6 +2329,7 @@ private void GenerateInitCallbacksMethod() Writer.WriteLine("}"); } + [RequiresUnreferencedCode("calls WriteCheckDefault")] private void WriteQualifiedNameElement(string name, string? ns, object? defaultValue, string source, bool nullable, bool IsSoap, TypeMapping mapping) { bool hasDefault = defaultValue != null && defaultValue != DBNull.Value; @@ -2385,6 +2412,7 @@ private void WritePrimitiveValue(TypeDesc typeDesc, string source, bool isElemen } } + [RequiresUnreferencedCode("calls WriteCheckDefault")] private void WritePrimitive(string method, string name, string? ns, object? defaultValue, string source, TypeMapping mapping, bool writeXsiType, bool isElement, bool isNullable) { TypeDesc typeDesc = mapping.TypeDesc!; @@ -2540,6 +2568,7 @@ private void WriteEmptyTag(string name, string? ns) WriteTag("WriteEmptyTag", name, ns); } + [RequiresUnreferencedCode("calls WriteMember")] private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping) { ElementAccessor element = xmlMembersMapping.Accessor; @@ -2749,6 +2778,7 @@ private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping) return methodName; } + [RequiresUnreferencedCode("calls WriteMember")] private string GenerateTypeElement(XmlTypeMapping xmlTypeMapping) { ElementAccessor element = xmlTypeMapping.Accessor; @@ -2943,6 +2973,7 @@ private void WriteDerivedTypes(StructMapping mapping) } } + [RequiresUnreferencedCode("calls WriteMember")] private void WriteEnumAndArrayTypes() { foreach (TypeScope scope in Scopes) @@ -3011,6 +3042,7 @@ private void WriteEnumAndArrayTypes() } } + [RequiresUnreferencedCode("calls WriteMember")] private void WriteStructMethod(StructMapping mapping) { if (mapping.IsSoap && mapping.TypeDesc!.IsRoot) return; @@ -3222,6 +3254,7 @@ private bool CanOptimizeWriteListSequence(TypeDesc? listElementTypeDesc) return (listElementTypeDesc != null && listElementTypeDesc != QnameTypeDesc); } + [RequiresUnreferencedCode("calls WriteAttribute")] private void WriteMember(string source, AttributeAccessor attribute, TypeDesc memberTypeDesc, string parent) { if (memberTypeDesc.IsAbstract) return; @@ -3393,6 +3426,7 @@ private void WriteMember(string source, AttributeAccessor attribute, TypeDesc me } } + [RequiresUnreferencedCode("calls WritePrimitive")] private void WriteAttribute(string source, AttributeAccessor attribute, string parent) { if (attribute.Mapping is SpecialMapping) @@ -3417,6 +3451,7 @@ private void WriteAttribute(string source, AttributeAccessor attribute, string p } } + [RequiresUnreferencedCode("calls WriteElements")] private void WriteMember(string source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc memberTypeDesc, bool writeAccessors) { if (memberTypeDesc.IsArrayLike && @@ -3426,7 +3461,7 @@ private void WriteMember(string source, string? choiceSource, ElementAccessor[] WriteElements(source, choiceSource, elements, text, choice, "a", writeAccessors, memberTypeDesc.IsNullable); } - + [RequiresUnreferencedCode("calls WriteArrayItems")] private void WriteArray(string source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc) { if (elements.Length == 0 && text == null) return; @@ -3466,6 +3501,7 @@ private void WriteArray(string source, string? choiceSource, ElementAccessor[] e Writer.WriteLine("}"); } + [RequiresUnreferencedCode("calls WriteElements")] private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc, string arrayName, string? choiceName) { TypeDesc arrayElementTypeDesc = arrayTypeDesc.ArrayElementTypeDesc!; @@ -3567,11 +3603,13 @@ private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, Cho Writer.WriteLine("}"); } + [RequiresUnreferencedCode("calls WriteElements")] private void WriteElements(string source, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, string arrayName, bool writeAccessors, bool isNullable) { WriteElements(source, null, elements, text, choice, arrayName, writeAccessors, isNullable); } + [RequiresUnreferencedCode("calls WriteElement")] private void WriteElements(string source, string? enumSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, string arrayName, bool writeAccessors, bool isNullable) { if (elements.Length == 0 && text == null) return; @@ -3837,6 +3875,7 @@ private void WriteText(string source, TextAccessor text) } } + [RequiresUnreferencedCode("calls WritePrimitive")] private void WriteElement(string source, ElementAccessor element, string arrayName, bool writeAccessor) { string name = writeAccessor ? element.Name : element.Mapping!.TypeName!; @@ -4125,6 +4164,7 @@ private void WriteElementCall(string func, Type cast, string source, string name Writer.WriteLine(");"); } + [RequiresUnreferencedCode("calls GetType")] private void WriteCheckDefault(TypeMapping mapping, string source, object value, bool isNullable) { Writer.Write("if ("); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs index 48c39db732c44..46eda857550cb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriterILGen.cs @@ -15,13 +15,15 @@ namespace System.Xml.Serialization { - internal class XmlSerializationWriterILGen : XmlSerializationILGen + internal sealed class XmlSerializationWriterILGen : XmlSerializationILGen { + [RequiresUnreferencedCode("creates XmlSerializationILGen")] internal XmlSerializationWriterILGen(TypeScope[] scopes, string access, string className) : base(scopes, access, className) { } + [RequiresUnreferencedCode("calls WriteReflectionInit")] internal void GenerateBegin() { this.typeBuilder = CodeGenerator.CreateTypeBuilder( @@ -44,6 +46,7 @@ internal void GenerateBegin() } } + [RequiresUnreferencedCode("calls WriteStructMethod")] internal override void GenerateMethod(TypeMapping mapping) { if (!GeneratedMethods.Add(mapping)) @@ -58,6 +61,8 @@ internal override void GenerateMethod(TypeMapping mapping) WriteEnumMethod((EnumMapping)mapping); } } + + [RequiresUnreferencedCode("calls GenerateReferencedMethods")] internal Type GenerateEnd() { GenerateReferencedMethods(); @@ -67,6 +72,7 @@ internal Type GenerateEnd() return this.typeBuilder.CreateTypeInfo()!.AsType(); } + [RequiresUnreferencedCode("calls GenerateTypeElement")] internal string? GenerateElement(XmlMapping xmlMapping) { if (!xmlMapping.IsWriteable) @@ -89,6 +95,7 @@ private void GenerateInitCallbacksMethod() ilg.EndMethod(); } + [RequiresUnreferencedCode("calls Load")] private void WriteQualifiedNameElement(string name, string? ns, object? defaultValue, SourceInfo source, bool nullable, TypeMapping mapping) { bool hasDefault = defaultValue != null && defaultValue != DBNull.Value; @@ -121,6 +128,7 @@ private void WriteQualifiedNameElement(string name, string? ns, object? defaultV } } + [RequiresUnreferencedCode("calls Load")] private void WriteEnumValue(EnumMapping mapping, SourceInfo source, out Type returnType) { string? methodName = ReferenceMapping(mapping); @@ -143,6 +151,7 @@ private void WriteEnumValue(EnumMapping mapping, SourceInfo source, out Type ret returnType = typeof(string); } + [RequiresUnreferencedCode("calls Load")] private void WritePrimitiveValue(TypeDesc typeDesc, SourceInfo source, out Type returnType) { if (typeDesc == StringTypeDesc || typeDesc.FormatterName == "String") @@ -191,6 +200,7 @@ private void WritePrimitiveValue(TypeDesc typeDesc, SourceInfo source, out Type } } + [RequiresUnreferencedCode("Calls WriteCheckDefault")] private void WritePrimitive(string method, string name, string? ns, object? defaultValue, SourceInfo source, TypeMapping mapping, bool writeXsiType, bool isElement, bool isNullable) { TypeDesc typeDesc = mapping.TypeDesc!; @@ -277,6 +287,7 @@ private void WritePrimitive(string method, string name, string? ns, object? defa } } + [RequiresUnreferencedCode("XmlSerializationWriter methods have RequiresUnreferencedCode")] private void WriteTag(string methodName, string name, string? ns) { MethodInfo XmlSerializationWriter_Method = typeof(XmlSerializationWriter).GetMethod( @@ -290,6 +301,7 @@ private void WriteTag(string methodName, string name, string? ns) ilg.Call(XmlSerializationWriter_Method); } + [RequiresUnreferencedCode("XmlSerializationWriter methods have RequiresUnreferencedCode")] private void WriteTag(string methodName, string name, string? ns, bool writePrefixed) { MethodInfo XmlSerializationWriter_Method = typeof(XmlSerializationWriter).GetMethod( @@ -305,6 +317,7 @@ private void WriteTag(string methodName, string name, string? ns, bool writePref ilg.Call(XmlSerializationWriter_Method); } + [RequiresUnreferencedCode("calls WriteTag")] private void WriteStartElement(string name, string? ns, bool writePrefixed) { WriteTag("WriteStartElement", name, ns, writePrefixed); @@ -334,16 +347,19 @@ private void WriteEndElement(string source) ilg.Call(XmlSerializationWriter_WriteEndElement); } + [RequiresUnreferencedCode("calls WriteTag")] private void WriteLiteralNullTag(string name, string? ns) { WriteTag("WriteNullTagLiteral", name, ns); } + [RequiresUnreferencedCode("calls WriteTag")] private void WriteEmptyTag(string name, string? ns) { WriteTag("WriteEmptyTag", name, ns); } + [RequiresUnreferencedCode("calls WriteMember")] private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping) { ElementAccessor element = xmlMembersMapping.Accessor; @@ -537,6 +553,7 @@ private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping) return methodName; } + [RequiresUnreferencedCode("calls WriteMember")] private string GenerateTypeElement(XmlTypeMapping xmlTypeMapping) { ElementAccessor element = xmlTypeMapping.Accessor; @@ -776,6 +793,7 @@ private void WriteDerivedTypes(StructMapping mapping) } } + [RequiresUnreferencedCode("calls WriteMember")] private void WriteEnumAndArrayTypes() { foreach (TypeScope scope in Scopes) @@ -904,6 +922,7 @@ private void WriteEnumAndArrayTypes() } } + [RequiresUnreferencedCode("Calls WriteMember")] private void WriteStructMethod(StructMapping mapping) { string? methodName; @@ -1147,6 +1166,7 @@ private bool CanOptimizeWriteListSequence(TypeDesc? listElementTypeDesc) return (listElementTypeDesc != null && listElementTypeDesc != QnameTypeDesc); } + [RequiresUnreferencedCode("calls WriteAttribute")] private void WriteMember(SourceInfo source, AttributeAccessor attribute, TypeDesc memberTypeDesc, string parent) { if (memberTypeDesc.IsAbstract) return; @@ -1348,6 +1368,7 @@ private void WriteMember(SourceInfo source, AttributeAccessor attribute, TypeDes } } + [RequiresUnreferencedCode("calls WritePrimitive")] private void WriteAttribute(SourceInfo source, AttributeAccessor attribute, string parent) { if (attribute.Mapping is SpecialMapping) @@ -1378,7 +1399,10 @@ private void WriteAttribute(SourceInfo source, AttributeAccessor attribute, stri } } - private static object? GetConvertedDefaultValue(Type? targetType, object? rawDefaultValue) + private static object? GetConvertedDefaultValue( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + Type? targetType, + object? rawDefaultValue) { if (targetType == null) { @@ -1394,6 +1418,7 @@ private void WriteAttribute(SourceInfo source, AttributeAccessor attribute, stri return convertedDefaultValue; } + [RequiresUnreferencedCode("Calls WriteElements")] private void WriteMember(SourceInfo source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc memberTypeDesc, bool writeAccessors) { if (memberTypeDesc.IsArrayLike && @@ -1404,7 +1429,7 @@ private void WriteMember(SourceInfo source, string? choiceSource, ElementAccesso WriteElements(source, choiceSource, elements, text, choice, "a" + memberTypeDesc.Name, writeAccessors, memberTypeDesc.IsNullable); } - + [RequiresUnreferencedCode("calls WriteArrayItems")] private void WriteArray(SourceInfo source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc) { if (elements.Length == 0 && text == null) return; @@ -1463,6 +1488,7 @@ private void WriteArray(SourceInfo source, string? choiceSource, ElementAccessor } } + [RequiresUnreferencedCode("calls WriteElements")] private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc, string arrayName, string? choiceName) { TypeDesc arrayElementTypeDesc = arrayTypeDesc.ArrayElementTypeDesc!; @@ -1552,6 +1578,7 @@ private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, Cho } } + [RequiresUnreferencedCode("Calls WriteElement")] private void WriteElements(SourceInfo source, string? enumSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, string arrayName, bool writeAccessors, bool isNullable) { if (elements.Length == 0 && text == null) return; @@ -1834,6 +1861,7 @@ private void WriteElements(SourceInfo source, string? enumSource, ElementAccesso } } + [RequiresUnreferencedCode("calls Load")] private void WriteText(SourceInfo source, TextAccessor text) { if (text.Mapping is PrimitiveMapping) @@ -1883,6 +1911,7 @@ private void WriteText(SourceInfo source, TextAccessor text) } } + [RequiresUnreferencedCode("Calls WriteCheckDefault")] private void WriteElement(SourceInfo source, ElementAccessor element, string arrayName, bool writeAccessor) { string name = writeAccessor ? element.Name : element.Mapping!.TypeName!; @@ -2053,6 +2082,7 @@ private void WriteElement(SourceInfo source, ElementAccessor element, string arr } } + [RequiresUnreferencedCode("XmlSerializationWriter methods have RequiresUnreferencedCode")] private void WriteElementCall(string func, Type cast, SourceInfo source, string? name, string? ns, bool isNullable, bool isAny) { MethodInfo XmlSerializationWriter_func = typeof(XmlSerializationWriter).GetMethod( @@ -2069,6 +2099,7 @@ private void WriteElementCall(string func, Type cast, SourceInfo source, string? ilg.Call(XmlSerializationWriter_func); } + [RequiresUnreferencedCode("Dynamically looks for '!=' operator on 'value' parameter")] private void WriteCheckDefault(SourceInfo source, object value, bool isNullable) { if (value is string && ((string)value).Length == 0) @@ -2140,6 +2171,7 @@ private void WriteCheckDefault(SourceInfo source, object value, bool isNullable) } } + [RequiresUnreferencedCode("calls Load")] private void WriteChoiceTypeCheck(SourceInfo source, string fullTypeName, ChoiceIdentifierAccessor choice, string enumName, TypeDesc typeDesc) { Label labelFalse = ilg.DefineLabel(); @@ -2170,6 +2202,7 @@ private void WriteChoiceTypeCheck(SourceInfo source, string fullTypeName, Choice ilg.EndIf(); } + [RequiresUnreferencedCode("calls WriteLiteralNullTag")] private void WriteNullCheckBegin(string source, ElementAccessor element) { LocalBuilder local = ilg.GetLocal(source); @@ -2181,7 +2214,7 @@ private void WriteNullCheckBegin(string source, ElementAccessor element) ilg.Else(); } - + [RequiresUnreferencedCode("calls ILGenLoad")] private void WriteNamespaces(string source) { MethodInfo XmlSerializationWriter_WriteNamespaceDeclarations = typeof(XmlSerializationWriter).GetMethod( @@ -2205,12 +2238,13 @@ private int FindXmlnsIndex(MemberMapping[] members) return -1; } - + [RequiresUnreferencedCode("calls WriteLocalDecl")] private void WriteLocalDecl(string variableName, string initValue, Type type) { RaCodeGen.WriteLocalDecl(variableName, new SourceInfo(initValue, initValue, null, type, ilg)); } + [RequiresUnreferencedCode("calls WriteArrayLocalDecl")] private void WriteArrayLocalDecl(string typeName, string variableName, SourceInfo initValue, TypeDesc arrayTypeDesc) { RaCodeGen.WriteArrayLocalDecl(typeName, variableName, initValue, arrayTypeDesc); @@ -2219,6 +2253,8 @@ private void WriteTypeCompare(string variable, Type type) { RaCodeGen.WriteTypeCompare(variable, type, ilg); } + + [RequiresUnreferencedCode("calls WriteInstanceOf")] private void WriteInstanceOf(SourceInfo source, Type type) { RaCodeGen.WriteInstanceOf(source, type, ilg); @@ -2277,7 +2313,7 @@ private string FindChoiceEnumValue(ElementAccessor element, EnumMapping choiceMa } } - internal class ReflectionAwareILGen + internal sealed class ReflectionAwareILGen { // reflectionVariables holds mapping between a reflection entity // referenced in the generated code (such as TypeInfo, @@ -2297,6 +2333,7 @@ internal class ReflectionAwareILGen // ---------------------------------------------------------------------------------- internal ReflectionAwareILGen() { } + [RequiresUnreferencedCode("calls GetTypeDesc")] internal void WriteReflectionInit(TypeScope scope) { foreach (Type type in scope.Types) @@ -2345,6 +2382,8 @@ internal string GetStringForMethod(string obj, string typeFullName, string membe { return obj + "." + memberName + "("; } + + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] internal void ILGenForCreateInstance(CodeGenerator ilg, Type type, bool ctorInaccessible, bool cast) { if (!ctorInaccessible) @@ -2368,6 +2407,7 @@ internal void ILGenForCreateInstance(CodeGenerator ilg, Type type, bool ctorInac ILGenForCreateInstance(ilg, type, cast ? type : null, ctorInaccessible); } + [RequiresUnreferencedCode("calls GetType")] internal void ILGenForCreateInstance(CodeGenerator ilg, Type type, Type? cast, bool nonPublic) { // Special case DBNull @@ -2476,6 +2516,7 @@ internal void ILGenForCreateInstance(CodeGenerator ilg, Type type, Type? cast, b ilg.ConvertValue(Activator_CreateInstance.ReturnType, cast); } + [RequiresUnreferencedCode("calls LoadMember")] internal void WriteLocalDecl(string variableName, SourceInfo initValue) { Type localType = initValue.Type!; @@ -2517,12 +2558,15 @@ internal void WriteLocalDecl(string variableName, SourceInfo initValue) } } + [RequiresUnreferencedCode("calls ILGenForCreateInstance")] internal void WriteCreateInstance(string source, bool ctorInaccessible, Type type, CodeGenerator ilg) { LocalBuilder sLoc = ilg.DeclareOrGetLocal(type, source); ILGenForCreateInstance(ilg, type, ctorInaccessible, ctorInaccessible); ilg.Stloc(sLoc); } + + [RequiresUnreferencedCode("calls Load")] internal void WriteInstanceOf(SourceInfo source, Type type, CodeGenerator ilg) { { @@ -2534,6 +2578,7 @@ internal void WriteInstanceOf(SourceInfo source, Type type, CodeGenerator ilg) } } + [RequiresUnreferencedCode("calls Load")] internal void WriteArrayLocalDecl(string typeName, string variableName, SourceInfo initValue, TypeDesc arrayTypeDesc) { Debug.Assert(typeName == arrayTypeDesc.CSharpName || typeName == arrayTypeDesc.CSharpName + "[]"); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index 67b49b7b39a8d..2e6ecc3d16d6d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -150,29 +150,38 @@ private static XmlSerializerNamespaces DefaultNamespaces } } + // Trimmer warning messages + internal const string TrimSerializationWarning = "Members from serialized types may be trimmed if not referenced directly"; + private const string TrimDeserializationWarning = "Members from deserialized types may be trimmed if not referenced directly"; + private static readonly Dictionary> s_xmlSerializerTable = new Dictionary>(); protected XmlSerializer() { } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type, XmlAttributeOverrides? overrides, Type[]? extraTypes, XmlRootAttribute? root, string? defaultNamespace) : this(type, overrides, extraTypes, root, defaultNamespace, null) { } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type, XmlRootAttribute? root) : this(type, null, Type.EmptyTypes, root, null, null) { } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type, Type[]? extraTypes) : this(type, null, extraTypes, null, null, null) { } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type, XmlAttributeOverrides? overrides) : this(type, overrides, Type.EmptyTypes, null, null, null) { } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(XmlTypeMapping xmlTypeMapping) { if (xmlTypeMapping == null) @@ -182,10 +191,12 @@ public XmlSerializer(XmlTypeMapping xmlTypeMapping) _mapping = xmlTypeMapping; } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type) : this(type, (string?)null) { } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type, string? defaultNamespace) { if (type == null) @@ -243,6 +254,7 @@ public XmlSerializer(Type type, string? defaultNamespace) } } + [RequiresUnreferencedCode(TrimSerializationWarning)] public XmlSerializer(Type type, XmlAttributeOverrides? overrides, Type[]? extraTypes, XmlRootAttribute? root, string? defaultNamespace, string? location) { if (type == null) @@ -254,6 +266,7 @@ public XmlSerializer(Type type, XmlAttributeOverrides? overrides, Type[]? extraT _tempAssembly = GenerateTempAssembly(_mapping, type, defaultNamespace, location); } + [RequiresUnreferencedCode("calls ImportTypeMapping")] private XmlTypeMapping GenerateXmlTypeMapping(Type type, XmlAttributeOverrides? overrides, Type[]? extraTypes, XmlRootAttribute? root, string? defaultNamespace) { XmlReflectionImporter importer = new XmlReflectionImporter(overrides, defaultNamespace); @@ -266,16 +279,19 @@ private XmlTypeMapping GenerateXmlTypeMapping(Type type, XmlAttributeOverrides? return importer.ImportTypeMapping(type, root, defaultNamespace); } + [RequiresUnreferencedCode("creates TempAssembly")] internal static TempAssembly? GenerateTempAssembly(XmlMapping xmlMapping) { return GenerateTempAssembly(xmlMapping, null, null); } + [RequiresUnreferencedCode("creates TempAssembly")] internal static TempAssembly? GenerateTempAssembly(XmlMapping xmlMapping, Type? type, string? defaultNamespace) { return GenerateTempAssembly(xmlMapping, type, defaultNamespace, null); } + [RequiresUnreferencedCode("creates TempAssembly")] internal static TempAssembly? GenerateTempAssembly(XmlMapping xmlMapping, Type? type, string? defaultNamespace, string? location) { if (xmlMapping == null) @@ -292,11 +308,13 @@ private XmlTypeMapping GenerateXmlTypeMapping(Type type, XmlAttributeOverrides? return new TempAssembly(new XmlMapping[] { xmlMapping }, new Type?[] { type }, defaultNamespace, location); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(TextWriter textWriter, object? o) { Serialize(textWriter, o, null); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(TextWriter textWriter, object? o, XmlSerializerNamespaces? namespaces) { XmlTextWriter xmlWriter = new XmlTextWriter(textWriter); @@ -305,11 +323,13 @@ public void Serialize(TextWriter textWriter, object? o, XmlSerializerNamespaces? Serialize(xmlWriter, o, namespaces); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(Stream stream, object? o) { Serialize(stream, o, null); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(Stream stream, object? o, XmlSerializerNamespaces? namespaces) { XmlTextWriter xmlWriter = new XmlTextWriter(stream, null); @@ -318,21 +338,25 @@ public void Serialize(Stream stream, object? o, XmlSerializerNamespaces? namespa Serialize(xmlWriter, o, namespaces); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(XmlWriter xmlWriter, object? o) { Serialize(xmlWriter, o, null); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? namespaces) { Serialize(xmlWriter, o, namespaces, null); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? namespaces, string? encodingStyle) { Serialize(xmlWriter, o, namespaces, encodingStyle, null); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public void Serialize(XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? namespaces, string? encodingStyle, string? id) { try @@ -375,6 +399,7 @@ public void Serialize(XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? n xmlWriter.Flush(); } + [RequiresUnreferencedCode("calls GetMapping")] private void SerializeUsingReflection(XmlWriter xmlWriter, object? o, XmlSerializerNamespaces? namespaces, string? encodingStyle, string? id) { XmlMapping mapping = GetMapping(); @@ -382,6 +407,7 @@ private void SerializeUsingReflection(XmlWriter xmlWriter, object? o, XmlSeriali writer.WriteObject(o); } + [RequiresUnreferencedCode("calls GenerateXmlTypeMapping")] private XmlMapping GetMapping() { if (_mapping == null || !_mapping.GenerateSerializer) @@ -392,6 +418,7 @@ private XmlMapping GetMapping() return _mapping; } + [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(Stream stream) { XmlTextReader xmlReader = new XmlTextReader(stream); @@ -401,6 +428,7 @@ private XmlMapping GetMapping() return Deserialize(xmlReader, null); } + [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(TextReader textReader) { XmlTextReader xmlReader = new XmlTextReader(textReader); @@ -410,21 +438,25 @@ private XmlMapping GetMapping() return Deserialize(xmlReader, null); } + [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(XmlReader xmlReader) { return Deserialize(xmlReader, null); } + [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(XmlReader xmlReader, XmlDeserializationEvents events) { return Deserialize(xmlReader, null, events); } + [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(XmlReader xmlReader, string? encodingStyle) { return Deserialize(xmlReader, encodingStyle, _events); } + [RequiresUnreferencedCode(TrimDeserializationWarning)] public object? Deserialize(XmlReader xmlReader, string? encodingStyle, XmlDeserializationEvents events) { events.sender = this; @@ -477,6 +509,7 @@ private XmlMapping GetMapping() } } + [RequiresUnreferencedCode("calls GetMapping")] private object? DeserializeUsingReflection(XmlReader xmlReader, string? encodingStyle, XmlDeserializationEvents events) { XmlMapping mapping = GetMapping(); @@ -507,11 +540,13 @@ public virtual bool CanDeserialize(XmlReader xmlReader) } } + [RequiresUnreferencedCode(TrimSerializationWarning)] public static XmlSerializer[] FromMappings(XmlMapping[]? mappings) { return FromMappings(mappings, (Type?)null); } + [RequiresUnreferencedCode(TrimSerializationWarning)] public static XmlSerializer[] FromMappings(XmlMapping[]? mappings, Type? type) { if (mappings == null || mappings.Length == 0) return Array.Empty(); @@ -593,6 +628,7 @@ private static XmlSerializer[] GetReflectionBasedSerializers(XmlMapping[] mappin return serializers; } + [RequiresUnreferencedCode("calls GenerateSerializerToStream")] internal static bool GenerateSerializer(Type[]? types, XmlMapping[] mappings, Stream stream) { if (types == null || types.Length == 0) @@ -631,6 +667,7 @@ internal static bool GenerateSerializer(Type[]? types, XmlMapping[] mappings, St return TempAssembly.GenerateSerializerToStream(mappings, types, null, assembly, new Hashtable(), stream); } + [RequiresUnreferencedCode("calls Contract")] private static XmlSerializer[] GetSerializersFromCache(XmlMapping[] mappings, Type type) { XmlSerializer?[] serializers = new XmlSerializer?[mappings.Length]; @@ -683,6 +720,7 @@ private static XmlSerializer[] GetSerializersFromCache(XmlMapping[] mappings, Ty return serializers!; } + [RequiresUnreferencedCode(TrimSerializationWarning)] public static XmlSerializer[] FromTypes(Type[]? types) { if (types == null) @@ -944,7 +982,7 @@ private void SerializePrimitive(XmlWriter xmlWriter, object? o, XmlSerializerNam return o; } - private class XmlSerializerMappingKey + private sealed class XmlSerializerMappingKey { public XmlMapping Mapping; public XmlSerializerMappingKey(XmlMapping mapping) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs index 746698cc20964..301f3c48ef028 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializerFactory.cs @@ -14,44 +14,53 @@ namespace System.Xml.Serialization using System.Xml.Serialization.Configuration; using System.Diagnostics; using System.Xml.Serialization; + using System.Diagnostics.CodeAnalysis; public class XmlSerializerFactory { + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type, XmlAttributeOverrides? overrides, Type[]? extraTypes, XmlRootAttribute? root, string? defaultNamespace) { return CreateSerializer(type, overrides, extraTypes, root, defaultNamespace, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type, XmlRootAttribute? root) { return CreateSerializer(type, null, Type.EmptyTypes, root, null, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type, Type[]? extraTypes) { return CreateSerializer(type, null, extraTypes, null, null, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type, XmlAttributeOverrides? overrides) { return CreateSerializer(type, overrides, Type.EmptyTypes, null, null, null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(XmlTypeMapping xmlTypeMapping) { return new XmlSerializer(xmlTypeMapping); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type) { return CreateSerializer(type, (string?)null); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type, string? defaultNamespace) { return new XmlSerializer(type, defaultNamespace); } + [RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)] public XmlSerializer CreateSerializer(Type type, XmlAttributeOverrides? overrides, Type[]? extraTypes, XmlRootAttribute? root, string? defaultNamespace, string? location) { return new XmlSerializer(type, overrides, extraTypes, root, defaultNamespace, location); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs index e60231d22144a..4b5c236af79b9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Xmlcustomformatter.cs @@ -17,7 +17,7 @@ namespace System.Xml.Serialization /// /// The class provides a set of static methods for converting /// primitive type values to and from their XML string representations. - internal class XmlCustomFormatter + internal static class XmlCustomFormatter { private static DateTimeSerializationSection.DateTimeSerializationMode s_mode; @@ -32,7 +32,6 @@ private static DateTimeSerializationSection.DateTimeSerializationMode Mode return s_mode; } } - private XmlCustomFormatter() { } [return: NotNullIfNotNull("value")] internal static string? FromDefaultValue(object? value, string formatter) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs index 365707c2619c0..a1dbe3ea07de7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/indentedWriter.cs @@ -8,7 +8,7 @@ namespace System.Xml.Serialization /// /// This class will write to a stream and manage indentation. /// - internal class IndentedWriter + internal sealed class IndentedWriter { private readonly TextWriter _writer; private bool _needIndent; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/CompiledXPathExpr.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/CompiledXPathExpr.cs index fb5390ef2de4b..59de59eaf33f6 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/CompiledXPathExpr.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/CompiledXPathExpr.cs @@ -109,7 +109,7 @@ public override void SetContext(IXmlNamespaceResolver? nsResolver) public override XPathResultType ReturnType { get { return _query.StaticType; } } - private class UndefinedXsltContext : XsltContext + private sealed class UndefinedXsltContext : XsltContext { private readonly IXmlNamespaceResolver _nsResolver; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/DescendantQuery.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/DescendantQuery.cs index 3f2ee1b3fa78b..70913a34b88e8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/DescendantQuery.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/DescendantQuery.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class DescendantQuery : DescendantBaseQuery + internal sealed class DescendantQuery : DescendantBaseQuery { private XPathNodeIterator? _nodeIterator; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Filter.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Filter.cs index 11ecec19c49c7..228833a49cb31 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Filter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Filter.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class Filter : AstNode + internal sealed class Filter : AstNode { private readonly AstNode _input; private readonly AstNode _condition; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Function.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Function.cs index 4b8157850ea2e..579ee685be867 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Function.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Function.cs @@ -6,7 +6,7 @@ namespace MS.Internal.Xml.XPath { - internal class Function : AstNode + internal sealed class Function : AstNode { public enum FunctionType { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Group.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Group.cs index de0171981253f..b19cc55695aff 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Group.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Group.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class Group : AstNode + internal sealed class Group : AstNode { private readonly AstNode _groupNode; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/IteratorFilter.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/IteratorFilter.cs index 9123f14e32796..e1572e34ae1ca 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/IteratorFilter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/IteratorFilter.cs @@ -6,7 +6,7 @@ namespace MS.Internal.Xml.XPath { - internal class IteratorFilter : XPathNodeIterator + internal sealed class IteratorFilter : XPathNodeIterator { private readonly XPathNodeIterator _innerIterator; private readonly string _name; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operand.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operand.cs index ae309945f4158..f3bfbdaff6f4a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operand.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operand.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class Operand : AstNode + internal sealed class Operand : AstNode { private readonly XPathResultType _type; private readonly object _val; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operator.cs index 6dd69c8e611ca..f6ed8c1fec969 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Operator.cs @@ -6,7 +6,7 @@ namespace MS.Internal.Xml.XPath { - internal class Operator : AstNode + internal sealed class Operator : AstNode { public enum Op { // order is aligned with XPathOperator diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/PreSiblingQuery.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/PreSiblingQuery.cs index 6db2488019be5..311dc6895a923 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/PreSiblingQuery.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/PreSiblingQuery.cs @@ -20,10 +20,10 @@ namespace MS.Internal.Xml.XPath // --- if false, we hold with row #I and apply this algorithm starting for row #I+1 // --- when we done with #I+1 we continue with row #I - internal class PreSiblingQuery : CacheAxisQuery + internal sealed class PreSiblingQuery : CacheAxisQuery { public PreSiblingQuery(Query qyInput, string name, string prefix, XPathNodeType typeTest) : base(qyInput, name, prefix, typeTest) { } - protected PreSiblingQuery(PreSiblingQuery other) : base(other) { } + private PreSiblingQuery(PreSiblingQuery other) : base(other) { } private static bool NotVisited(XPathNavigator nav, List parentStk) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Root.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Root.cs index 505bc9e2bb3a4..44e1ad17fdf68 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Root.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Root.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class Root : AstNode + internal sealed class Root : AstNode { public Root() { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Variable.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Variable.cs index 45eb4bcf71548..0ea67b9483978 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Variable.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/Variable.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class Variable : AstNode + internal sealed class Variable : AstNode { private readonly string _localname; private readonly string _prefix; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathAncestorIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathAncestorIterator.cs index d5b07d571e986..d27040a15b678 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathAncestorIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathAncestorIterator.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class XPathAncestorIterator : XPathAxisIterator + internal sealed class XPathAncestorIterator : XPathAxisIterator { public XPathAncestorIterator(XPathNavigator nav, XPathNodeType type, bool matchSelf) : base(nav, type, matchSelf) { } public XPathAncestorIterator(XPathNavigator nav, string name, string namespaceURI, bool matchSelf) : base(nav, name, namespaceURI, matchSelf) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathChildIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathChildIterator.cs index 1db05129cec2f..8c2c5bcca23da 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathChildIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathChildIterator.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class XPathChildIterator : XPathAxisIterator + internal sealed class XPathChildIterator : XPathAxisIterator { public XPathChildIterator(XPathNavigator nav, XPathNodeType type) : base(nav, type, /*matchSelf:*/false) { } public XPathChildIterator(XPathNavigator nav, string name, string namespaceURI) : base(nav, name, namespaceURI, /*matchSelf:*/false) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathDescendantIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathDescendantIterator.cs index 65a0e9b17972b..aa1bfe0277634 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathDescendantIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathDescendantIterator.cs @@ -5,7 +5,7 @@ namespace MS.Internal.Xml.XPath { - internal class XPathDescendantIterator : XPathAxisIterator + internal sealed class XPathDescendantIterator : XPathAxisIterator { private int _level; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathMultyIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathMultyIterator.cs index 69c80c3d7da96..e35ec3f253fa9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathMultyIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathMultyIterator.cs @@ -10,11 +10,11 @@ namespace MS.Internal.Xml.XPath using System.Globalization; using System.Collections; - internal class XPathMultyIterator : ResetableIterator + internal sealed class XPathMultyIterator : ResetableIterator { - protected ResetableIterator[] arr; - protected int firstNotEmpty; - protected int position; + private ResetableIterator[] arr; + private int firstNotEmpty; + private int position; public XPathMultyIterator(ArrayList inputArray) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathParser.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathParser.cs index 5c08a2b078d43..9f1c1f1a6ea8b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathParser.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathParser.cs @@ -9,7 +9,7 @@ namespace MS.Internal.Xml.XPath { - internal class XPathParser + internal sealed class XPathParser { private readonly XPathScanner _scanner; @@ -797,7 +797,7 @@ private void CheckNodeSet(XPathResultType t) private static readonly XPathResultType[] s_temparray8 = { XPathResultType.Boolean }; private static readonly XPathResultType[] s_temparray9 = { XPathResultType.Number }; - private class ParamInfo + private sealed class ParamInfo { private readonly Function.FunctionType _ftype; private readonly int _minargs; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSelectionIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSelectionIterator.cs index e710cf3556fef..ab2d689a977e5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSelectionIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSelectionIterator.cs @@ -8,7 +8,7 @@ namespace MS.Internal.Xml.XPath // We need this wrapper object to: // 1. Calculate position // 2. Protect internal query.Current from user who may call MoveNext(). - internal class XPathSelectionIterator : ResetableIterator + internal sealed class XPathSelectionIterator : ResetableIterator { private XPathNavigator _nav; private readonly Query _query; @@ -20,7 +20,7 @@ internal XPathSelectionIterator(XPathNavigator nav, Query query) _query = query; } - protected XPathSelectionIterator(XPathSelectionIterator it) + private XPathSelectionIterator(XPathSelectionIterator it) { _nav = it._nav.Clone(); _query = (Query)it._query.Clone(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSingletonIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSingletonIterator.cs index 67cdd88036038..9c62162089b4a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSingletonIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/Internal/XPathSingletonIterator.cs @@ -6,7 +6,7 @@ namespace MS.Internal.Xml.XPath { - internal class XPathSingletonIterator : ResetableIterator + internal sealed class XPathSingletonIterator : ResetableIterator { private readonly XPathNavigator _nav; private int _position; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs index f7c86f7f9b723..6568055ff2d6a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs @@ -1116,7 +1116,7 @@ private XmlReader GetValidatingReader(XmlReader reader, XmlSchemaSet schemas, Va return XmlReader.Create(reader, readerSettings); } - private class CheckValidityHelper + private sealed class CheckValidityHelper { private bool _isValid; private readonly ValidationEventHandler _nextEventHandler; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorKeyComparer.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorKeyComparer.cs index 2e4a259a8d415..703aca1d6e1e2 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorKeyComparer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorKeyComparer.cs @@ -6,7 +6,7 @@ namespace System.Xml.XPath { - internal class XPathNavigatorKeyComparer : IEqualityComparer + internal sealed class XPathNavigatorKeyComparer : IEqualityComparer { bool IEqualityComparer.Equals(object? obj1, object? obj2) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorReader.cs index 94c1dce35728a..2b9a0eb0a81e9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigatorReader.cs @@ -1081,7 +1081,7 @@ private void SetEOF() } #if NAVREADER_SUPPORTSLINEINFO - internal class XPathNavigatorReaderWithLI : XPathNavigatorReader, System.Xml.IXmlLineInfo { + internal sealed class XPathNavigatorReaderWithLI : XPathNavigatorReader, System.Xml.IXmlLineInfo { internal XPathNavigatorReaderWithLI( XPathNavigator navToRead, IXmlLineInfo xli, IXmlSchemaInfo? xsi ) : base( navToRead, xli, xsi ) { } @@ -1095,7 +1095,7 @@ internal XPathNavigatorReaderWithLI( XPathNavigator navToRead, IXmlLineInfo xli, public virtual int LinePosition { get { return IsReading ? this.lineInfo.LinePosition : 0; } } } - internal class XPathNavigatorReaderWithLIAndSI : XPathNavigatorReaderWithLI, System.Xml.IXmlLineInfo, System.Xml.Schema.IXmlSchemaInfo { + internal sealed class XPathNavigatorReaderWithLIAndSI : XPathNavigatorReaderWithLI, System.Xml.IXmlLineInfo, System.Xml.Schema.IXmlSchemaInfo { internal XPathNavigatorReaderWithLIAndSI( XPathNavigator navToRead, IXmlLineInfo xli, IXmlSchemaInfo xsi ) : base( navToRead, xli, xsi ) { } @@ -1114,7 +1114,7 @@ internal XPathNavigatorReaderWithLIAndSI( XPathNavigator navToRead, IXmlLineInfo } #endif - internal class XPathNavigatorReaderWithSI : XPathNavigatorReader, System.Xml.Schema.IXmlSchemaInfo + internal sealed class XPathNavigatorReaderWithSI : XPathNavigatorReader, System.Xml.Schema.IXmlSchemaInfo { internal XPathNavigatorReaderWithSI(XPathNavigator navToRead, IXmlLineInfo? xli, IXmlSchemaInfo xsi) : base(navToRead, xli, xsi) @@ -1126,13 +1126,13 @@ internal XPathNavigatorReaderWithSI(XPathNavigator navToRead, IXmlLineInfo? xli, // IXmlSchemaInfo //----------------------------------------------- - public virtual XmlSchemaValidity Validity { get { return IsReading ? this.schemaInfo!.Validity : XmlSchemaValidity.NotKnown; } } + public XmlSchemaValidity Validity { get { return IsReading ? this.schemaInfo!.Validity : XmlSchemaValidity.NotKnown; } } public override bool IsDefault { get { return IsReading ? this.schemaInfo!.IsDefault : false; } } - public virtual bool IsNil { get { return IsReading ? this.schemaInfo!.IsNil : false; } } - public virtual XmlSchemaSimpleType? MemberType { get { return IsReading ? this.schemaInfo!.MemberType : null; } } - public virtual XmlSchemaType? SchemaType { get { return IsReading ? this.schemaInfo!.SchemaType : null; } } - public virtual XmlSchemaElement? SchemaElement { get { return IsReading ? this.schemaInfo!.SchemaElement : null; } } - public virtual XmlSchemaAttribute? SchemaAttribute { get { return IsReading ? this.schemaInfo!.SchemaAttribute : null; } } + public bool IsNil { get { return IsReading ? this.schemaInfo!.IsNil : false; } } + public XmlSchemaSimpleType? MemberType { get { return IsReading ? this.schemaInfo!.MemberType : null; } } + public XmlSchemaType? SchemaType { get { return IsReading ? this.schemaInfo!.SchemaType : null; } } + public XmlSchemaElement? SchemaElement { get { return IsReading ? this.schemaInfo!.SchemaElement : null; } } + public XmlSchemaAttribute? SchemaAttribute { get { return IsReading ? this.schemaInfo!.SchemaAttribute : null; } } } /// @@ -1140,7 +1140,7 @@ internal XPathNavigatorReaderWithSI(XPathNavigator navToRead, IXmlLineInfo? xli, /// Only one XmlEmptyNavigator exists per AppDomain (Singleton). That's why the constructor is private. /// Use the Singleton property to get the EmptyNavigator. /// - internal class XmlEmptyNavigator : XPathNavigator + internal sealed class XmlEmptyNavigator : XPathNavigator { private static volatile XmlEmptyNavigator? s_singleton; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNodeIterator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNodeIterator.cs index b8750782c6207..4bd27407e99b1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNodeIterator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNodeIterator.cs @@ -40,7 +40,7 @@ public virtual IEnumerator GetEnumerator() /// /// Implementation of a resetable enumerator that is linked to the XPathNodeIterator used to create it. /// - private class Enumerator : IEnumerator + private sealed class Enumerator : IEnumerator { private readonly XPathNodeIterator _original; // Keep original XPathNodeIterator in case Reset() is called private XPathNodeIterator? _current; @@ -51,7 +51,7 @@ public Enumerator(XPathNodeIterator original) _original = original.Clone(); } - public virtual object Current + public object Current { get { @@ -72,7 +72,7 @@ public virtual object Current } } - public virtual bool MoveNext() + public bool MoveNext() { // Delegate to XPathNodeIterator if (!_iterationStarted) @@ -91,7 +91,7 @@ public virtual bool MoveNext() return true; } - public virtual void Reset() + public void Reset() { _iterationStarted = false; } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManager.cs index 2e35677cf389a..9ceef6da61f9c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManager.cs @@ -7,7 +7,7 @@ namespace System.Xml { - internal partial class XmlDownloadManager + internal sealed partial class XmlDownloadManager { internal Stream GetStream(Uri uri, ICredentials? credentials, IWebProxy? proxy) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManagerAsync.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManagerAsync.cs index 35886f6aad519..1e5efb99d35d7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManagerAsync.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlDownloadManagerAsync.cs @@ -9,7 +9,7 @@ namespace System.Xml { - internal partial class XmlDownloadManager + internal sealed partial class XmlDownloadManager { internal Task GetStreamAsync(Uri uri, ICredentials? credentials, IWebProxy? proxy) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlEncoding.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlEncoding.cs index 5a2c74afa0e76..57742531e80aa 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlEncoding.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlEncoding.cs @@ -7,7 +7,7 @@ namespace System.Xml { - internal class UTF16Decoder : System.Text.Decoder + internal sealed class UTF16Decoder : System.Text.Decoder { private readonly bool _bigEndian; private int _lastByte; @@ -161,7 +161,7 @@ public override void Convert(byte[] bytes, int byteIndex, int byteCount, char[] } } - internal class SafeAsciiDecoder : Decoder + internal sealed class SafeAsciiDecoder : Decoder { public SafeAsciiDecoder() { @@ -544,7 +544,7 @@ internal void Ucs4ToUTF16(uint code, char[] chars, int charIndex) } } - internal class Ucs4Decoder4321 : Ucs4Decoder + internal sealed class Ucs4Decoder4321 : Ucs4Decoder { internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { @@ -585,7 +585,7 @@ internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, c } } - internal class Ucs4Decoder1234 : Ucs4Decoder + internal sealed class Ucs4Decoder1234 : Ucs4Decoder { internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { @@ -627,7 +627,7 @@ internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, c } - internal class Ucs4Decoder2143 : Ucs4Decoder + internal sealed class Ucs4Decoder2143 : Ucs4Decoder { internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { @@ -669,7 +669,7 @@ internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, c } - internal class Ucs4Decoder3412 : Ucs4Decoder + internal sealed class Ucs4Decoder3412 : Ucs4Decoder { internal override int GetFullChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs b/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs index ea93f83dfffbb..cae8ad8549695 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XmlNullResolver.cs @@ -5,7 +5,7 @@ namespace System.Xml { - internal class XmlNullResolver : XmlResolver + internal sealed class XmlNullResolver : XmlResolver { public static readonly XmlNullResolver Singleton = new XmlNullResolver(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs index 65ca16a8928ad..e02699f4abe80 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/GenerateHelper.cs @@ -24,7 +24,7 @@ namespace System.Xml.Xsl.IlGen /// /// List of all XmlIL runtime constructors. /// - internal class XmlILStorageMethods + internal sealed class XmlILStorageMethods { // Aggregates public readonly MethodInfo? AggAvg; @@ -303,7 +303,11 @@ internal static class XmlILMethods public static readonly MethodInfo GetDataSource = typeof(XmlQueryContext).GetMethod("GetDataSource")!; public static readonly MethodInfo GetDefaultDataSource = typeof(XmlQueryContext).GetMethod("get_DefaultDataSource")!; public static readonly MethodInfo GetParam = typeof(XmlQueryContext).GetMethod("GetParameter")!; - public static readonly MethodInfo InvokeXsltLate = typeof(XmlQueryContext).GetMethod("InvokeXsltLateBoundFunction")!; + public static readonly MethodInfo InvokeXsltLate = GetInvokeXsltLateBoundFunction(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since this code path " + + "will only be emitting IL that will later be called by Transform() method which is already annotated as RequiresUnreferencedCode")] + private static MethodInfo GetInvokeXsltLateBoundFunction() => typeof(XmlQueryContext).GetMethod("InvokeXsltLateBoundFunction")!; // XmlILIndex public static readonly MethodInfo IndexAdd = typeof(XmlILIndex).GetMethod("Add")!; @@ -467,7 +471,7 @@ internal enum GenerateNameType /// /// Contains helper methods used during the code generation phase. /// - internal class GenerateHelper + internal sealed class GenerateHelper { private MethodBase? _methInfo; private ILGenerator? _ilgen; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs index 46bb78a63e092..cc7b1fcc8394c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/IteratorDescriptor.cs @@ -227,7 +227,7 @@ public Type ItemStorageType /// /// A data class to hold information for a "Current" StorageLocation. /// - internal class CurrentContext + internal sealed class CurrentContext { public CurrentContext(LocalBuilder local, MethodInfo currentMethod) { @@ -243,7 +243,7 @@ public CurrentContext(LocalBuilder local, MethodInfo currentMethod) /// Iterators are joined together, are nested within each other, and reference each other. This internal class /// contains detailed information about iteration next labels, caching, iterator item location, etc. /// - internal class IteratorDescriptor + internal sealed class IteratorDescriptor { private GenerateHelper _helper; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs index 4c3965fac095f..dc3bd0d9a387c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/OptimizerPatterns.cs @@ -52,7 +52,7 @@ internal enum OptimizerPatternArgument /// As the Qil graph is traversed, patterns are identified. Subtrees that match these patterns are /// annotated with this class, which identifies the matching patterns and their arguments. /// - internal class OptimizerPatterns : IQilAnnotation + internal sealed class OptimizerPatterns : IQilAnnotation { private static readonly int s_patternCount = Enum.GetValues(typeof(OptimizerPatternName)).Length; @@ -260,7 +260,7 @@ public bool MatchesPattern(OptimizerPatternName pattern) /// /// Return name of this annotation. /// - public virtual string Name + public string Name { get { return "Patterns"; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs index c9e25cdad922b..12931f66a90f1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/StaticDataManager.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Xml.Xsl.Qil; using System.Xml.Xsl.Runtime; @@ -14,7 +15,7 @@ namespace System.Xml.Xsl.IlGen /// This internal class maintains a list of unique values. Each unique value is assigned a unique ID, which can /// be used to quickly access the value, since it corresponds to the value's position in the list. /// - internal class UniqueList where T : notnull + internal sealed class UniqueList where T : notnull { private readonly Dictionary _lookup = new Dictionary(); private readonly List _list = new List(); @@ -58,7 +59,7 @@ public T[] ToArray() /// 3. All Xml types that will be used at run-time /// 4. All global variables and parameters /// - internal class StaticDataManager + internal sealed class StaticDataManager { private UniqueList? _uniqueNames; private UniqueList? _uniqueFilters; @@ -172,7 +173,7 @@ public string[]? GlobalNames /// Add early bound information to a list that is used by this query. Return the index of /// the early bound information in the list. /// - public int DeclareEarlyBound(string namespaceUri, Type ebType) + public int DeclareEarlyBound(string namespaceUri, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type ebType) { if (_earlyInfo == null) _earlyInfo = new UniqueList(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILAnnotation.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILAnnotation.cs index e1580e23a70f9..59183e0606309 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILAnnotation.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILAnnotation.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.IlGen /// /// Several annotations are created and attached to Qil nodes during the optimization and code generation phase. /// - internal class XmlILAnnotation : ListBase + internal sealed class XmlILAnnotation : ListBase { private readonly object? _annPrev; private MethodInfo? _funcMethod; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILConstructAnalyzer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILConstructAnalyzer.cs index 730054b9eb521..8e37d3eefc078 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILConstructAnalyzer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILConstructAnalyzer.cs @@ -47,7 +47,7 @@ internal enum XmlILConstructMethod /// /// Every node is annotated with information about how it will be constructed by ILGen. /// - internal class XmlILConstructInfo : IQilAnnotation + internal sealed class XmlILConstructInfo : IQilAnnotation { private readonly QilNodeType _nodeType; private PossibleXmlStates _xstatesInitial, _xstatesFinal, _xstatesBeginLoop, _xstatesEndLoop; @@ -373,7 +373,7 @@ public ArrayList CallersInfo /// /// Return name of this annotation. /// - public virtual string Name + public string Name { get { return "ConstructInfo"; } } @@ -789,7 +789,7 @@ private bool MaybeContent(XmlQueryType typ) /// Scans the content of an ElementCtor and tries to minimize the number of well-formed checks that will have /// to be made at runtime when constructing content. /// - internal class XmlILElementAnalyzer : XmlILStateAnalyzer + internal sealed class XmlILElementAnalyzer : XmlILStateAnalyzer { private readonly NameTable _attrNames = new NameTable(); private readonly ArrayList _dupAttrs = new ArrayList(); @@ -942,7 +942,7 @@ private void CheckAttributeNamespaceConstruct(XmlQueryType typ) /// Scans constructed content, looking for redundant namespace declarations. If any are found, then they are marked /// and removed later. /// - internal class XmlILNamespaceAnalyzer + internal sealed class XmlILNamespaceAnalyzer { private readonly XmlNamespaceManager _nsmgr = new XmlNamespaceManager(new NameTable()); private bool _addInScopeNmsp; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILModule.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILModule.cs index 9d193487a007d..006865b50b4c0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILModule.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILModule.cs @@ -20,7 +20,7 @@ internal enum XmlILMethodAttributes Raw = 2, // Raw method which should not add an implicit first argument of type XmlQueryRuntime } - internal class XmlILModule + internal sealed class XmlILModule { private static long s_assemblyId; // Unique identifier used to ensure that assembly names are unique within AppDomain private static readonly ModuleBuilder s_LREModule = CreateLREModule(); // Module used to emit dynamic lightweight-reflection-emit (LRE) methods diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs index e02dc3d989261..1c7ea589ad30f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlILOptimizerVisitor.cs @@ -11,7 +11,7 @@ namespace System.Xml.Xsl.IlGen { - internal class XmlILOptimizerVisitor : QilPatternVisitor + internal sealed class XmlILOptimizerVisitor : QilPatternVisitor { private static readonly QilPatterns s_patternsNoOpt = CreatePatternsNoOpt(); @@ -152,7 +152,7 @@ protected override QilNode VisitReference(QilNode oldNode) /// /// Strongly-typed AllowReplace. /// - protected bool AllowReplace(XmlILOptimization pattern, QilNode original) + private bool AllowReplace(XmlILOptimization pattern, QilNode original) { return base.AllowReplace((int)pattern, original); } @@ -160,7 +160,7 @@ protected bool AllowReplace(XmlILOptimization pattern, QilNode original) /// /// Strongly-typed Replace. /// - protected QilNode Replace(XmlILOptimization pattern, QilNode original, QilNode replacement) + private QilNode Replace(XmlILOptimization pattern, QilNode original, QilNode replacement) { return base.Replace((int)pattern, original, replacement); } @@ -4964,10 +4964,10 @@ private bool this[XmlILOptimization ann] get { return Patterns.IsSet((int)ann); } } - private class NodeCounter : QilVisitor + private sealed class NodeCounter : QilVisitor { - protected QilNode? target; - protected int cnt; + private QilNode? target; + private int cnt; /// /// Returns number of occurrences of "target" node within the subtree of "expr". @@ -5059,7 +5059,7 @@ protected virtual bool OnFound(QilNode expr) } } - private class PositionOfFinder : NodeFinder + private sealed class PositionOfFinder : NodeFinder { /// /// Return true only if parent node type is PositionOf. @@ -5070,10 +5070,10 @@ protected override bool OnFound(QilNode expr) } } - private class EqualityIndexVisitor : QilVisitor + private sealed class EqualityIndexVisitor : QilVisitor { - protected bool result; - protected QilNode? ctxt, key; + private bool result; + private QilNode? ctxt, key; /// /// Returns true if the subtree of "expr" meets the following requirements: @@ -5128,7 +5128,7 @@ private bool DependsOn(QilNode expr, QilNode target) /// /// Returns true if there is no PositionOf operator within the "expr" subtree that references iterator "iter". /// - protected bool NonPositional(QilNode expr, QilNode iter) + private bool NonPositional(QilNode expr, QilNode iter) { return !(new PositionOfFinder().Find(expr, iter)); } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlTypeHelper.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlTypeHelper.cs index a7ead3818658e..fc4253c4ebc40 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlTypeHelper.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlTypeHelper.cs @@ -16,13 +16,8 @@ namespace System.Xml.Xsl.IlGen /// /// Static QilExpression type helper methods. /// - internal class XmlILTypeHelper + internal static class XmlILTypeHelper { - // Not creatable - private XmlILTypeHelper() - { - } - /// /// Return the default Clr data type that will be used to store instances of the QilNode's type. /// diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs index 10443b3ef29ea..7c5a4c78db480 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/IlGen/XmlIlVisitor.cs @@ -27,7 +27,7 @@ namespace System.Xml.Xsl.IlGen /// the stack or a local variable by an iterator. The iterator is passive, and will just wait for /// a caller to pull the data and/or instruct the iterator to enumerate the next value. /// - internal class XmlILVisitor : QilVisitor + internal sealed class XmlILVisitor : QilVisitor { private QilExpression _qil = null!; private GenerateHelper _helper = null!; @@ -35,6 +35,9 @@ internal class XmlILVisitor : QilVisitor private IteratorDescriptor? _iterNested; private int _indexId; + [RequiresUnreferencedCode("Method VisitXsltInvokeEarlyBound will require code that cannot be statically analyzed.")] + public XmlILVisitor() + { } //----------------------------------------------- // Entry @@ -3597,6 +3600,9 @@ protected override QilNode VisitXsltInvokeLateBound(QilInvokeLateBound ndInvoke) /// /// Generate code for QilNodeType.XsltInvokeEarlyBound. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since we added " + + "the attribute to this subclass' constructor. This allows us to not have to annotate the whole QilNode hirerarchy.")] protected override QilNode VisitXsltInvokeEarlyBound(QilInvokeEarlyBound ndInvoke) { QilName ndName = ndInvoke.Name; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilChoice.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilChoice.cs index daff0690f6d2b..d7fc6a4dd1ecf 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilChoice.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilChoice.cs @@ -15,7 +15,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilChoice : QilBinary + internal sealed class QilChoice : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilDataSource.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilDataSource.cs index 99ad88b9a45a2..006f770405342 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilDataSource.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilDataSource.cs @@ -14,7 +14,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilDataSource : QilBinary + internal sealed class QilDataSource : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilExpression.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilExpression.cs index 5b743985b8b0e..2c0261566199a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilExpression.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilExpression.cs @@ -15,7 +15,7 @@ namespace System.Xml.Xsl.Qil /// designed for optimization, composition with virtual XML views, translation into other forms, /// and direct execution. See also the QIL specification.

/// - internal class QilExpression : QilNode + internal sealed class QilExpression : QilNode { private QilFactory _factory; private QilNode _isDebug; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilFunction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilFunction.cs index b6dd25a1c8da8..38f119df85d57 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilFunction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilFunction.cs @@ -20,7 +20,7 @@ namespace System.Xml.Xsl.Qil /// override the types after setting the function's definition (for example, an XQuery /// might define a function's return type to be wider than its definition would imply.) /// - internal class QilFunction : QilReference + internal sealed class QilFunction : QilReference { private QilNode _arguments, _definition, _sideEffects; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvoke.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvoke.cs index 0530d062e2c9a..e4daf017b2585 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvoke.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvoke.cs @@ -11,7 +11,7 @@ namespace System.Xml.Xsl.Qil /// /// A function invocation node which represents a call to a Qil functions. /// - internal class QilInvoke : QilBinary + internal sealed class QilInvoke : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeEarlyBound.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeEarlyBound.cs index dcc36df218df2..e72f2d41b639e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeEarlyBound.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeEarlyBound.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.Qil /// /// A function invocation node which represents a call to an early bound Clr function. /// - internal class QilInvokeEarlyBound : QilTernary + internal sealed class QilInvokeEarlyBound : QilTernary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeLateBound.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeLateBound.cs index dfa095333190e..0947ec7fcf28b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeLateBound.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilInvokeLateBound.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.Qil /// /// A function invocation node which represents a call to an late bound function. /// - internal class QilInvokeLateBound : QilBinary + internal sealed class QilInvokeLateBound : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilList.cs index d105ee90bf45d..d22519c964ed1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilList.cs @@ -13,7 +13,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilList : QilNode + internal sealed class QilList : QilNode { private int _count; private QilNode[] _members; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilLoop.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilLoop.cs index b3f2f29898563..6fad4d5c14c38 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilLoop.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilLoop.cs @@ -15,7 +15,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilLoop : QilBinary + internal sealed class QilLoop : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilName.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilName.cs index f39da3bacf554..f7810e1a524c5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilName.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilName.cs @@ -13,7 +13,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilName : QilLiteral + internal sealed class QilName : QilLiteral { private string _local; private string _uri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilParameter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilParameter.cs index 258dcb1067545..b8b78e3fdcb2c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilParameter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilParameter.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.Qil /// /// View over a Qil parameter node. /// - internal class QilParameter : QilIterator + internal sealed class QilParameter : QilIterator { private QilNode? _name; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilSortKey.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilSortKey.cs index 073e8a291fd67..98c64f5ec784d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilSortKey.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilSortKey.cs @@ -12,7 +12,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilSortKey : QilBinary + internal sealed class QilSortKey : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilStrConcat.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilStrConcat.cs index 8dafd52113c44..40ab77c95c29d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilStrConcat.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilStrConcat.cs @@ -12,7 +12,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilStrConcat : QilBinary + internal sealed class QilStrConcat : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTargetType.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTargetType.cs index adcc09db04b7d..2d0931291f624 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTargetType.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTargetType.cs @@ -14,7 +14,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilTargetType : QilBinary + internal sealed class QilTargetType : QilBinary { //----------------------------------------------- // Constructor diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTypeChecker.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTypeChecker.cs index 2b187a375aa06..592c9a3f1173b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTypeChecker.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilTypeChecker.cs @@ -18,7 +18,7 @@ namespace System.Xml.Xsl.Qil /// 1. Infer XmlQueryType of Qil nodes (constant, from arguments, etc) /// 2. Validate the arguments of Qil nodes if DEBUG is defined ///
- internal class QilTypeChecker + internal sealed class QilTypeChecker { public QilTypeChecker() { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilUnary.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilUnary.cs index 112cf60971487..6d0cabff115b0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilUnary.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilUnary.cs @@ -13,7 +13,7 @@ namespace System.Xml.Xsl.Qil /// /// Don't construct QIL nodes directly; instead, use the QilFactory. /// - internal class QilUnary : QilNode + internal sealed class QilUnary : QilNode { private QilNode _child; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilValidationVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilValidationVisitor.cs index 41c3a5765b1ba..3a4d4805b44eb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilValidationVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilValidationVisitor.cs @@ -6,7 +6,7 @@ namespace System.Xml.Xsl.Qil { - /// A internal class that validates QilExpression graphs. + /// An internal class that validates QilExpression graphs. /// /// QilValidationVisitor traverses the QilExpression graph once to enforce the following constraints: /// @@ -24,7 +24,7 @@ namespace System.Xml.Xsl.Qil /// to print correctly.)

///
/// - internal class QilValidationVisitor : QilScopedVisitor + internal sealed class QilValidationVisitor : QilScopedVisitor { #if DEBUG private readonly QilTypeChecker _typeCheck = new QilTypeChecker(); @@ -41,12 +41,12 @@ public static void Validate(QilNode node) new QilValidationVisitor().VisitAssumeReference(node); } - protected QilValidationVisitor() { } + private QilValidationVisitor() { } #if DEBUG - protected Hashtable allNodes = new ObjectHashtable(); - protected Hashtable parents = new ObjectHashtable(); - protected Hashtable scope = new ObjectHashtable(); + private Hashtable allNodes = new ObjectHashtable(); + private Hashtable parents = new ObjectHashtable(); + private Hashtable scope = new ObjectHashtable(); //----------------------------------------------- @@ -150,7 +150,7 @@ protected override void EndScope(QilNode node) // Helper methods //----------------------------------------------- - private class ObjectHashtable : Hashtable + private sealed class ObjectHashtable : Hashtable { protected override bool KeyEquals(object? item, object key) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilVisitor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilVisitor.cs index 561d83704219a..3e5998b65885c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilVisitor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilVisitor.cs @@ -433,7 +433,7 @@ protected virtual QilNode VisitReference(QilNode n) } //for checking the depth of QilNode to avoid StackOverflow in visit stage - internal class QilDepthChecker + internal sealed class QilDepthChecker { private const int MAX_QIL_DEPTH = 800; private readonly Dictionary _visitedRef = new Dictionary(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilXmlWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilXmlWriter.cs index 7e2b54f7ced50..b81a2a61f4beb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilXmlWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QIL/QilXmlWriter.cs @@ -35,10 +35,10 @@ internal interface IQilAnnotation /// Annotations are serialized as processing-instructions in front of a node. /// Feel free to subclass this visitor to customize its behavior. /// - internal class QilXmlWriter : QilScopedVisitor + internal sealed class QilXmlWriter : QilScopedVisitor { - protected XmlWriter writer; - protected Options options; + private XmlWriter writer; + private Options options; private readonly NameGenerator _ngen; [Flags] @@ -90,7 +90,7 @@ public void ToXml(QilNode node) /// 3. IList{object} -- recursively call WriteAnnotations for each object in list /// 4. otherwise, do not write the annotation ///
- protected virtual void WriteAnnotations(object? ann) + private void WriteAnnotations(object? ann) { string? s = null, name = null; @@ -121,7 +121,7 @@ protected virtual void WriteAnnotations(object? ann) /// /// Called in order to write out source line information. /// - protected virtual void WriteLineInfo(QilNode node) + private void WriteLineInfo(QilNode node) { this.writer.WriteAttributeString("lineInfo", string.Format(CultureInfo.InvariantCulture, "[{0},{1} -- {2},{3}]", node.SourceLine!.Start.Line, node.SourceLine.Start.Pos, @@ -132,7 +132,7 @@ protected virtual void WriteLineInfo(QilNode node) /// /// Called in order to write out the xml type of a node. /// - protected virtual void WriteXmlType(QilNode node) + private void WriteXmlType(QilNode node) { this.writer.WriteAttributeString("xmlType", node.XmlType!.ToString((this.options & Options.RoundTripTypeInfo) != 0 ? "S" : "G")); } @@ -316,7 +316,7 @@ protected override void AfterVisit(QilNode node) /// /// Find list of all iterators and functions which are referenced before they have been declared. /// - internal class ForwardRefFinder : QilVisitor + internal sealed class ForwardRefFinder : QilVisitor { private readonly List _fwdrefs = new List(); private readonly List _backrefs = new List(); @@ -439,7 +439,7 @@ public void ClearName(QilNode n) /// /// Class used to hold our annotations on the graph /// - private class NameAnnotation : ListBase + private sealed class NameAnnotation : ListBase { public string Name; public object? PriorAnnotation; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QueryReaderSettings.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QueryReaderSettings.cs index 9bd4a85ac9023..4a056cc8af08d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QueryReaderSettings.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/QueryReaderSettings.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl { - internal class QueryReaderSettings + internal sealed class QueryReaderSettings { private readonly bool _validatingReader; private readonly XmlReaderSettings? _xmlReaderSettings; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs index ee3d21356c627..dba6c4bf8d555 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DecimalFormatter.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.Runtime { - internal class DecimalFormat + internal sealed class DecimalFormat { public NumberFormatInfo info; public char digit; @@ -24,7 +24,7 @@ internal DecimalFormat(NumberFormatInfo info, char digit, char zeroDigit, char p } } - internal class DecimalFormatter + internal sealed class DecimalFormatter { private readonly NumberFormatInfo _posFormatInfo; private readonly NumberFormatInfo _negFormatInfo; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DocumentOrderComparer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DocumentOrderComparer.cs index f595922ebcb62..4641526f0b5e1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DocumentOrderComparer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/DocumentOrderComparer.cs @@ -16,7 +16,7 @@ namespace System.Xml.Xsl.Runtime /// XmlNodeOrder.Unknown, a stable order between documents is maintained by an ordered list mapping each root node /// to an ordering index. /// - internal class DocumentOrderComparer : IComparer + internal sealed class DocumentOrderComparer : IComparer { private List _roots; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs index 9b44a9fe557d9..b63284285b45c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/EarlyBoundInfo.cs @@ -3,6 +3,7 @@ #nullable disable using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace System.Xml.Xsl.Runtime @@ -14,13 +15,16 @@ internal sealed class EarlyBoundInfo { private readonly string _namespaceUri; // Namespace Uri mapped to these early bound functions private readonly ConstructorInfo _constrInfo; // Constructor for the early bound function object + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + private readonly Type _ebType; - public EarlyBoundInfo(string namespaceUri, Type ebType) + public EarlyBoundInfo(string namespaceUri, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type ebType) { Debug.Assert(namespaceUri != null && ebType != null); // Get the default constructor _namespaceUri = namespaceUri; + _ebType = ebType; _constrInfo = ebType.GetConstructor(Type.EmptyTypes); Debug.Assert(_constrInfo != null, "The early bound object type " + ebType.FullName + " must have a public default constructor"); } @@ -33,7 +37,11 @@ public EarlyBoundInfo(string namespaceUri, Type ebType) /// /// Return the Clr Type of the early bound object. /// - public Type EarlyBoundType { get { return _constrInfo.DeclaringType; } } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + public Type EarlyBoundType + { + get { return _ebType; } + } /// /// Create an instance of the early bound object. diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/NumberFormatter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/NumberFormatter.cs index ffe223ead7302..f2f4c9b2aa7d8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/NumberFormatter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/NumberFormatter.cs @@ -72,7 +72,7 @@ internal enum NumberingSequence LastSpecial = Zodiac3, } - internal class NumberFormatterBase + internal abstract class NumberFormatterBase { protected const int MaxAlphabeticValue = int.MaxValue; // Maximum value that can be represented private const int MaxAlphabeticLength = 7; // Number of letters needed to represent the maximum value diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleLookup.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleLookup.cs index 20cccb874ffb0..5197ab728ecf8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleLookup.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleLookup.cs @@ -16,7 +16,7 @@ namespace System.Xml.Xsl.Runtime /// This class keeps a list of whitespace rules in order to determine whether whitespace children of particular /// elements should be stripped. /// - internal class WhitespaceRuleLookup + internal sealed class WhitespaceRuleLookup { private readonly Hashtable _qnames; private readonly ArrayList _wildcards; @@ -124,7 +124,7 @@ public bool ShouldStripSpace(string localName, string namespaceName) return (qnameRule != null && !qnameRule.PreserveSpace); } - private class InternalWhitespaceRule : WhitespaceRule + private sealed class InternalWhitespaceRule : WhitespaceRule { private int _priority; // Relative priority of this test private int _hashCode; // Cached hashcode diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleReader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleReader.cs index dabcd574c4775..7deb72ba577ab 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleReader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/WhitespaceRuleReader.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.Runtime { /// /// - internal class WhitespaceRuleReader : XmlWrappingReader + internal sealed class WhitespaceRuleReader : XmlWrappingReader { private readonly WhitespaceRuleLookup _wsRules; private readonly BitStack _stkStrip; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs index 1707aa9d56099..da2e1b645d436 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlExtensionFunction.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Globalization; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Xsl.Runtime { @@ -16,7 +17,7 @@ namespace System.Xml.Xsl.Runtime /// Table of bound extension functions. Once an extension function is bound and entered into the table, future bindings /// will be very fast. This table is not thread-safe. /// - internal class XmlExtensionFunctionTable + internal sealed class XmlExtensionFunctionTable { private readonly Dictionary _table; private XmlExtensionFunction _funcCached; @@ -26,7 +27,12 @@ public XmlExtensionFunctionTable() _table = new Dictionary(); } - public XmlExtensionFunction Bind(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) + public XmlExtensionFunction Bind( + string name, + string namespaceUri, + int numArgs, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type objectType, + BindingFlags flags) { XmlExtensionFunction func; @@ -52,11 +58,12 @@ public XmlExtensionFunction Bind(string name, string namespaceUri, int numArgs, /// /// This internal class contains methods that allow binding to extension functions and invoking them. /// - internal class XmlExtensionFunction + internal sealed class XmlExtensionFunction { private string _namespaceUri; // Extension object identifier private string _name; // Name of this method private int _numArgs; // Argument count + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] private Type _objectType; // Type of the object which will be searched for matching methods private BindingFlags _flags; // Modifiers that were used to search for a matching signature private int _hashCode; // Pre-computed hashcode @@ -87,7 +94,7 @@ public XmlExtensionFunction(string name, string namespaceUri, MethodInfo meth) /// /// Constructor. /// - public XmlExtensionFunction(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) + public XmlExtensionFunction(string name, string namespaceUri, int numArgs, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicMethods)] Type objectType, BindingFlags flags) { Init(name, namespaceUri, numArgs, objectType, flags); } @@ -95,7 +102,7 @@ public XmlExtensionFunction(string name, string namespaceUri, int numArgs, Type /// /// Initialize, but do not bind. /// - public void Init(string name, string namespaceUri, int numArgs, Type objectType, BindingFlags flags) + public void Init(string name, string namespaceUri, int numArgs, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicMethods)] Type objectType, BindingFlags flags) { _name = name; _namespaceUri = namespaceUri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlNavigatorFilter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlNavigatorFilter.cs index ee3756d2e7afe..78a849b50020c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlNavigatorFilter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlNavigatorFilter.cs @@ -58,7 +58,7 @@ public abstract class XmlNavigatorFilter /// /// Filters any non-element and any element with a non-matching local name or namespace uri. /// - internal class XmlNavNameFilter : XmlNavigatorFilter + internal sealed class XmlNavNameFilter : XmlNavigatorFilter { private readonly string _localName; private readonly string _namespaceUri; @@ -133,7 +133,7 @@ public override bool IsFiltered(XPathNavigator navigator) /// /// Filters any node not of the specified type (type may not be attribute or namespace). /// - internal class XmlNavTypeFilter : XmlNavigatorFilter + internal sealed class XmlNavTypeFilter : XmlNavigatorFilter { private static readonly XmlNavigatorFilter[] s_typeFilters = CreateTypeFilters(); private readonly XPathNodeType _nodeType; @@ -224,7 +224,7 @@ public override bool IsFiltered(XPathNavigator navigator) /// /// Filters all attribute nodes. /// - internal class XmlNavAttrFilter : XmlNavigatorFilter + internal sealed class XmlNavAttrFilter : XmlNavigatorFilter { private static readonly XmlNavigatorFilter s_singleton = new XmlNavAttrFilter(); @@ -296,7 +296,7 @@ public override bool IsFiltered(XPathNavigator navigator) /// /// Never filter nodes. /// - internal class XmlNavNeverFilter : XmlNavigatorFilter + internal sealed class XmlNavNeverFilter : XmlNavigatorFilter { private static readonly XmlNavigatorFilter s_singleton = new XmlNavNeverFilter(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs index c2d3b2e3781d1..c696d59abe044 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryContext.cs @@ -12,6 +12,8 @@ using System.Xml.Schema; using System.Xml.XPath; using System.Runtime.Versioning; +using System.Diagnostics.CodeAnalysis; +using System.Xml.Xsl.Xslt; namespace System.Xml.Xsl.Runtime { @@ -222,6 +224,8 @@ public object GetParameter(string localName, string namespaceUri) /// /// Return the extension object that is mapped to the specified namespace, or null if no object is mapped. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = XsltArgumentList.ExtensionObjectSuppresion)] public object GetLateBoundObject(string namespaceUri) { return (_argList != null) ? _argList.GetExtensionObject(namespaceUri) : null; @@ -230,6 +234,7 @@ public object GetLateBoundObject(string namespaceUri) /// /// Return true if the late bound object identified by "namespaceUri" contains a method that matches "name". /// + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public bool LateBoundFunctionExists(string name, string namespaceUri) { object instance; @@ -248,6 +253,7 @@ public bool LateBoundFunctionExists(string name, string namespaceUri) /// Get a late-bound extension object from the external argument list. Bind to a method on the object and invoke it, /// passing "args" as arguments. /// + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public IList InvokeXsltLateBoundFunction(string name, string namespaceUri, IList[] args) { object instance; @@ -335,7 +341,7 @@ public void OnXsltMessageEncountered(string message) /// /// Simple implementation of XsltMessageEncounteredEventArgs. /// - internal class XmlILQueryEventArgs : XsltMessageEncounteredEventArgs + internal sealed class XmlILQueryEventArgs : XsltMessageEncounteredEventArgs { private readonly string _message; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs index ce2e8f1ccbe9b..4a490b3128ffb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryRuntime.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Xml.Schema; using System.Xml.XPath; using System.Xml.Xsl.IlGen; +using System.Xml.Xsl.Xslt; using MS.Internal.Xml.XPath; namespace System.Xml.Xsl.Runtime @@ -273,6 +275,7 @@ public object GetEarlyBoundObject(int index) /// /// Return true if the early bound object identified by "namespaceUri" contains a method that matches "name". /// + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public bool EarlyBoundFunctionExists(string name, string namespaceUri) { if (_earlyInfo == null) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs index 6f0fd48703b77..fa076608d7548 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlQueryStaticData.cs @@ -4,6 +4,7 @@ #nullable disable using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Xml.Xsl.IlGen; using System.Xml.Xsl.Qil; @@ -13,7 +14,7 @@ namespace System.Xml.Xsl.Runtime /// /// Contains all static data that is used by the runtime. /// - internal class XmlQueryStaticData + internal sealed class XmlQueryStaticData { // Name of the field to serialize to public const string DataFieldName = "staticData"; @@ -35,6 +36,7 @@ internal class XmlQueryStaticData /// /// Constructor. /// + [RequiresUnreferencedCode("This method will create a copy that uses earlybound types which cannot be statically analyzed.")] public XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList whitespaceRules, StaticDataManager staticData) { Debug.Assert(defaultWriterSettings != null && staticData != null); @@ -70,6 +72,7 @@ public XmlQueryStaticData(XmlWriterSettings defaultWriterSettings, IList /// Deserialize XmlQueryStaticData object from a byte array. /// + [RequiresUnreferencedCode("This method will create EarlyBoundInfo from passed in ebTypes array which cannot be statically analyzed.")] public XmlQueryStaticData(byte[] data, Type[] ebTypes) { MemoryStream dataStream = new MemoryStream(data, /*writable:*/false); @@ -399,7 +402,7 @@ public EarlyBoundInfo[] EarlyBound /// /// Subclass of BinaryReader used to serialize query static data. /// - internal class XmlQueryDataReader : BinaryReader + internal sealed class XmlQueryDataReader : BinaryReader { public XmlQueryDataReader(Stream input) : base(input) { } @@ -429,7 +432,7 @@ public sbyte ReadSByte(sbyte minValue, sbyte maxValue) /// /// Subclass of BinaryWriter used to deserialize query static data. /// - internal class XmlQueryDataWriter : BinaryWriter + internal sealed class XmlQueryDataWriter : BinaryWriter { public XmlQueryDataWriter(Stream output) : base(output) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSequenceWriter.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSequenceWriter.cs index 873f52b8b6eec..677c8660cc2a3 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSequenceWriter.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSequenceWriter.cs @@ -54,7 +54,7 @@ internal abstract class XmlSequenceWriter /// /// An implementation of XmlSequenceWriter that builds a cached XPath/XQuery sequence. /// - internal class XmlCachedSequenceWriter : XmlSequenceWriter + internal sealed class XmlCachedSequenceWriter : XmlSequenceWriter { private readonly XmlQueryItemSequence _seqTyped; private XPathDocument _doc; @@ -119,7 +119,7 @@ public override void WriteItem(XPathItem item) /// 3. A call to XmlRawWriter.WriteWhitespace(" ") is made between adjacent atomic values at the top-level /// 4. All items in the top-level sequence are merged together into a single result document. /// - internal class XmlMergeSequenceWriter : XmlSequenceWriter + internal sealed class XmlMergeSequenceWriter : XmlSequenceWriter { private readonly XmlRawWriter _xwrt; private bool _lastItemWasAtomic; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSortKey.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSortKey.cs index fe94d265fb885..6d5c89d14ca8b 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSortKey.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XmlSortKey.cs @@ -98,7 +98,7 @@ protected int CompareToEmpty(object obj) /// Sort key for the empty sequence. Empty sequence always compares sorts either before all other values, /// or after all other values. /// - internal class XmlEmptySortKey : XmlSortKey + internal sealed class XmlEmptySortKey : XmlSortKey { private readonly bool _isEmptyGreatest; @@ -136,7 +136,7 @@ public override int CompareTo(object obj) /// /// Sort key for xs:decimal values. /// - internal class XmlDecimalSortKey : XmlSortKey + internal sealed class XmlDecimalSortKey : XmlSortKey { private readonly decimal _decVal; @@ -194,7 +194,7 @@ public override int CompareTo(object obj) /// /// Sort key for xs:int values. /// - internal class XmlIntSortKey : XmlSortKey + internal sealed class XmlIntSortKey : XmlSortKey { private readonly int _intVal; @@ -221,7 +221,7 @@ public override int CompareTo(object obj) /// /// Sort key for xs:string values. Strings are sorted according to a byte-wise sort key calculated by caller. /// - internal class XmlStringSortKey : XmlSortKey + internal sealed class XmlStringSortKey : XmlSortKey { private readonly SortKey _sortKey; private readonly byte[] _sortKeyBytes; @@ -294,7 +294,7 @@ public override int CompareTo(object obj) /// /// Sort key for Double values. /// - internal class XmlDoubleSortKey : XmlSortKey + internal sealed class XmlDoubleSortKey : XmlSortKey { private readonly double _dblVal; private readonly bool _isNaN; @@ -361,7 +361,7 @@ public override int CompareTo(object obj) /// /// Sort key for DateTime values (just convert DateTime to ticks and use Long sort key). /// - internal class XmlDateTimeSortKey : XmlIntegerSortKey + internal sealed class XmlDateTimeSortKey : XmlIntegerSortKey { public XmlDateTimeSortKey(DateTime value, XmlCollation collation) : base(value.Ticks, collation) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs index 4673961e6b7b2..a0f4551447647 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XslNumber.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.Runtime { - internal class TokenInfo + internal sealed class TokenInfo { public char startChar; // First element of numbering sequence for format token public int startIdx; // Start index of separator token @@ -109,7 +109,7 @@ public static TokenInfo CreateFormat(string formatString, int startIdx, int tokL } } - internal class NumberFormatter : NumberFormatterBase + internal sealed class NumberFormatter : NumberFormatterBase { private readonly string _formatString; private readonly int _lang; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs index 0c8018bbb1ba6..31c779d6abd65 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Runtime/XsltLibrary.cs @@ -10,6 +10,7 @@ using System.Xml.XPath; using System.Xml.Xsl.Xslt; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Xsl.Runtime { @@ -59,7 +60,11 @@ internal static class XsltMethods // XSLT functions and helper methods (non-static) public static readonly MethodInfo CheckScriptNamespace = typeof(XsltLibrary).GetMethod("CheckScriptNamespace"); - public static readonly MethodInfo FunctionAvailable = typeof(XsltLibrary).GetMethod("FunctionAvailable"); + public static readonly MethodInfo FunctionAvailable = GetFunctionAvailableMethod(); + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since this code path " + + "will only be emitting IL that will later be called by Transform() method which is already annotated as RequiresUnreferencedCode")] + private static MethodInfo GetFunctionAvailableMethod() => typeof(XsltLibrary).GetMethod("FunctionAvailable"); public static readonly MethodInfo ElementAvailable = typeof(XsltLibrary).GetMethod("ElementAvailable"); public static readonly MethodInfo RegisterDecimalFormat = typeof(XsltLibrary).GetMethod("RegisterDecimalFormat"); public static readonly MethodInfo RegisterDecimalFormatter = typeof(XsltLibrary).GetMethod("RegisterDecimalFormatter"); @@ -110,6 +115,7 @@ public bool ElementAvailable(XmlQualifiedName name) } // Spec: http://www.w3.org/TR/xslt#function-function-available + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] public bool FunctionAvailable(XmlQualifiedName name) { if (_functionsAvail == null) @@ -130,6 +136,7 @@ public bool FunctionAvailable(XmlQualifiedName name) return result; } + [RequiresUnreferencedCode(Scripts.ExtensionFunctionCannotBeStaticallyAnalyzed)] private bool FunctionAvailableHelper(XmlQualifiedName name) { // Is this an XPath or an XSLT function? diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/SourceLineInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/SourceLineInfo.cs index d0b14c713385a..ac4e94d9bc5f7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/SourceLineInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/SourceLineInfo.cs @@ -25,11 +25,11 @@ public bool LessOrEqual(Location that) } [DebuggerDisplay("{Uri} [{StartLine},{StartPos} -- {EndLine},{EndPos}]")] - internal class SourceLineInfo : ISourceLineInfo + internal sealed class SourceLineInfo : ISourceLineInfo { - protected string? uriString; - protected Location start; - protected Location end; + private string? uriString; + private Location start; + private Location end; public SourceLineInfo(string? uriString, int startLine, int startPos, int endLine, int endPos) : this(uriString, new Location(startLine, startPos), new Location(endLine, endPos)) @@ -53,7 +53,7 @@ public SourceLineInfo(string? uriString, Location start, Location end) /// When VS debugger steps into IL marked with 0xfeefee, it will continue the step until it reaches /// some user code. /// - protected const int NoSourceMagicNumber = 0xfeefee; + private const int NoSourceMagicNumber = 0xfeefee; public static SourceLineInfo NoSource = new SourceLineInfo(string.Empty, NoSourceMagicNumber, 0, NoSourceMagicNumber, 0); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathBuilder.cs index 0be02ffcb2f7b..80a5b06014e8a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathBuilder.cs @@ -828,7 +828,7 @@ public static bool IsFunctionAvailable(string localName, string nsUri) return FunctionTable.ContainsKey(localName); } - internal class FixupVisitor : QilReplaceVisitor + internal sealed class FixupVisitor : QilReplaceVisitor { private new readonly QilPatternFactory f; private readonly QilNode _fixupCurrent, _fixupPosition, _fixupLast; // fixup nodes we are replacing @@ -975,7 +975,7 @@ bool GetStopVisitMark(QilNode n) { #endif } - internal class FunctionInfo + internal sealed class FunctionInfo { public T id; public int minArgs; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathCompileException.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathCompileException.cs index 6b2e67550a4c1..c4d4e030964ba 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathCompileException.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathCompileException.cs @@ -7,7 +7,7 @@ namespace System.Xml.Xsl.XPath { [Serializable] - internal class XPathCompileException : XslLoadException + internal sealed class XPathCompileException : XslLoadException { public string? queryString; public int startChar; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathContext.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathContext.cs index f1b073d2e95af..94002f11864bc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathContext.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathContext.cs @@ -13,7 +13,7 @@ namespace System.Xml.Xsl.XPath { - internal class XPathContext { + internal sealed class XPathContext { // Context is the most fundamental concept of XPath // In docs it is -- "current node-set" and "current node in this node-set" // on practice in this implementation we have "current node" (C), "position of current node in current node-set" (P) @@ -96,7 +96,7 @@ public static QilTuple GetTuple(QilTuple context) { return context; } - private class Replacer : QilActiveVisitor { + private sealed class Replacer : QilActiveVisitor { QilIterator from, to; public Replacer(QilFactory f) : base(f) {} diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathParser.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathParser.cs index 2c1a51cf9469e..f2226a57863dd 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathParser.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPath/XPathParser.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XPath { using XPathNodeType = System.Xml.XPath.XPathNodeType; - internal class XPathParser + internal sealed class XPathParser { private XPathScanner? _scanner; private IXPathBuilder? _builder; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPathConvert.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPathConvert.cs index 3e0daa1336abc..ed6d5cc482e4e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPathConvert.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XPathConvert.cs @@ -1683,7 +1683,7 @@ function Sub(a, b) * Hungarian: bi * */ - private class BigInteger : IComparable + private sealed class BigInteger : IComparable { // Make this big enough that we rarely have to reallocate. private const int InitCapacity = 30; @@ -2315,7 +2315,7 @@ public static explicit operator double(BigInteger bi) { /** * Floating point number represented in base-10. */ - private class FloatingDecimal + private sealed class FloatingDecimal { public const int MaxDigits = 50; private const int MaxExp10 = 310; // Upper bound on base 10 exponent diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs index 0cb44e83eee64..0ee028ba24909 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlILCommand.cs @@ -16,7 +16,7 @@ namespace System.Xml.Xsl /// /// This is the executable command generated by the XmlILGenerator. /// - internal class XmlILCommand + internal sealed class XmlILCommand { private readonly ExecuteDelegate _delExec; private readonly XmlQueryStaticData _staticData; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs index 610a883ecfb0e..19dbf13cf50a1 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlIlGenerator.cs @@ -49,7 +49,7 @@ namespace System.Xml.Xsl /// As visits to each node in the tree start and end, various Analyzers are invoked. These Analyzers incrementally /// collect and store information that is later used to generate faster and smaller code. /// - internal class XmlILGenerator + internal sealed class XmlILGenerator { private QilExpression? _qil; private GenerateHelper? _helper; @@ -70,6 +70,10 @@ public XmlILGenerator() // SxS Note: The way the trace file names are created (hardcoded) is NOT SxS safe. However the files are // created only for internal tracing purposes. In addition XmlILTrace class is not compiled into retail // builds. As a result it is fine to suppress the FxCop SxS warning. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "This method will generate the IL methods using RefEmit at runtime, which will then try to call them " + + "using methods that are annotated as RequiresUnreferencedCode. In this case, these uses can be suppressed as the " + + "trimmer won't be able to trim any IL that gets generated at runtime.")] public XmlILCommand? Generate(QilExpression query, TypeBuilder? typeBldr) { _qil = query; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlQualifiedNameTest.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlQualifiedNameTest.cs index b140fc567eac5..a3af660f0379e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlQualifiedNameTest.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XmlQualifiedNameTest.cs @@ -21,7 +21,7 @@ namespace System.Xml.Xsl /// ~{A}:* XmlQualifiedNameTest.New("B", "A") Match QName with namespace not A and any local name /// {~A}:B only as a result of the intersection Match QName with namespace not A and local name B /// - internal class XmlQualifiedNameTest : XmlQualifiedName + internal sealed class XmlQualifiedNameTest : XmlQualifiedName { private readonly bool _exclude; private const string wildcard = "*"; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs index b195085b3a4e7..c8ad0097517fb 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Compiler.cs @@ -22,7 +22,7 @@ internal enum XslVersion // One more reason to for this design is to normolize apply-templates and apply-imports to one concept: // apply-templates is apply-imports(compiler.Root). // For now I don't create new files for these new classes to simplify integrations WebData <-> WebData_xsl - internal class RootLevel : StylesheetLevel + internal sealed class RootLevel : StylesheetLevel { public RootLevel(Stylesheet principal) { @@ -30,7 +30,7 @@ public RootLevel(Stylesheet principal) } } - internal class Compiler + internal sealed class Compiler { public XsltSettings Settings; public bool IsDebug; @@ -355,7 +355,7 @@ private void SortErrors() } } - private class CompilerErrorComparer : IComparer + private sealed class CompilerErrorComparer : IComparer { private readonly Dictionary _moduleOrder; @@ -400,7 +400,7 @@ public int Compare(CompilerError? x, CompilerError? y) } } - internal class Output + internal sealed class Output { public XmlWriterSettings Settings; public string? Version; @@ -430,7 +430,7 @@ public Output() } } - internal class DecimalFormats : KeyedCollection + internal sealed class DecimalFormats : KeyedCollection { protected override XmlQualifiedName GetKeyForItem(DecimalFormatDecl format) { @@ -438,7 +438,7 @@ protected override XmlQualifiedName GetKeyForItem(DecimalFormatDecl format) } } - internal class DecimalFormatDecl + internal sealed class DecimalFormatDecl { public readonly XmlQualifiedName Name; public readonly string InfinitySymbol; @@ -457,7 +457,7 @@ public DecimalFormatDecl(XmlQualifiedName name, string infinitySymbol, string na } } - internal class NsAlias + internal sealed class NsAlias { public readonly string ResultNsUri; public readonly string? ResultPrefix; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/CompilerError.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/CompilerError.cs index fa10fd9a2480f..1ae326c41bc12 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/CompilerError.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/CompilerError.cs @@ -5,7 +5,7 @@ namespace System.Xml.Xsl.Xslt { - internal class CompilerError + internal sealed class CompilerError { public CompilerError(string fileName, int line, int column, string errorNumber, string errorText) { @@ -29,7 +29,7 @@ public CompilerError(string fileName, int line, int column, string errorNumber, public string FileName { get; set; } } - internal class CompilerErrorCollection : CollectionBase + internal sealed class CompilerErrorCollection : CollectionBase { public CompilerErrorCollection() { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/InvokeGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/InvokeGenerator.cs index 6e15fc5605a9d..9cd4c14dc1334 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/InvokeGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/InvokeGenerator.cs @@ -20,7 +20,7 @@ ArrayList actualArgs -- Array of QilNodes annotated with names. When name of fo is used as invokeArg, otherwise formalArg's default value is cloned and used. **/ - internal class InvokeGenerator : QilCloneVisitor + internal sealed class InvokeGenerator : QilCloneVisitor { private readonly bool _debug; private readonly Stack _iterStack; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/KeyMatchBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/KeyMatchBuilder.cs index b7a110a2e6023..5b35ae0a37f77 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/KeyMatchBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/KeyMatchBuilder.cs @@ -14,7 +14,7 @@ namespace System.Xml.Xsl.Xslt { - internal class KeyMatchBuilder : XPathBuilder, XPathPatternParser.IPatternBuilder + internal sealed class KeyMatchBuilder : XPathBuilder, XPathPatternParser.IPatternBuilder { private int _depth; private readonly PathConvertor _convertor; @@ -55,14 +55,14 @@ public override void StartBuild() // -------------------------------------- GetPredicateBuilder() --------------------------------------- - public virtual IXPathBuilder GetPredicateBuilder(QilNode ctx) + public IXPathBuilder GetPredicateBuilder(QilNode ctx) { return this; } // This code depends on particula shapes that XPathBuilder generates. // It works only on pathes. - internal class PathConvertor : QilReplaceVisitor + internal sealed class PathConvertor : QilReplaceVisitor { private new readonly XPathQilFactory f; private QilNode? _fixup; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Keywords.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Keywords.cs index 7574183e0ca17..89626c7e10070 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Keywords.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Keywords.cs @@ -5,7 +5,7 @@ namespace System.Xml.Xsl.Xslt { - internal class KeywordsTable + internal sealed class KeywordsTable { public XmlNameTable NameTable; public string AnalyzeString; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs index 3e16e60d002d8..0df9d3b3a9d7a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/MatcherBuilder.cs @@ -86,7 +86,7 @@ namespace System.Xml.Xsl.Xslt */ #endregion - internal class TemplateMatch + internal sealed class TemplateMatch { public static readonly TemplateMatchComparer Comparer = new TemplateMatchComparer(); @@ -224,7 +224,7 @@ private void NipOffTypeNameCheck() } } - internal class TemplateMatchComparer : IComparer + internal sealed class TemplateMatchComparer : IComparer { // TemplateMatch x is "greater" than TemplateMatch y iff // * x's priority is greater than y's priority, or @@ -258,7 +258,7 @@ public Pattern(TemplateMatch match, int priority) } } - internal class PatternBag + internal sealed class PatternBag { public Dictionary> FixedNamePatterns = new Dictionary>(); public List FixedNamePatternsNames = new List(); // Needed only to guarantee a stable order @@ -292,7 +292,7 @@ public void Add(Pattern pattern) } } - internal class MatcherBuilder + internal sealed class MatcherBuilder { private readonly XPathQilFactory _f; private readonly ReferenceReplacer _refReplacer; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/OutputScopeManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/OutputScopeManager.cs index ffcbe6d8e387c..3a2fa196302cc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/OutputScopeManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/OutputScopeManager.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.Xslt { - internal class OutputScopeManager + internal sealed class OutputScopeManager { public struct ScopeReord { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs index d7d3d3740534d..2352c31d00c06 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGenerator.cs @@ -23,7 +23,7 @@ namespace System.Xml.Xsl.Xslt // Everywhere in this code in case of error in the stylesheet we should call ReportError or ReportWarning - internal class ReferenceReplacer : QilReplaceVisitor + internal sealed class ReferenceReplacer : QilReplaceVisitor { private QilReference? _lookFor, _replaceBy; @@ -45,7 +45,7 @@ protected override QilNode VisitReference(QilNode n) } } - internal partial class QilGenerator : IErrorHelper + internal sealed partial class QilGenerator : IErrorHelper { private readonly CompilerScopeManager _scope; private readonly OutputScopeManager _outputScope; @@ -196,13 +196,14 @@ private QilExpression Compile(Compiler compiler) } // Create list of all early bound objects - Dictionary scriptClasses = compiler.Scripts.ScriptClasses; + Scripts.TrimSafeDictionary scriptClasses = compiler.Scripts.ScriptClasses; List ebTypes = new List(scriptClasses.Count); - foreach (KeyValuePair pair in scriptClasses) + foreach (string key in scriptClasses.Keys) { - if (pair.Value != null) + Type? value = scriptClasses[key]; + if (value != null) { - ebTypes.Add(new EarlyBoundInfo(pair.Key, pair.Value)); + ebTypes.Add(new EarlyBoundInfo(key, value)); } } @@ -410,7 +411,7 @@ private XmlQueryType ChooseBestType(VarPar var) switch (var.Flags & XslFlags.TypeFilter) { - case XslFlags.String: return T.StringX; ; + case XslFlags.String: return T.StringX; case XslFlags.Number: return T.DoubleX; case XslFlags.Boolean: return T.BooleanX; case XslFlags.Node: return T.NodeNotRtf; @@ -2853,7 +2854,7 @@ private QilName CloneName(QilName name) } // This helper internal class is used for compiling sort's and with-param's - private class VariableHelper + private sealed class VariableHelper { private readonly Stack _vars = new Stack(); private readonly XPathQilFactory _f; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs index 38d543b513841..a9a2e410c1aa5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilGeneratorEnv.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Xml.Schema; using System.Xml.XPath; using System.Xml.Xsl.Qil; @@ -14,7 +15,7 @@ namespace System.Xml.Xsl.Xslt using FunctionInfo = XPathBuilder.FunctionInfo; using T = XmlQueryTypeFactory; - internal partial class QilGenerator : IXPathEnvironment + internal sealed partial class QilGenerator : IXPathEnvironment { // Everywhere in this code in case of error in the stylesheet we should throw XslLoadException. // This helper IErrorHelper implementation is used to wrap XmlException's into XslLoadException's. @@ -95,6 +96,9 @@ QilNode IXPathEnvironment.ResolveVariable(string prefix, string name) } // NOTE: DO NOT call QilNode.Clone() while executing this method since fixup nodes cannot be cloned + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Suppressing the warning for the ResolveFunction call on the Scripts since " + + "Scripts functionality is not supported by .NET Core")] QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList args, IFocus env) { Debug.Assert(!args.IsReadOnly, "Writable collection expected"); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilStrConcatenator.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilStrConcatenator.cs index 32c71b7e35221..96010a3a622c7 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilStrConcatenator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/QilStrConcatenator.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.Xslt { - internal class QilStrConcatenator + internal sealed class QilStrConcatenator { private readonly XPathQilFactory _f; private readonly StringBuilder _builder; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs index cbc8e315b3a92..72ec06504aa6d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Scripts.cs @@ -4,27 +4,31 @@ // http://devdiv/Documents/Whidbey/CLR/CurrentSpecs/BCL/CodeDom%20Activation.doc //------------------------------------------------------------------------------ +using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Xml.Xsl.Runtime; namespace System.Xml.Xsl.Xslt { - internal class Scripts + internal sealed class Scripts { private readonly Compiler _compiler; - private readonly Dictionary _nsToType = new Dictionary(); + private readonly TrimSafeDictionary _nsToType = new TrimSafeDictionary(); private readonly XmlExtensionFunctionTable _extFuncs = new XmlExtensionFunctionTable(); + internal const string ExtensionFunctionCannotBeStaticallyAnalyzed = "The extension function referenced will be called from the stylesheet which cannot be statically analyzed."; public Scripts(Compiler compiler) { _compiler = compiler; } - public Dictionary ScriptClasses + public TrimSafeDictionary ScriptClasses { get { return _nsToType; } } + [RequiresUnreferencedCode(ExtensionFunctionCannotBeStaticallyAnalyzed)] public XmlExtensionFunction? ResolveFunction(string name, string ns, int numArgs, IErrorHelper errorHelper) { Type? type; @@ -41,5 +45,29 @@ public Scripts(Compiler compiler) } return null; } + + internal sealed class TrimSafeDictionary + { + private readonly Dictionary _backingDictionary = new Dictionary(); + + public Type? this[string key] + { + [UnconditionalSuppressMessage("TrimAnalysis", "IL2073:MissingDynamicallyAccessedMembers", + Justification = "The getter of the dictionary is not annotated to preserve the constructor, but the sources that are adding the items to " + + "the dictionary are annotated so we can supress the message as we know the constructor will be preserved.")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + get => _backingDictionary[key]; + [param: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + set => _backingDictionary[key] = value; + } + + public ICollection Keys => _backingDictionary.Keys; + + public int Count => _backingDictionary.Count; + + public bool ContainsKey(string key) => _backingDictionary.ContainsKey(key); + + public bool TryGetValue(string key, [MaybeNullWhen(false)] out Type? value) => _backingDictionary.TryGetValue(key, out value); + } } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Stylesheet.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Stylesheet.cs index 011862c1ff00a..c25973488f32a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Stylesheet.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/Stylesheet.cs @@ -23,7 +23,7 @@ internal class StylesheetLevel public Dictionary> ApplyFunctions = new Dictionary>(); } - internal class Stylesheet : StylesheetLevel + internal sealed class Stylesheet : StylesheetLevel { private readonly Compiler _compiler; public List ImportHrefs = new List(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs index 663a96bbd6d88..e60c3ce827c8c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternBuilder.cs @@ -15,7 +15,7 @@ namespace System.Xml.Xsl.Xslt { using T = XmlQueryTypeFactory; - internal class XPathPatternBuilder : XPathPatternParser.IPatternBuilder + internal sealed class XPathPatternBuilder : XPathPatternParser.IPatternBuilder { private readonly XPathPredicateEnvironment _predicateEnvironment; private readonly XPathBuilder _predicateBuilder; @@ -40,7 +40,7 @@ public QilNode FixupNode get { return _fixupNode; } } - public virtual void StartBuild() + public void StartBuild() { Debug.Assert(!_inTheBuild, "XPathBuilder is busy!"); _inTheBuild = true; @@ -63,7 +63,7 @@ private void FixupFilterBinding(QilLoop filter, QilNode newBinding) } [return: NotNullIfNotNull("result")] - public virtual QilNode? EndBuild(QilNode? result) + public QilNode? EndBuild(QilNode? result) { Debug.Assert(_inTheBuild, "StartBuild() wasn't called"); if (result == null) @@ -337,7 +337,7 @@ public QilNode Variable(string prefix, string name) // -------------------------------------- Priority / Parent --------------------------------------- - private class Annotation + private sealed class Annotation { public double Priority; public QilLoop? Parent; @@ -383,7 +383,7 @@ public IXPathBuilder GetPredicateBuilder(QilNode ctx) return _predicateBuilder; } - private class XPathPredicateEnvironment : IXPathEnvironment + private sealed class XPathPredicateEnvironment : IXPathEnvironment { private readonly IXPathEnvironment _baseEnvironment; private readonly XPathQilFactory _f; @@ -426,7 +426,7 @@ public string ResolvePrefix(string prefix) public QilNode GetLast() { numFixupLast++; return _fixupLast; } } - private class XsltFunctionFocus : IFocus + private sealed class XsltFunctionFocus : IFocus { private readonly QilIterator _current; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternParser.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternParser.cs index 2f9c06bafc1e0..2cae17eacbdc5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternParser.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XPathPatternParser.cs @@ -12,7 +12,7 @@ namespace System.Xml.Xsl.Xslt using XPathParser = XPathParser; using XPathNodeType = System.Xml.XPath.XPathNodeType; - internal class XPathPatternParser + internal sealed class XPathPatternParser { public interface IPatternBuilder : IXPathBuilder { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAst.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAst.cs index 6c6b0912c552a..9c94a48a2cd77 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAst.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAst.cs @@ -58,7 +58,7 @@ internal enum XslNodeType WithParam, } - internal class NsDecl + internal sealed class NsDecl { public readonly NsDecl? Prev; public readonly string? Prefix; // Empty string denotes the default namespace, null - extension or excluded namespace @@ -198,7 +198,7 @@ internal enum CycleCheck Completed = 2, } - internal class AttributeSet : ProtoTemplate + internal sealed class AttributeSet : ProtoTemplate { public CycleCheck CycleCheck; // Used to detect circular references @@ -225,7 +225,7 @@ public void MergeContent(AttributeSet other) } } - internal class Template : ProtoTemplate + internal sealed class Template : ProtoTemplate { public readonly string? Match; public readonly QilName Mode; @@ -274,7 +274,7 @@ public override string GetDebugName() } } - internal class VarPar : XslNode + internal sealed class VarPar : XslNode { public XslFlags DefValueFlags; public QilNode? Value; // Contains value for WithParams and global VarPars @@ -282,7 +282,7 @@ internal class VarPar : XslNode public VarPar(XslNodeType nt, QilName name, string? select, XslVersion xslVer) : base(nt, name, select, xslVer) { } } - internal class Sort : XslNode + internal sealed class Sort : XslNode { public readonly string? Lang; public readonly string? DataType; @@ -299,7 +299,7 @@ public Sort(string select, string? lang, string? dataType, string? order, string } } - internal class Keys : KeyedCollection> + internal sealed class Keys : KeyedCollection> { protected override QilName GetKeyForItem(List list) { @@ -308,7 +308,7 @@ protected override QilName GetKeyForItem(List list) } } - internal class Key : XslNode + internal sealed class Key : XslNode { public readonly string? Match; public readonly string? Use; @@ -354,7 +354,7 @@ internal enum NumberLevel Any, } - internal class Number : XslNode + internal sealed class Number : XslNode { public readonly NumberLevel Level; public readonly string? Count; @@ -382,7 +382,7 @@ public Number(NumberLevel level, string? count, string? from, string? value, } } - internal class NodeCtor : XslNode + internal sealed class NodeCtor : XslNode { public readonly string NameAvt; public readonly string? NsAvt; @@ -395,7 +395,7 @@ public NodeCtor(XslNodeType nt, string nameAvt, string? nsAvt, XslVersion xslVer } } - internal class Text : XslNode + internal sealed class Text : XslNode { public readonly SerializationHints Hints; @@ -406,7 +406,7 @@ public Text(string data, SerializationHints hints, XslVersion xslVer) } } - internal class XslNodeEx : XslNode + internal sealed class XslNodeEx : XslNode { public readonly ISourceLineInfo? ElemNameLi; public readonly ISourceLineInfo? EndTagLi; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs index 2f0ffada81514..482b951312942 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XslAstAnalyzer.cs @@ -18,7 +18,7 @@ namespace System.Xml.Xsl.Xslt // ------------------------------- XslAstAnalyzer ------------------------------- - internal class XslAstAnalyzer : XslVisitor + internal sealed class XslAstAnalyzer : XslVisitor { private CompilerScopeManager? _scope; private Compiler? _compiler; @@ -52,7 +52,7 @@ internal class XslAstAnalyzer : XslVisitor /// Represents a graph using hashtable of adjacency lists. /// /// Vertex type - internal class Graph : Dictionary?> + internal sealed class Graph : Dictionary?> where V : XslNode { private static readonly IList s_empty = (new List()).AsReadOnly(); @@ -851,7 +851,7 @@ public void ReportError(string res, params string?[]? args) { } public void ReportWarning(string res, params string?[]? args) { } } - internal class XPathAnalyzer : IXPathBuilder + internal sealed class XPathAnalyzer : IXPathBuilder { private readonly XPathParser _xpathParser = new XPathParser(); private readonly CompilerScopeManager _scope; @@ -969,22 +969,22 @@ public XslFlags AnalyzeAvt(string? source) } } - public virtual void StartBuild() + public void StartBuild() { } - public virtual XslFlags EndBuild(XslFlags result) + public XslFlags EndBuild(XslFlags result) { return result; } - public virtual XslFlags String(string value) + public XslFlags String(string value) { _typeDonor = null; return XslFlags.String; } - public virtual XslFlags Number(double value) + public XslFlags Number(double value) { _typeDonor = null; return XslFlags.Number; @@ -1009,7 +1009,7 @@ public virtual XslFlags Number(double value) /*Union */ XslFlags.Nodeset, }; - public virtual XslFlags Operator(XPathOperator op, XslFlags left, XslFlags right) + public XslFlags Operator(XPathOperator op, XslFlags left, XslFlags right) { _typeDonor = null; Debug.Assert(op != XPathOperator.Unknown); @@ -1017,7 +1017,7 @@ public virtual XslFlags Operator(XPathOperator op, XslFlags left, XslFlags right return result | s_operatorType[(int)op]; } - public virtual XslFlags Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string? prefix, string? name) + public XslFlags Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string? prefix, string? name) { _typeDonor = null; if (xpathAxis == XPathAxis.Self && nodeType == XPathNodeType.All && prefix == null && name == null) @@ -1031,20 +1031,20 @@ public virtual XslFlags Axis(XPathAxis xpathAxis, XPathNodeType nodeType, string } // "left/right" - public virtual XslFlags JoinStep(XslFlags left, XslFlags right) + public XslFlags JoinStep(XslFlags left, XslFlags right) { _typeDonor = null; return (left & ~XslFlags.TypeFilter) | XslFlags.Nodeset; // "ex:Foo(position())/Bar" } // "nodeset[predicate]" - public virtual XslFlags Predicate(XslFlags nodeset, XslFlags predicate, bool isReverseStep) + public XslFlags Predicate(XslFlags nodeset, XslFlags predicate, bool isReverseStep) { _typeDonor = null; return (nodeset & ~XslFlags.TypeFilter) | XslFlags.Nodeset | (predicate & XslFlags.SideEffects); // "ex:Foo(position())[Bar]" } - public virtual XslFlags Variable(string prefix, string name) + public XslFlags Variable(string prefix, string name) { _typeDonor = ResolveVariable(prefix, name); if (_typeDonor == null) @@ -1054,7 +1054,10 @@ public virtual XslFlags Variable(string prefix, string name) return XslFlags.None; } - public virtual XslFlags Function(string prefix, string name, IList args) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "Supressing warning about not having the RequiresUnreferencedCode attribute since xsl Scripts are " + + "not supported in .NET Core")] + public XslFlags Function(string prefix, string name, IList args) { _typeDonor = null; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltInput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltInput.cs index a92349ad85fb4..f203642824f61 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltInput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltInput.cs @@ -12,7 +12,7 @@ namespace System.Xml.Xsl.Xslt // a) Forward only, one pass. // b) You should call MoveToFirstChildren on nonempty element node. (or may be skip) - internal class XsltInput : IErrorHelper + internal sealed class XsltInput : IErrorHelper { #if DEBUG private const int InitRecordsSize = 1; @@ -1134,7 +1134,7 @@ private void ReportNYI(string arg) // -------------------------------- ContextInfo ------------------------------------ - internal class ContextInfo + internal sealed class ContextInfo { public NsDecl? nsList; public ISourceLineInfo? lineInfo; // Line info for whole start tag @@ -1187,7 +1187,7 @@ public void SaveExtendedLineInfo(XsltInput input) } // We need this wrapper class because elementTagLi is not yet calculated - internal class EmptyElementEndTag : ISourceLineInfo + internal sealed class EmptyElementEndTag : ISourceLineInfo { private readonly ISourceLineInfo _elementTagLi; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs index da0c17a1febeb..6272150fdd650 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltLoader.cs @@ -21,7 +21,7 @@ namespace System.Xml.Xsl.Xslt using QName = XsltInput.DelayedQName; using XsltAttribute = XsltInput.XsltAttribute; - internal class XsltLoader : IErrorHelper + internal sealed class XsltLoader : IErrorHelper { private Compiler _compiler = null!; private XmlResolver _xmlResolver = null!; @@ -2069,7 +2069,7 @@ private XslNode XslElement() { ContextInfo ctxInfo = _input.GetAttributes(_elementAttributes); - string name = ParseNCNameAttribute(0); ; + string name = ParseNCNameAttribute(0); string? ns = ParseStringAttribute(1, "namespace"); CheckError(ns == XmlReservedNs.NsXmlNs, /*[XT_024]*/SR.Xslt_ReservedNS, ns); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs index 909d0f8a7cade..e8e8b9be0f381 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/Xslt/XsltQilFactory.cs @@ -12,7 +12,7 @@ namespace System.Xml.Xsl.Xslt { using T = XmlQueryTypeFactory; - internal class XsltQilFactory : XPathQilFactory + internal sealed class XsltQilFactory : XPathQilFactory { public XsltQilFactory(QilFactory f, bool debug) : base(f, debug) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ActionFrame.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ActionFrame.cs index 083e1e7da48a9..3ebd89409062f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ActionFrame.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ActionFrame.cs @@ -11,7 +11,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using MS.Internal.Xml.XPath; - internal class ActionFrame + internal sealed class ActionFrame { private int _state; // Action execution state private int _counter; // Counter, for the use of particular action @@ -318,7 +318,7 @@ internal bool NewNextNode(Processor proc) } // special array iterator that iterates over ArrayList of SortKey - private class XPathSortArrayIterator : XPathArrayIterator + private sealed class XPathSortArrayIterator : XPathArrayIterator { public XPathSortArrayIterator(List list) : base(list) { } public XPathSortArrayIterator(XPathSortArrayIterator it) : base(it) { } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/BuilderInfo.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/BuilderInfo.cs index 028752efcf66e..5931e93918ed0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/BuilderInfo.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/BuilderInfo.cs @@ -11,7 +11,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Diagnostics.CodeAnalysis; - internal class BuilderInfo + internal sealed class BuilderInfo { private string? _name; private string _localName; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ChooseAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ChooseAction.cs index 274ac1be1c29e..2a7935d7d885f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ChooseAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ChooseAction.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Xml.XPath; - internal class ChooseAction : ContainerAction + internal sealed class ChooseAction : ContainerAction { internal override void Compile(Compiler compiler) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Compiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Compiler.cs index 9b6c419539596..3c86fe279171a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Compiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Compiler.cs @@ -21,7 +21,7 @@ namespace System.Xml.Xsl.XsltOld { - internal class Sort + internal sealed class Sort { internal int select; internal string? lang; @@ -1169,7 +1169,7 @@ public XsltException UnexpectedKeyword() return XsltException.Create(SR.Xslt_UnexpectedKeyword, thisName, parentName); } - internal class ErrorXPathExpression : CompiledXpathExpr + internal sealed class ErrorXPathExpression : CompiledXpathExpr { private readonly string _baseUri; private readonly int _lineNumber, _linePosition; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ContainerAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ContainerAction.cs index aaa95f7190d2d..e315c71d0d52c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ContainerAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ContainerAction.cs @@ -15,7 +15,7 @@ namespace System.Xml.Xsl.XsltOld using System.Runtime.Versioning; using System.Diagnostics.CodeAnalysis; - internal class NamespaceInfo + internal sealed class NamespaceInfo { internal string? prefix; internal string? nameSpace; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/CopyCodeAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/CopyCodeAction.cs index 3234ebc3ae9c0..83e63af5e4b83 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/CopyCodeAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/CopyCodeAction.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Collections; - internal class CopyCodeAction : Action + internal sealed class CopyCodeAction : Action { // Execution states: private const int Outputting = 2; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/DbgCompiler.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/DbgCompiler.cs index 02668f7a9c511..0c07a1988ab46 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/DbgCompiler.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/DbgCompiler.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Xml.Xsl.XsltOld.Debugger; - internal class DbgData + internal sealed class DbgData { private VariableAction[] _variables; public XPathNavigator StyleSheet { get; } @@ -32,7 +32,7 @@ private DbgData() public static DbgData Empty { get { return s_nullDbgData; } } } - internal class DbgCompiler : Compiler + internal sealed class DbgCompiler : Compiler { private readonly IXsltDebugger _debugger; @@ -54,7 +54,7 @@ public DbgCompiler(IXsltDebugger debugger) private readonly ArrayList _localVars = new ArrayList(); private VariableAction[]? _globalVarsCache, _localVarsCache; - public virtual VariableAction[] GlobalVariables + public VariableAction[] GlobalVariables { get { @@ -66,7 +66,7 @@ public virtual VariableAction[] GlobalVariables return _globalVarsCache; } } - public virtual VariableAction[] LocalVariables + public VariableAction[] LocalVariables { get { @@ -316,7 +316,7 @@ public override TextEvent CreateTextEvent() // Debugger enabled implemetation of most compiled actions - private class ApplyImportsActionDbg : ApplyImportsAction + private sealed class ApplyImportsActionDbg : ApplyImportsAction { internal override void Compile(Compiler compiler) { @@ -333,7 +333,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class ApplyTemplatesActionDbg : ApplyTemplatesAction + private sealed class ApplyTemplatesActionDbg : ApplyTemplatesAction { internal override void Compile(Compiler compiler) { @@ -350,7 +350,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class AttributeActionDbg : AttributeAction + private sealed class AttributeActionDbg : AttributeAction { internal override void Compile(Compiler compiler) { @@ -367,7 +367,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class AttributeSetActionDbg : AttributeSetAction + private sealed class AttributeSetActionDbg : AttributeSetAction { internal override void Compile(Compiler compiler) { @@ -384,7 +384,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class CallTemplateActionDbg : CallTemplateAction + private sealed class CallTemplateActionDbg : CallTemplateAction { internal override void Compile(Compiler compiler) { @@ -401,7 +401,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class CommentActionDbg : CommentAction + private sealed class CommentActionDbg : CommentAction { internal override void Compile(Compiler compiler) { @@ -418,7 +418,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class CopyActionDbg : CopyAction + private sealed class CopyActionDbg : CopyAction { internal override void Compile(Compiler compiler) { @@ -435,7 +435,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class CopyOfActionDbg : CopyOfAction + private sealed class CopyOfActionDbg : CopyOfAction { internal override void Compile(Compiler compiler) { @@ -452,7 +452,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class ElementActionDbg : ElementAction + private sealed class ElementActionDbg : ElementAction { internal override void Compile(Compiler compiler) { @@ -469,7 +469,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class ForEachActionDbg : ForEachAction + private sealed class ForEachActionDbg : ForEachAction { internal override void Compile(Compiler compiler) { @@ -491,7 +491,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class IfActionDbg : IfAction + private sealed class IfActionDbg : IfAction { internal IfActionDbg(ConditionType type) : base(type) { } @@ -510,7 +510,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class MessageActionDbg : MessageAction + private sealed class MessageActionDbg : MessageAction { internal override void Compile(Compiler compiler) { @@ -527,7 +527,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class NewInstructionActionDbg : NewInstructionAction + private sealed class NewInstructionActionDbg : NewInstructionAction { internal override void Compile(Compiler compiler) { @@ -544,7 +544,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class NumberActionDbg : NumberAction + private sealed class NumberActionDbg : NumberAction { internal override void Compile(Compiler compiler) { @@ -561,7 +561,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class ProcessingInstructionActionDbg : ProcessingInstructionAction + private sealed class ProcessingInstructionActionDbg : ProcessingInstructionAction { internal override void Compile(Compiler compiler) { @@ -578,7 +578,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class RootActionDbg : RootAction + private sealed class RootActionDbg : RootAction { private DbgData? _dbgData; @@ -618,7 +618,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class SortActionDbg : SortAction + private sealed class SortActionDbg : SortAction { internal override void Compile(Compiler compiler) { @@ -635,7 +635,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class TemplateActionDbg : TemplateAction + private sealed class TemplateActionDbg : TemplateAction { internal override void Compile(Compiler compiler) { @@ -657,7 +657,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class TextActionDbg : TextAction + private sealed class TextActionDbg : TextAction { internal override void Compile(Compiler compiler) { @@ -674,7 +674,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class UseAttributeSetsActionDbg : UseAttributeSetsAction + private sealed class UseAttributeSetsActionDbg : UseAttributeSetsAction { internal override void Compile(Compiler compiler) { @@ -691,7 +691,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class ValueOfActionDbg : ValueOfAction + private sealed class ValueOfActionDbg : ValueOfAction { internal override void Compile(Compiler compiler) { @@ -708,7 +708,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class VariableActionDbg : VariableAction + private sealed class VariableActionDbg : VariableAction { internal VariableActionDbg(VariableType type) : base(type) { } private DbgData? _dbgData; @@ -730,7 +730,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - private class WithParamActionDbg : WithParamAction + private sealed class WithParamActionDbg : WithParamAction { internal override void Compile(Compiler compiler) { @@ -749,7 +749,7 @@ internal override void Execute(Processor processor, ActionFrame frame) // ---------------- Events: --------------- - private class BeginEventDbg : BeginEvent + private sealed class BeginEventDbg : BeginEvent { private readonly DbgData _dbgData; internal override DbgData DbgData { get { return _dbgData; } } @@ -765,7 +765,7 @@ public override bool Output(Processor processor, ActionFrame frame) } } - private class TextEventDbg : TextEvent + private sealed class TextEventDbg : TextEvent { private readonly DbgData _dbgData; internal override DbgData DbgData { get { return _dbgData; } } diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/EndEvent.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/EndEvent.cs index 1e831a9a5eb2f..753787a893d0e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/EndEvent.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/EndEvent.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Xml.XPath; - internal class EndEvent : Event + internal sealed class EndEvent : Event { private readonly XPathNodeType _nodeType; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/HtmlProps.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/HtmlProps.cs index 7ae3808c59185..eb507621140bc 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/HtmlProps.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/HtmlProps.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Globalization; - internal class HtmlElementProps + internal sealed class HtmlElementProps { private bool _empty; private bool _abrParent; @@ -133,7 +133,7 @@ private static Hashtable CreatePropsTable() } } - internal class HtmlAttributeProps + internal sealed class HtmlAttributeProps { private bool _abr; private bool _uri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScope.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScope.cs index 93601bbb1908f..08ad280514f43 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScope.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScope.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Collections; - internal class InputScope : DocumentScope + internal sealed class InputScope : DocumentScope { private InputScope? _parent; private bool _forwardCompatibility; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScopeManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScopeManager.cs index 25acce1c6ae40..5d2e8988b4e32 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScopeManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/InputScopeManager.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Xml.XPath; - internal class InputScopeManager + internal sealed class InputScopeManager { private InputScope? _scopeStack; private string _defaultNS = string.Empty; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NameSpaceEvent.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NameSpaceEvent.cs index 57002bcfac1a7..32c5d8090b54d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NameSpaceEvent.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NameSpaceEvent.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Xml.XPath; - internal class NamespaceEvent : Event + internal sealed class NamespaceEvent : Event { private string _namespaceUri; private string _name; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NamespaceDecl.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NamespaceDecl.cs index a709a4e16a544..a4601f1aa1438 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NamespaceDecl.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NamespaceDecl.cs @@ -7,7 +7,7 @@ namespace System.Xml.Xsl.XsltOld using System.Diagnostics.CodeAnalysis; using System.Xml; - internal class NamespaceDecl + internal sealed class NamespaceDecl { private string _prefix; private string _nsUri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NavigatorInput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NavigatorInput.cs index e149f0118fc5e..8d1497f60aa46 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NavigatorInput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NavigatorInput.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using KeywordsTable = System.Xml.Xsl.Xslt.KeywordsTable; - internal class NavigatorInput + internal sealed class NavigatorInput { private XPathNavigator? _Navigator; private PositionInfo? _PositionInfo; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs index 26b54dd0ca771..f118809686f9d 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/NumberAction.cs @@ -14,7 +14,7 @@ namespace System.Xml.Xsl.XsltOld { internal class NumberAction : ContainerAction { - internal class FormatInfo + internal sealed class FormatInfo { public bool isSeparator; // False for alphanumeric strings of chars public NumberingSequence numSequence; // Specifies numbering sequence @@ -33,7 +33,7 @@ public FormatInfo() { } private static readonly FormatInfo s_defaultFormat = new FormatInfo(false, "0"); private static readonly FormatInfo s_defaultSeparator = new FormatInfo(true, "."); - private class NumberingFormat : NumberFormatterBase + private sealed class NumberingFormat : NumberFormatterBase { private NumberingSequence _seq; private int _cMinLen; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutKeywords.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutKeywords.cs index 813f36473c3fe..4ec228d48d16c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutKeywords.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutKeywords.cs @@ -7,7 +7,7 @@ namespace System.Xml.Xsl.XsltOld using System.Diagnostics; using System.Xml; - internal class OutKeywords + internal sealed class OutKeywords { #if DEBUG private readonly XmlNameTable _NameTable; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScope.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScope.cs index c978824e63669..9f8f3d8d3c1f8 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScope.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScope.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Diagnostics.CodeAnalysis; using System.Xml; - internal class OutputScope : DocumentScope + internal sealed class OutputScope : DocumentScope { private string _name; private string _nsUri; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScopeManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScopeManager.cs index 12cb000164494..8cce6af5e27e2 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScopeManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/OutputScopeManager.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Diagnostics; using System.Xml; - internal class OutputScopeManager + internal sealed class OutputScopeManager { private const int STACK_INCREMENT = 10; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs index 8ef9fb4acef42..7eec614316236 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Processor.cs @@ -6,6 +6,7 @@ namespace System.Xml.Xsl.XsltOld using System.Collections; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; @@ -247,6 +248,8 @@ parameter is float || parameter is decimal return parameter; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = XsltArgumentList.ExtensionObjectSuppresion)] internal object? GetExtensionObject(string nsUri) { return _args.GetExtensionObject(nsUri); @@ -371,15 +374,10 @@ public Processor( _scriptExtensions = new Hashtable(_stylesheet.ScriptObjectTypes.Count); { - foreach (DictionaryEntry entry in _stylesheet.ScriptObjectTypes) + // Scripts are not supported on stylesheets + if (_stylesheet.ScriptObjectTypes.Count > 0) { - string namespaceUri = (string)entry.Key; - if (GetExtensionObject(namespaceUri) != null) - { - throw XsltException.Create(SR.Xslt_ScriptDub, namespaceUri); - } - _scriptExtensions.Add(namespaceUri, Activator.CreateInstance((Type)entry.Value!, - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, null, null)); + throw new PlatformNotSupportedException(SR.CompilingScriptsNotSupported); } } @@ -1078,7 +1076,7 @@ internal void ResetParams() // ---------------------- Debugger stack ----------------------- - internal class DebuggerFrame + internal sealed class DebuggerFrame { internal ActionFrame? actionFrame; internal XmlQualifiedName? currentMode; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ReaderOutput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ReaderOutput.cs index 0deeabd95fdcf..0e27790aa6029 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ReaderOutput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ReaderOutput.cs @@ -695,7 +695,7 @@ private void CheckCurrentInfo() Debug.Assert((_currentIndex == -1) || (_currentInfo == _attributeValue || _attributeList![_currentIndex] is BuilderInfo && _attributeList[_currentIndex] == _currentInfo)); } - private class XmlEncoder + private sealed class XmlEncoder { private StringBuilder? _buffer; private XmlTextEncoder? _encoder; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/RootAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/RootAction.cs index d1b684a3f0ee7..718a6be718ca5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/RootAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/RootAction.cs @@ -14,7 +14,7 @@ namespace System.Xml.Xsl.XsltOld using MS.Internal.Xml.XPath; using System.Security; - internal class Key + internal sealed class Key { private readonly XmlQualifiedName _name; private readonly int _matchKey; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StateMachine.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StateMachine.cs index 00027965e4c4d..9b1d337da6624 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StateMachine.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StateMachine.cs @@ -8,7 +8,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Xml.XPath; - internal class StateMachine + internal sealed class StateMachine { // Constants for the state table private const int Init = 0x000000; // Initial state diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StringOutput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StringOutput.cs index 1856daed82c2c..d5af900e9cb4c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StringOutput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/StringOutput.cs @@ -7,7 +7,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml; using System.Text; - internal class StringOutput : SequentialOutput + internal sealed class StringOutput : SequentialOutput { private readonly StringBuilder _builder; private string? _result; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Stylesheet.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Stylesheet.cs index 189e27543b14d..18ce1a7600fce 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Stylesheet.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/Stylesheet.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Collections; - internal class Stylesheet + internal sealed class Stylesheet { private readonly ArrayList _imports = new ArrayList(); private Hashtable? _modeManagers; @@ -24,7 +24,7 @@ internal class Stylesheet private TemplateManager? _templates; - private class WhitespaceElement + private sealed class WhitespaceElement { private readonly int _key; private readonly double _priority; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateLookupAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateLookupAction.cs index 8715505643d70..4b73d0950126c 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateLookupAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateLookupAction.cs @@ -94,7 +94,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - internal class TemplateLookupActionDbg : TemplateLookupAction + internal sealed class TemplateLookupActionDbg : TemplateLookupAction { internal override void Execute(Processor processor, ActionFrame frame) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateManager.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateManager.cs index 4567404439567..da0553266686f 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateManager.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TemplateManager.cs @@ -9,13 +9,13 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Collections; - internal class TemplateManager + internal sealed class TemplateManager { private readonly XmlQualifiedName _mode; internal ArrayList? templates; private readonly Stylesheet _stylesheet; // Owning stylesheet - private class TemplateComparer : IComparer + private sealed class TemplateComparer : IComparer { public int Compare(object? x, object? y) { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TextOutput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TextOutput.cs index bed1a25a468c3..15360671a3ac9 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TextOutput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/TextOutput.cs @@ -9,7 +9,7 @@ namespace System.Xml.Xsl.XsltOld using System.Xml.XPath; using System.Text; - internal class TextOutput : SequentialOutput + internal sealed class TextOutput : SequentialOutput { private TextWriter _writer; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ValueOfAction.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ValueOfAction.cs index 719b0c747bce0..dfa60089a214e 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ValueOfAction.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/ValueOfAction.cs @@ -87,7 +87,7 @@ internal override void Execute(Processor processor, ActionFrame frame) } } - internal class BuiltInRuleTextAction : Action + internal sealed class BuiltInRuleTextAction : Action { private const int ResultStored = 2; internal override void Execute(Processor processor, ActionFrame frame) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs index a0f3474dbb627..af038b5711cf0 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltCompileContext.cs @@ -15,7 +15,7 @@ namespace System.Xml.Xsl.XsltOld using System.Runtime.Versioning; using System.Diagnostics.CodeAnalysis; - internal class XsltCompileContext : XsltContext + internal sealed class XsltCompileContext : XsltContext { private InputScopeManager? _manager; private Processor? _processor; @@ -183,6 +183,8 @@ public override bool PreserveWhitespace(XPathNavigator node) } private const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:RequiresUnreferencedCode", + Justification = XsltArgumentList.ExtensionObjectSuppresion)] private IXsltContextFunction? GetExtentionMethod(string ns, string name, XPathResultType[]? argTypes, out object? extension) { FuncExtension? result = null; @@ -736,7 +738,7 @@ public static object ConvertToXPathType(object val, XPathResultType xt, Type typ } } - private class FuncCurrent : XsltFunctionImpl + private sealed class FuncCurrent : XsltFunctionImpl { public FuncCurrent() : base(0, 0, XPathResultType.NodeSet, Array.Empty()) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -745,7 +747,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncUnEntityUri : XsltFunctionImpl + private sealed class FuncUnEntityUri : XsltFunctionImpl { public FuncUnEntityUri() : base(1, 1, XPathResultType.String, new XPathResultType[] { XPathResultType.String }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -754,7 +756,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncGenerateId : XsltFunctionImpl + private sealed class FuncGenerateId : XsltFunctionImpl { public FuncGenerateId() : base(0, 1, XPathResultType.String, new XPathResultType[] { XPathResultType.NodeSet }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -779,7 +781,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncSystemProp : XsltFunctionImpl + private sealed class FuncSystemProp : XsltFunctionImpl { public FuncSystemProp() : base(1, 1, XPathResultType.String, new XPathResultType[] { XPathResultType.String }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -789,7 +791,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } // see http://www.w3.org/TR/xslt#function-element-available - private class FuncElementAvailable : XsltFunctionImpl + private sealed class FuncElementAvailable : XsltFunctionImpl { public FuncElementAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -799,7 +801,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } // see: http://www.w3.org/TR/xslt#function-function-available - private class FuncFunctionAvailable : XsltFunctionImpl + private sealed class FuncFunctionAvailable : XsltFunctionImpl { public FuncFunctionAvailable() : base(1, 1, XPathResultType.Boolean, new XPathResultType[] { XPathResultType.String }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -808,7 +810,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncDocument : XsltFunctionImpl + private sealed class FuncDocument : XsltFunctionImpl { public FuncDocument() : base(1, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Any, XPathResultType.NodeSet }) { } @@ -847,7 +849,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncKey : XsltFunctionImpl + private sealed class FuncKey : XsltFunctionImpl { public FuncKey() : base(2, 2, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.String, XPathResultType.Any }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -929,7 +931,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncFormatNumber : XsltFunctionImpl + private sealed class FuncFormatNumber : XsltFunctionImpl { public FuncFormatNumber() : base(2, 3, XPathResultType.String, new XPathResultType[] { XPathResultType.Number, XPathResultType.String, XPathResultType.String }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -939,7 +941,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncNodeSet : XsltFunctionImpl + private sealed class FuncNodeSet : XsltFunctionImpl { public FuncNodeSet() : base(1, 1, XPathResultType.NodeSet, new XPathResultType[] { XPathResultType.Navigator }) { } public override object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext) @@ -948,7 +950,7 @@ public override object Invoke(XsltContext xsltContext, object[] args, XPathNavig } } - private class FuncExtension : XsltFunctionImpl + private sealed class FuncExtension : XsltFunctionImpl { private readonly object _extension; private readonly MethodInfo _method; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltOutput.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltOutput.cs index ee25af7352232..d90674571eb42 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltOutput.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xsl/XsltOld/XsltOutput.cs @@ -10,7 +10,7 @@ namespace System.Xml.Xsl.XsltOld using System.Text; using System.Collections; - internal class XsltOutput : CompiledAction + internal sealed class XsltOutput : CompiledAction { internal enum OutputMethod { diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs index 6ed5b7ace1a74..f9abc50023f24 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XslCompiledTransform.cs @@ -194,7 +194,7 @@ private void CompileQilToMsil(XsltSettings settings) //------------------------------------------------ // Load compiled stylesheet from a Type //------------------------------------------------ - + [RequiresUnreferencedCode("This method will get fields and types from the assembly of the passed in compiledStylesheet and call their constructors which cannot be statically analyzed")] public void Load(Type compiledStylesheet) { Reset(); @@ -238,6 +238,7 @@ public void Load(Type compiledStylesheet) throw new ArgumentException(SR.Format(SR.Xslt_NotCompiledStylesheet, compiledStylesheet.FullName), nameof(compiledStylesheet)); } + [RequiresUnreferencedCode("This method will call into constructors of the earlyBoundTypes array which cannot be statically analyzed.")] public void Load(MethodInfo executeMethod, byte[] queryData, Type[]? earlyBoundTypes) { Reset(); diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs index 34ed4a5367fd9..76045522c3088 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Xslt/XsltArgumentList.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; +using System.Diagnostics.CodeAnalysis; namespace System.Xml.Xsl { @@ -16,6 +17,10 @@ public class XsltArgumentList { private readonly Hashtable _parameters = new Hashtable(); private readonly Hashtable _extensions = new Hashtable(); + private const string ExtensionObjectWarning = @"The stylesheet may have calls to methods of the extension object passed in which cannot be statically analyzed " + + "by the trimmer. Ensure all methods that may be called are preserved."; + internal const string ExtensionObjectSuppresion = @"In order for this code path to be hit, a previous call to XsltArgumentList.AddExtensionObject is " + + "required. That method is already annotated as unsafe and throwing a warning, so we can suppress here."; // Used for reporting xsl:message's during execution internal XsltMessageEncounteredEventHandler? xsltMessageEncountered; @@ -27,6 +32,7 @@ public XsltArgumentList() { } return _parameters[new XmlQualifiedName(name, namespaceUri)]; } + [RequiresUnreferencedCode(ExtensionObjectWarning)] public object? GetExtensionObject(string namespaceUri) { return _extensions[namespaceUri]; @@ -43,6 +49,7 @@ public void AddParam(string name, string namespaceUri, object parameter) _parameters.Add(qname, parameter); } + [RequiresUnreferencedCode(ExtensionObjectWarning)] public void AddExtensionObject(string namespaceUri, object extension) { CheckArgumentNull(namespaceUri, nameof(namespaceUri)); diff --git a/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj b/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj index c833fa31e9135..37ce217c591df 100644 --- a/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj +++ b/src/libraries/System.Private.Xml/tests/Misc/System.Xml.Misc.Tests.csproj @@ -2,9 +2,6 @@ XmlMiscTests $(NetCoreAppCurrent) - - true diff --git a/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Private.Xml.TrimmingTests.proj b/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Private.Xml.TrimmingTests.proj index 44cc404e03bb3..89084f6e8620a 100644 --- a/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Private.Xml.TrimmingTests.proj +++ b/src/libraries/System.Private.Xml/tests/TrimmingTests/System.Private.Xml.TrimmingTests.proj @@ -3,6 +3,7 @@ + diff --git a/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSchema.Write.cs b/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSchema.Write.cs new file mode 100644 index 0000000000000..9316dddeb5a42 --- /dev/null +++ b/src/libraries/System.Private.Xml/tests/TrimmingTests/XmlSchema.Write.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; + +class XMLSchemaExamples +{ + public static int Main() + { + + XmlSchema schema = new XmlSchema(); + string expectedSchema = @""; + + // + XmlSchemaElement elementCat = new XmlSchemaElement(); + schema.Items.Add(elementCat); + elementCat.Name = "cat"; + elementCat.SchemaTypeName = new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema"); + + // + XmlSchemaElement elementDog = new XmlSchemaElement(); + schema.Items.Add(elementDog); + elementDog.Name = "dog"; + elementDog.SchemaTypeName = new XmlQualifiedName("string", "http://www.w3.org/2001/XMLSchema"); + + // + XmlSchemaElement elementRedDog = new XmlSchemaElement(); + schema.Items.Add(elementRedDog); + elementRedDog.Name = "redDog"; + elementRedDog.SubstitutionGroup = new XmlQualifiedName("dog"); + + // + XmlSchemaElement elementBrownDog = new XmlSchemaElement(); + schema.Items.Add(elementBrownDog); + elementBrownDog.Name = "brownDog"; + elementBrownDog.SubstitutionGroup = new XmlQualifiedName("dog"); + + // + XmlSchemaElement elementPets = new XmlSchemaElement(); + schema.Items.Add(elementPets); + elementPets.Name = "pets"; + + using (var stream = new MemoryStream()) + { + using (var writer = XmlWriter.Create(stream)) + { + schema.Write(writer, null); + } + + var str = Encoding.UTF8.GetString(stream.ToArray()); + if (str.Equals(expectedSchema, StringComparison.OrdinalIgnoreCase)) + { + return 100; + } + return -1; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Private.Xml/tests/Writers/XmlWriterApi/TCFullEndElement.cs b/src/libraries/System.Private.Xml/tests/Writers/XmlWriterApi/TCFullEndElement.cs index 2f60bbf13cf01..0e2e261ea494d 100644 --- a/src/libraries/System.Private.Xml/tests/Writers/XmlWriterApi/TCFullEndElement.cs +++ b/src/libraries/System.Private.Xml/tests/Writers/XmlWriterApi/TCFullEndElement.cs @@ -5755,7 +5755,14 @@ public void BinHex_9(XmlWriterUtils utils) w.WriteBinHex(Wbase64, 0, (int)Wbase64len); w.WriteEndElement(); } - Assert.True(utils.CompareReader("")); + if (System.BitConverter.IsLittleEndian) + { + Assert.True(utils.CompareReader("")); + } + else + { + Assert.True(utils.CompareReader("")); + } } // Call WriteBinHex and verify results can be read as a string @@ -5777,7 +5784,14 @@ public void BinHex_10(XmlWriterUtils utils) w.WriteBinHex(Wbase64, 0, (int)Wbase64len); w.WriteEndElement(); } - Assert.True(utils.CompareReader("610062006300")); + if (System.BitConverter.IsLittleEndian) + { + Assert.True(utils.CompareReader("610062006300")); + } + else + { + Assert.True(utils.CompareReader("006100620063")); + } } } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/EncodeDecodeTests.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/EncodeDecodeTests.cs index ba7f39278e66e..deb5fa7edd8d1 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/EncodeDecodeTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/EncodeDecodeTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -47,7 +48,8 @@ public int v3() string strUni = string.Empty; for (int i = 0; i < _dbyte.Length; i = i + 2) { - strUni += (BitConverter.ToChar(_dbyte, i)).ToString(); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_dbyte, i, 2)); + strUni += c.ToString(); } CError.WriteLine(strUni + " " + XmlConvert.EncodeName(strUni)); CError.Compare(XmlConvert.EncodeName(strUni), "_xFF71_", "EncodeName"); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests1.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests1.cs index 64180821d65dc..f6ed436805d9e 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests1.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName4() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeName((BitConverter.ToChar(_byte_BaseChar, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_BaseChar, i, 2)); + strEnVal = XmlConvert.EncodeName(c.ToString()); CError.Compare(strEnVal, _Expbyte_BaseChar[i / 2], "Comparison failed at " + i); return TEST_PASS; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests2.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests2.cs index 2d7ca1783f6c7..01fa29d3b09b9 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests2.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests2.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName5() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeNmToken((BitConverter.ToChar(_byte_BaseChar, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_BaseChar, i, 2)); + strEnVal = XmlConvert.EncodeNmToken(c.ToString()); if (_Expbyte_BaseChar[i / 2] != "_x0387_" && _Expbyte_BaseChar[i / 2] != "_x0640_" && _Expbyte_BaseChar[i / 2] != "_x064B_" && _Expbyte_BaseChar[i / 2] != "_x0670_" && _Expbyte_BaseChar[i / 2] != "_x06D6_" && _Expbyte_BaseChar[i / 2] != "_x06E4_" && _Expbyte_BaseChar[i / 2] != "_x06E7_" && _Expbyte_BaseChar[i / 2] != "_x093C_" && _Expbyte_BaseChar[i / 2] != "_x093E_" && _Expbyte_BaseChar[i / 2] != "_x0962_" && _Expbyte_BaseChar[i / 2] != "_x09E2_" && _Expbyte_BaseChar[i / 2] != "_x09EF_" && _Expbyte_BaseChar[i / 2] != "_x0A71_" && _Expbyte_BaseChar[i / 2] != "_x0ABC_" && _Expbyte_BaseChar[i / 2] != "_x0ABE_" && _Expbyte_BaseChar[i / 2] != "_x0B3C_" && _Expbyte_BaseChar[i / 2] != "_x0B3E_" && _Expbyte_BaseChar[i / 2] != "_x0E31_" && _Expbyte_BaseChar[i / 2] != "_x0E34_" && _Expbyte_BaseChar[i / 2] != "_x0E46_" && _Expbyte_BaseChar[i / 2] != "_x0EB1_" && _Expbyte_BaseChar[i / 2] != "_x0EB4_" && _Expbyte_BaseChar[i / 2] != "_x0EBC_" && _Expbyte_BaseChar[i / 2] != "_x0F3F_") { diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests3.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests3.cs index a5492547492a1..33400fba8b67a 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests3.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlBaseCharConvertTests3.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -28,7 +29,8 @@ public int XmlEncodeName6() string strDeVal = string.Empty; string strVal = string.Empty; - strVal = (BitConverter.ToChar(_byte_BaseChar, i)).ToString(); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_BaseChar, i, 2)); + strVal = c.ToString(); strEnVal = XmlConvert.EncodeName(strVal); CError.Compare(strEnVal, _Expbyte_BaseChar[i / 2], "Encode Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests1.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests1.cs index 9f7a72896e9a8..668791afd5ddb 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests1.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName1() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeName((BitConverter.ToChar(_byte_CombiningChar, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_CombiningChar, i, 2)); + strEnVal = XmlConvert.EncodeName(c.ToString()); CError.Compare(strEnVal, _Expbyte_CombiningChar[i / 2], "Comparison failed at " + i); return TEST_PASS; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests2.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests2.cs index 614e4e555550e..4eed8aa1a4bf3 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests2.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests2.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName2() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeNmToken((BitConverter.ToChar(_byte_CombiningChar, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_CombiningChar, i, 2)); + strEnVal = XmlConvert.EncodeNmToken(c.ToString()); if (_Expbyte_CombiningChar[i / 2] != "_x0A6F_" && _Expbyte_CombiningChar[i / 2] != "_x0E46_") { CError.Compare(strEnVal, _Expbyte_CombiningChar[i / 2], "Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests3.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests3.cs index 3567d3fc0170f..93c96521d90b8 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests3.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlCombiningCharConvertTests3.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -28,7 +29,8 @@ public int XmlEncodeName3() string strEnVal = string.Empty; string strVal = string.Empty; - strVal = (BitConverter.ToChar(_byte_CombiningChar, i)).ToString(); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_CombiningChar, i, 2)); + strVal = c.ToString(); strEnVal = XmlConvert.EncodeName(strVal); CError.Compare(strEnVal, _Expbyte_CombiningChar[i / 2], "Encode Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests1.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests1.cs index dfe456c438ef1..825c923f3d4ae 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests1.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName1() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeName((BitConverter.ToChar(_byte_Digit, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_Digit, i, 2)); + strEnVal = XmlConvert.EncodeName(c.ToString()); CError.Compare(strEnVal, _Expbyte_Digit[i / 2], "Comparison failed at " + i); return TEST_PASS; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests2.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests2.cs index 9a5c3dfead0f9..01784cb330cdf 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests2.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests2.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName2() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeNmToken((BitConverter.ToChar(_byte_Digit, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_Digit, i, 2)); + strEnVal = XmlConvert.EncodeNmToken(c.ToString()); if (_Expbyte_Digit[i / 2] != "_x0A70_") { CError.Compare(strEnVal, _Expbyte_Digit[i / 2], "Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests3.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests3.cs index 3be2e9359af28..b9eeda41232f0 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests3.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlDigitCharConvertTests3.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -28,7 +29,8 @@ public int XmlEncodeName3() string strEnVal = string.Empty; string strVal = string.Empty; - strVal = (BitConverter.ToChar(_byte_Digit, i)).ToString(); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_Digit, i, 2)); + strVal = c.ToString(); strEnVal = XmlConvert.EncodeName(strVal); CError.Compare(strEnVal, _Expbyte_Digit[i / 2], "Encode Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests1.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests1.cs index 9d3219207e96d..21ca58a0cf6b6 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests1.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName1() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeName((BitConverter.ToChar(_byte_EmbeddedNull, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_EmbeddedNull, i, 2)); + strEnVal = XmlConvert.EncodeName(c.ToString()); CError.Compare(strEnVal, _Expbyte_EmbeddedNull[i / 2], "Comparison failed at " + i); return TEST_PASS; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests2.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests2.cs index ffd428e4f051b..c5e15bd7bcc6c 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests2.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests2.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName2() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeNmToken((BitConverter.ToChar(_byte_EmbeddedNull, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_EmbeddedNull, i, 2)); + strEnVal = XmlConvert.EncodeNmToken(c.ToString()); CError.Compare(strEnVal, _Expbyte_EmbeddedNull[i / 2], "Comparison failed at " + i); return TEST_PASS; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests3.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests3.cs index 9f078c086a624..cead014f7c823 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests3.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlEmbeddedNullCharConvertTests3.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -28,7 +29,8 @@ public int XmlEncodeName3() string strEnVal = string.Empty; string strVal = string.Empty; - strVal = (BitConverter.ToChar(_byte_EmbeddedNull, i)).ToString(); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_EmbeddedNull, i, 2)); + strVal = c.ToString(); strEnVal = XmlConvert.EncodeName(strVal); CError.Compare(strEnVal, _Expbyte_EmbeddedNull[i / 2], "Encode Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests1.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests1.cs index 2ce9b6ace1cef..c33333f6ffe13 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests1.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests1.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeName((BitConverter.ToChar(_byte_Ideographic, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_Ideographic, i, 2)); + strEnVal = XmlConvert.EncodeName(c.ToString()); CError.Compare(strEnVal, _Expbyte_Ideographic[i / 2], "Comparison failed at " + i); return TEST_PASS; } diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests2.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests2.cs index ee1be130945af..54aa5c7f5c851 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests2.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests2.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -26,7 +27,8 @@ public int XmlEncodeName() int i = ((CurVariation.id) - 1) * 2; string strEnVal = string.Empty; - strEnVal = XmlConvert.EncodeNmToken((BitConverter.ToChar(_byte_Ideographic, i)).ToString()); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_Ideographic, i, 2)); + strEnVal = XmlConvert.EncodeNmToken(c.ToString()); if (_Expbyte_Ideographic[i / 2] != "_x302A_") { CError.Compare(strEnVal, _Expbyte_Ideographic[i / 2], "Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests3.cs b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests3.cs index 101ed2ad39e1c..f57c2bc80a73b 100644 --- a/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests3.cs +++ b/src/libraries/System.Private.Xml/tests/XmlConvert/XmlIdeographicCharConvertTests3.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using OLEDB.Test.ModuleCore; +using System.Buffers.Binary; namespace System.Xml.Tests { @@ -28,7 +29,8 @@ public int XmlEncodeName() string strEnVal = string.Empty; string strVal = string.Empty; - strVal = (BitConverter.ToChar(_byte_Ideographic, i)).ToString(); + char c = (char)BinaryPrimitives.ReadUInt16LittleEndian(new Span(_byte_Ideographic, i, 2)); + strVal = c.ToString(); strEnVal = XmlConvert.EncodeName(strVal); CError.Compare(strEnVal, _Expbyte_Ideographic[i / 2], "Encode Comparison failed at " + i); diff --git a/src/libraries/System.Private.Xml/tests/XmlReader/Tests/ReaderEncodingTests.cs b/src/libraries/System.Private.Xml/tests/XmlReader/Tests/ReaderEncodingTests.cs index 70af3c32a85f0..0c91c2b2e27b3 100644 --- a/src/libraries/System.Private.Xml/tests/XmlReader/Tests/ReaderEncodingTests.cs +++ b/src/libraries/System.Private.Xml/tests/XmlReader/Tests/ReaderEncodingTests.cs @@ -80,7 +80,7 @@ public static void BytesEndingWithSurrogateChar() Assert.True(reader.Read()); Assert.True(reader.Read()); - XmlException ex = Assert.Throws(() => reader.Read());; + XmlException ex = Assert.Throws(() => reader.Read()); } [Fact] diff --git a/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs b/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs index fc89feabd262a..70c84ffe6605b 100644 --- a/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs +++ b/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs @@ -237,7 +237,7 @@ public bool CheckCanReadBinaryContent() try { int nBytes = 0; - switch ((int)new Random().Next(4)) + switch ((int)Random.Shared.Next(4)) { case 0: CError.WriteLineIgnore("Selecting RCABH"); diff --git a/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs b/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs index ae30ed823485e..b937ba14f7d38 100644 --- a/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs +++ b/src/libraries/System.Private.Xml/tests/XmlSchema/XmlSchemaValidatorApi/ValidateMisc.cs @@ -669,7 +669,7 @@ public void XmlSchemaSetCompileAfterRemovingLastSchemaInTheSetIsNotClearingCache try { while (xmlReader.Read()) ; - Assert.True(false); ; + Assert.True(false); } catch (XmlSchemaValidationException e) { diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj index d1d40a593739a..2349cc2a966b3 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslCompiledTransformApi/System.Xml.Xsl.XslCompiledTransformApi.Tests.csproj @@ -1,9 +1,6 @@ $(NetCoreAppCurrent) - - true diff --git a/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs b/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs index 2a71cb87072ef..141c830925b1b 100644 --- a/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs +++ b/src/libraries/System.Private.Xml/tests/Xslt/XslTransformApi/CXslTransform.cs @@ -871,6 +871,34 @@ public void LoadGeneric12(InputType inputType, ReaderType readerType) _output.WriteLine("Did not throw compile exception for stylesheet"); Assert.True(false); } + + [Fact] + public void XslTransformThrowsPNSEWhenUsingScripts() + { + using StringReader xslFile = new StringReader( + @" + + + + + + + + +"); + + using XmlReader reader = XmlReader.Create(xslFile); + XslTransform xslt = new XslTransform(); + XsltCompileException compilationException = Assert.Throws(() => xslt.Load(reader)); + Assert.True(compilationException.InnerException != null && compilationException.InnerException is PlatformNotSupportedException); + } } /**************************************************************************/ diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs index c78e0e401d4e7..1b7e20a4c5614 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/AttributeUtils.cs @@ -6,7 +6,7 @@ namespace System.Reflection.Context.Custom { - internal class AttributeUtils + internal static class AttributeUtils { public static object[] GetCustomAttributes(CustomReflectionContext context, CustomType type, Type attributeFilterType, bool inherit) { diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomAssembly.cs index a6d9f249b6b5d..3576d5d60427d 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomAssembly.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomAssembly : ProjectingAssembly + internal sealed class CustomAssembly : ProjectingAssembly { public CustomAssembly(Assembly template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomConstructorInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomConstructorInfo.cs index c036fb11d9762..f1a65cf18ec8c 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomConstructorInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomConstructorInfo.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomConstructorInfo : ProjectingConstructorInfo + internal sealed class CustomConstructorInfo : ProjectingConstructorInfo { public CustomConstructorInfo(ConstructorInfo template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomEventInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomEventInfo.cs index 3ca8423cd26ca..e14c82ef5616f 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomEventInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomEventInfo.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomEventInfo : ProjectingEventInfo + internal sealed class CustomEventInfo : ProjectingEventInfo { public CustomEventInfo(EventInfo template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomFieldInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomFieldInfo.cs index d4f2b7f637487..764a9dcb46a9f 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomFieldInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomFieldInfo.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomFieldInfo : ProjectingFieldInfo + internal sealed class CustomFieldInfo : ProjectingFieldInfo { public CustomFieldInfo(FieldInfo template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomMethodInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomMethodInfo.cs index 964830b5388b2..c73d0d65efc82 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomMethodInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomMethodInfo.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomMethodInfo : ProjectingMethodInfo + internal sealed class CustomMethodInfo : ProjectingMethodInfo { public CustomMethodInfo(MethodInfo template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomModule.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomModule.cs index abf6f1c449c5a..05ec843cc81ce 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomModule.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomModule.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomModule : ProjectingModule + internal sealed class CustomModule : ProjectingModule { public CustomModule(Module template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomParameterInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomParameterInfo.cs index f09098c12214c..ba2229c3ee730 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomParameterInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomParameterInfo.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomParameterInfo : ProjectingParameterInfo + internal sealed class CustomParameterInfo : ProjectingParameterInfo { public CustomParameterInfo(ParameterInfo template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomPropertyInfo.cs index cac49fff02aa9..396404af651dc 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomPropertyInfo.cs @@ -5,7 +5,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomPropertyInfo : ProjectingPropertyInfo + internal sealed class CustomPropertyInfo : ProjectingPropertyInfo { public CustomPropertyInfo(PropertyInfo template, CustomReflectionContext context) : base(template, context.Projector) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs index bf6d6ffd60a96..ea2d3ae2ee553 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Context.Custom { - internal class CustomType : ProjectingType + internal sealed class CustomType : ProjectingType { private IEnumerable _newProperties; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs index 6e4f4870d13ba..95e33d7173f8c 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.Projector.cs @@ -9,7 +9,7 @@ namespace System.Reflection.Context { public partial class CustomReflectionContext { - private class ReflectionContextProjector : Projector + private sealed class ReflectionContextProjector : Projector { public ReflectionContextProjector(CustomReflectionContext context) { diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs index 2676cf631b558..4ff6bef37c016 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/CustomReflectionContext.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Context { - internal class IdentityReflectionContext : ReflectionContext + internal sealed class IdentityReflectionContext : ReflectionContext { public override Assembly MapAssembly(Assembly assembly) { return assembly; } public override TypeInfo MapType(TypeInfo type) { return type; } diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingCustomAttributeData.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingCustomAttributeData.cs index ac392a35b01a2..d086cef1452fd 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingCustomAttributeData.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingCustomAttributeData.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Context.Projection { // Recursively 'projects' any assemblies, modules, types and members returned by a given custom attribute data - internal class ProjectingCustomAttributeData : DelegatingCustomAttributeData + internal sealed class ProjectingCustomAttributeData : DelegatingCustomAttributeData { private readonly Projector _projector; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs index 255b082f6af44..bcc74828a26b1 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingExceptionHandlingClause.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Projection { // Recursively 'projects' any assemblies, modules, types and members returned by a given exception handling clause - internal class ProjectingExceptionHandlingClause : DelegatingExceptionHandlingClause + internal sealed class ProjectingExceptionHandlingClause : DelegatingExceptionHandlingClause { private readonly Projector _projector; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingLocalVariableInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingLocalVariableInfo.cs index e1003de06052d..0e8d3d170787f 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingLocalVariableInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingLocalVariableInfo.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Projection { // Recursively 'projects' any assemblies, modules, types and members returned by a given variable - internal class ProjectingLocalVariableInfo : DelegatingLocalVariableInfo + internal sealed class ProjectingLocalVariableInfo : DelegatingLocalVariableInfo { private readonly Projector _projector; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs index 734efef1ff051..32b9b1831dfc2 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingManifestResourceInfo.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Projection { // Recursively 'projects' any assemblies, modules, types and members returned by a given resource - internal class ProjectingManifestResourceInfo : DelegatingManifestResourceInfo + internal sealed class ProjectingManifestResourceInfo : DelegatingManifestResourceInfo { private readonly Projector _projector; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodBody.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodBody.cs index 28e94fa661fb8..5d137e403fc24 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodBody.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingMethodBody.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Context.Projection { // Recursively 'projects' any assemblies, modules, types and members returned by a given method body - internal class ProjectingMethodBody : DelegatingMethodBody + internal sealed class ProjectingMethodBody : DelegatingMethodBody { private readonly Projector _projector; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs index 734fbe4f8f1d1..f366517170a98 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedMethodInfo.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Virtual { // Represents a inherited method which is identical to the base method except for its ReflectedType. - internal partial class InheritedMethodInfo : DelegatingMethodInfo + internal sealed partial class InheritedMethodInfo : DelegatingMethodInfo { private readonly Type _reflectedType; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs index bc2cc4c67aa89..a4a142399f7e3 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/InheritedPropertyInfo.cs @@ -7,7 +7,7 @@ namespace System.Reflection.Context.Virtual { // Represents a inherited property which is identical to the base property except for its ReflectedType. - internal partial class InheritedPropertyInfo : DelegatingPropertyInfo + internal sealed partial class InheritedPropertyInfo : DelegatingPropertyInfo { private readonly Type _reflectedType; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs index 4c3c1ee064321..86caee374ed47 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertyGetter.cs @@ -8,9 +8,9 @@ namespace System.Reflection.Context.Virtual { - internal partial class VirtualPropertyInfo + internal sealed partial class VirtualPropertyInfo { - private class PropertyGetter : PropertyGetterBase + private sealed class PropertyGetter : PropertyGetterBase { private readonly Func _getter; private readonly IEnumerable _attributes; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs index 49b7251b8631b..f7b3878d8f416 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.PropertySetter.cs @@ -8,9 +8,9 @@ namespace System.Reflection.Context.Virtual { - internal partial class VirtualPropertyInfo + internal sealed partial class VirtualPropertyInfo { - private class PropertySetter : PropertySetterBase + private sealed class PropertySetter : PropertySetterBase { private readonly Action _setter; private readonly ParameterInfo _valueParameter; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs index ea21ece64221b..e56f1837c87e4 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualPropertyInfo.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Context.Virtual { // Represents a func-based 'PropertyInfo' - internal partial class VirtualPropertyInfo : VirtualPropertyBase + internal sealed partial class VirtualPropertyInfo : VirtualPropertyBase { private readonly PropertyGetter _getter; private readonly PropertySetter _setter; diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs index 0f0b8d7b729a9..ac2e91834a413 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Virtual/VirtualReturnParameter.cs @@ -4,7 +4,7 @@ namespace System.Reflection.Context.Virtual { // Represents the 'return' parameter for a method - internal class VirtualReturnParameter : VirtualParameter + internal sealed class VirtualReturnParameter : VirtualParameter { public VirtualReturnParameter(MethodInfo method) : base(method, method.ReturnType, name:null, position: -1) diff --git a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs index bc8fbd44bfe10..6cb945ca3a382 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs +++ b/src/libraries/System.Reflection.DispatchProxy/src/System/Reflection/DispatchProxyGenerator.cs @@ -145,7 +145,7 @@ private static GeneratedTypeInfo GenerateProxyType( return generatedProxyType; } - private class GeneratedTypeInfo + private sealed class GeneratedTypeInfo { public GeneratedTypeInfo( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type generatedType, @@ -160,7 +160,7 @@ public GeneratedTypeInfo( public MethodInfo[] MethodInfos { get; } } - private class ProxyAssembly + private sealed class ProxyAssembly { private readonly AssemblyBuilder _ab; private readonly ModuleBuilder _mb; @@ -232,7 +232,7 @@ internal void EnsureTypeIsVisible(Type type) } } - private class ProxyBuilder + private sealed class ProxyBuilder { private readonly ProxyAssembly _assembly; private readonly TypeBuilder _tb; @@ -718,7 +718,7 @@ private static void Stind(ILGenerator il, Type type) } } - private class ParametersArray + private sealed class ParametersArray { private readonly ILGenerator _il; private readonly Type[] _paramTypes; @@ -747,7 +747,7 @@ internal void EndSet(int i, Type stackType) } } - private class GenericArray + private sealed class GenericArray { private readonly ILGenerator _il; private readonly LocalBuilder _lb; diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs index 438b24b2e4ba0..3940c1559b804 100644 --- a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs +++ b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Text; @@ -38,7 +39,7 @@ public void EmitMethodInfo() MethodInfo genMethod = type.GetMethod("Get"); byte[] il = genMethod.GetMethodBody().GetILAsByteArray(); - int ilMethodMetadataToken = BitConverter.ToInt32(il, 1); + int ilMethodMetadataToken = BinaryPrimitives.ReadInt32LittleEndian(new Span(il, 1, 4)); MethodBase resolvedMethod = type.Module.ResolveMethod(ilMethodMetadataToken); Assert.Equal(method, resolvedMethod); var methodBase = (MethodBase)genMethod.Invoke(null, null); diff --git a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj index 2417558b7857a..e099ac354b03f 100644 --- a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj +++ b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj @@ -1,4 +1,4 @@ - + true en-US @@ -15,9 +15,11 @@ + Link="Common\Interop\Windows\Interop.Libraries.cs" + Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)' "/> + Link="Common\Interop\Windows\kernel32\Interop.ReadFile_SafeHandle_IntPtr.cs" + Condition="'$(TargetFramework)' != '$(NetCoreAppCurrent)' "/> @@ -102,8 +104,9 @@ - + + diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs index 7581e90f0cb4f..1c9f68da95e0b 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/MemoryBlocks/StreamMemoryBlockProvider.cs @@ -76,9 +76,11 @@ internal static unsafe NativeHeapMemoryBlock ReadMemoryBlockNoLock(Stream stream { stream.Seek(start, SeekOrigin.Begin); - if (!isFileStream || !FileStreamReadLightUp.TryReadFile(stream, block.Pointer, start, size)) + int bytesRead = 0; + + if (!isFileStream || (bytesRead = FileStreamReadLightUp.ReadFile(stream, block.Pointer, size)) != size) { - stream.CopyTo(block.Pointer, size); + stream.CopyTo(block.Pointer + bytesRead, size - bytesRead); } fault = false; diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.cs deleted file mode 100644 index 99539fc8f955e..0000000000000 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; - -namespace System.Reflection.Internal -{ - internal static class FileStreamReadLightUp - { - // internal for testing - internal static bool readFileNotAvailable = Path.DirectorySeparatorChar != '\\'; // Available on Windows only - internal static bool safeFileHandleNotAvailable; - - internal static bool IsFileStream(Stream stream) => stream is FileStream; - - internal static SafeHandle? GetSafeFileHandle(Stream stream) - { - SafeHandle handle; - try - { - handle = ((FileStream)stream).SafeFileHandle; - } - catch - { - // Some FileStream implementations (e.g. IsolatedStorage) restrict access to the underlying handle by throwing - // Tolerate it and fall back to slow path. - return null; - } - - if (handle != null && handle.IsInvalid) - { - // Also allow for FileStream implementations that do return a non-null, but invalid underlying OS handle. - // This is how brokered files on WinRT will work. Fall back to slow path. - return null; - } - - return handle; - } - - internal static unsafe bool TryReadFile(Stream stream, byte* buffer, long start, int size) - { - if (readFileNotAvailable) - { - return false; - } - - SafeHandle? handle = GetSafeFileHandle(stream); - if (handle == null) - { - return false; - } - - int result; - int bytesRead; - - try - { - result = Interop.Kernel32.ReadFile(handle, buffer, size, out bytesRead, IntPtr.Zero); - } - catch - { - readFileNotAvailable = true; - return false; - } - - if (result == 0 || bytesRead != size) - { - // We used to throw here, but this is where we land if the FileStream was - // opened with useAsync: true, which is currently the default on .NET Core. - // https://github.com/dotnet/corefx/pull/987 filed to look in to how best to - // handle this, but in the meantime, we'll fall back to the slower code path - // just as in the case where the native API is unavailable in the current platform. - return false; - } - - return true; - } - } -} diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs new file mode 100644 index 0000000000000..aabc6739c7157 --- /dev/null +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netcoreapp.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; + +namespace System.Reflection.Internal +{ + internal static class FileStreamReadLightUp + { + internal static bool IsFileStream(Stream stream) => stream is FileStream; + + internal static unsafe int ReadFile(Stream stream, byte* buffer, int size) + => stream.Read(new Span(buffer, size)); + } +} diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs index 1bcc69e7a54c4..774def66573f7 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard1.1.cs @@ -86,43 +86,29 @@ internal static SafeHandle GetSafeFileHandle(Stream stream) return handle; } - internal static unsafe bool TryReadFile(Stream stream, byte* buffer, long start, int size) + internal static unsafe int ReadFile(Stream stream, byte* buffer, int size) { if (readFileNotAvailable) { - return false; + return 0; } SafeHandle handle = GetSafeFileHandle(stream); if (handle == null) { - return false; + return 0; } - int result; - int bytesRead; - try { - result = Interop.Kernel32.ReadFile(handle, buffer, size, out bytesRead, IntPtr.Zero); + int result = Interop.Kernel32.ReadFile(handle, buffer, size, out int bytesRead, IntPtr.Zero); + return result == 0 ? 0 : bytesRead; } catch { readFileNotAvailable = true; - return false; + return 0; } - - if (result == 0 || bytesRead != size) - { - // We used to throw here, but this is where we land if the FileStream was - // opened with useAsync: true, which is currently the default on .NET Core. - // https://github.com/dotnet/corefx/pull/987 filed to look in to how best to - // handle this, but in the meantime, we'll fall back to the slower code path - // just as in the case where the native API is unavailable in the current platform. - return false; - } - - return true; } } } diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard2.0.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard2.0.cs new file mode 100644 index 0000000000000..2416a723f6c02 --- /dev/null +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/FileStreamReadLightUp.netstandard2.0.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Runtime.InteropServices; + +namespace System.Reflection.Internal +{ + internal static class FileStreamReadLightUp + { + private static bool IsReadFileAvailable => Path.DirectorySeparatorChar == '\\'; + + internal static bool IsFileStream(Stream stream) => stream is FileStream; + + private static SafeHandle? GetSafeFileHandle(Stream stream) + { + SafeHandle handle; + try + { + handle = ((FileStream)stream).SafeFileHandle; + } + catch + { + // Some FileStream implementations (e.g. IsolatedStorage) restrict access to the underlying handle by throwing + // Tolerate it and fall back to slow path. + return null; + } + + if (handle != null && handle.IsInvalid) + { + // Also allow for FileStream implementations that do return a non-null, but invalid underlying OS handle. + // This is how brokered files on WinRT will work. Fall back to slow path. + return null; + } + + return handle; + } + + internal static unsafe int ReadFile(Stream stream, byte* buffer, int size) + { + if (!IsReadFileAvailable) + { + return 0; + } + + SafeHandle? handle = GetSafeFileHandle(stream); + if (handle == null) + { + return 0; + } + + int result = Interop.Kernel32.ReadFile(handle, buffer, size, out int bytesRead, IntPtr.Zero); + return result == 0 ? 0 : bytesRead; + } + } +} diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/PooledStringBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/PooledStringBuilder.cs index 05ab60bb2baf8..74118090fad57 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/PooledStringBuilder.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/PooledStringBuilder.cs @@ -14,7 +14,7 @@ namespace System.Reflection.Internal /// ... sb.ToString() ... /// inst.Free(); /// - internal class PooledStringBuilder + internal sealed class PooledStringBuilder { public readonly StringBuilder Builder = new StringBuilder(); private readonly ObjectPool _pool; diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobBuilder.cs index a5bb62623e0a0..cd56fe02ca60f 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobBuilder.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobBuilder.cs @@ -956,10 +956,9 @@ public unsafe void WriteUTF16(char[] value) } else { - byte[] bytes = Encoding.Unicode.GetBytes(value); - fixed (byte* ptr = &bytes[0]) + for (int i = 0; i < value.Length; i++) { - WriteBytesUnchecked((byte*)ptr, bytes.Length); + WriteUInt16((ushort)value[i]); } } } @@ -990,10 +989,9 @@ public unsafe void WriteUTF16(string value) } else { - byte[] bytes = Encoding.Unicode.GetBytes(value); - fixed (byte* ptr = bytes) + for (int i = 0; i < value.Length; i++) { - WriteBytesUnchecked((byte*)ptr, bytes.Length); + WriteUInt16((ushort)value[i]); } } } diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs index 270c911213469..0ca5a419f68cb 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs @@ -52,7 +52,7 @@ public static BlobContentId FromHash(ImmutableArray hashCode) return FromHash(ImmutableByteArrayInterop.DangerousGetUnderlyingArray(hashCode)!); } - public static unsafe BlobContentId FromHash(byte[] hashCode) + public static BlobContentId FromHash(byte[] hashCode) { const int minHashSize = 20; @@ -66,16 +66,23 @@ public static unsafe BlobContentId FromHash(byte[] hashCode) throw new ArgumentException(SR.Format(SR.HashTooShort, minHashSize), nameof(hashCode)); } - Guid guid = default(Guid); - byte* guidPtr = (byte*)&guid; - for (var i = 0; i < BlobUtilities.SizeOfGuid; i++) - { - guidPtr[i] = hashCode[i]; - } + // extract guid components from input data + uint a = ((uint)hashCode[3] << 24 | (uint)hashCode[2] << 16 | (uint)hashCode[1] << 8 | hashCode[0]); + ushort b = (ushort)((ushort)hashCode[5] << 8 | (ushort)hashCode[4]); + ushort c = (ushort)((ushort)hashCode[7] << 8 | (ushort)hashCode[6]); + byte d = hashCode[8]; + byte e = hashCode[9]; + byte f = hashCode[10]; + byte g = hashCode[11]; + byte h = hashCode[12]; + byte i = hashCode[13]; + byte j = hashCode[14]; + byte k = hashCode[15]; // modify the guid data so it decodes to the form of a "random" guid ala rfc4122 - guidPtr[7] = (byte)((guidPtr[7] & 0x0f) | (4 << 4)); - guidPtr[8] = (byte)((guidPtr[8] & 0x3f) | (2 << 6)); + c = (ushort)((c & 0x0fff) | (4 << 12)); + d = (byte)((d & 0x3f) | (2 << 6)); + Guid guid = new Guid((int)a, (short)b, (short)c, d, e, f, g, h, i, j, k); // compute a random-looking stamp from the remaining bits, but with the upper bit set uint stamp = 0x80000000u | ((uint)hashCode[19] << 24 | (uint)hashCode[18] << 16 | (uint)hashCode[17] << 8 | hashCode[16]); diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobReader.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobReader.cs index e7f05ceb42e95..ce1299e6f267b 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobReader.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobReader.cs @@ -12,9 +12,6 @@ namespace System.Reflection.Metadata [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] public unsafe struct BlobReader { - /// An array containing the '\0' character. - private static readonly char[] s_nullCharArray = new char[1] { '\0' }; - internal const int InvalidCompressedInteger = int.MaxValue; private readonly MemoryBlock _block; @@ -591,9 +588,7 @@ public SignatureTypeCode ReadSignatureTypeCode() int length; if (TryReadCompressedInteger(out length)) { - // Removal of trailing '\0' is a departure from the spec, but required - // for compatibility with legacy compilers. - return ReadUTF8(length).TrimEnd(s_nullCharArray); + return ReadUTF8(length); } if (ReadByte() != 0xFF) diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriter.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriter.cs index f8f1df5ae3155..5ae45500de884 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriter.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobWriter.cs @@ -381,9 +381,19 @@ public void WriteUTF16(char[] value) return; } - fixed (char* ptr = &value[0]) + if (BitConverter.IsLittleEndian) { - WriteBytesUnchecked((byte*)ptr, value.Length * sizeof(char)); + fixed (char* ptr = &value[0]) + { + WriteBytesUnchecked((byte*)ptr, value.Length * sizeof(char)); + } + } + else + { + for (int i = 0; i < value.Length; i++) + { + WriteUInt16((ushort)value[i]); + } } } @@ -398,9 +408,19 @@ public void WriteUTF16(string value) Throw.ArgumentNull(nameof(value)); } - fixed (char* ptr = value) + if (BitConverter.IsLittleEndian) { - WriteBytesUnchecked((byte*)ptr, value.Length * sizeof(char)); + fixed (char* ptr = value) + { + WriteBytesUnchecked((byte*)ptr, value.Length * sizeof(char)); + } + } + else + { + for (int i = 0; i < value.Length; i++) + { + WriteUInt16((ushort)value[i]); + } } } diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs index f0f044d609a26..23cc2ac2358c0 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataAggregator.cs @@ -28,7 +28,7 @@ public int CompareTo(RowCounts other) public override string ToString() { - return string.Format("+0x{0:x} ~0x{1:x}", AggregateInserts, Updates); + return $"+0x{AggregateInserts:x} ~0x{Updates:x}"; } } diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/NamespaceCache.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/NamespaceCache.cs index d01611cd81602..6aa8c6eae8dcc 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/NamespaceCache.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Internal/NamespaceCache.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Metadata.Ecma335 { - internal class NamespaceCache + internal sealed class NamespaceCache { private readonly MetadataReader _metadataReader; private readonly object _namespaceTableAndListLock = new object(); @@ -406,7 +406,7 @@ private void EnsureNamespaceTableIsPopulated() /// This class assumes that the builders will not be modified in any way after the first call to /// Freeze(). /// - private class NamespaceDataBuilder + private sealed class NamespaceDataBuilder { public readonly NamespaceDefinitionHandle Handle; public readonly StringHandle Name; diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs index 112359f45462e..a2de3a185fad0 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEBuilder.cs @@ -549,7 +549,8 @@ private static unsafe uint CalculateChecksum(IEnumerable blobs) while (ptr < end) { - checksum = AggregateChecksum(checksum, *(ushort*)ptr); + // little-endian encoding: + checksum = AggregateChecksum(checksum, (ushort)(ptr[1] << 8 | ptr[0])); ptr += sizeof(ushort); } } diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/BlobContentIdTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/BlobContentIdTests.cs index 8e9d1df26a66a..f6c58e74517fd 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/BlobContentIdTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/BlobContentIdTests.cs @@ -64,17 +64,17 @@ public void FromHash() 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)); AssertEx.Equal(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x48, 0x89, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 }, id1.Guid.ToByteArray()); - AssertEx.Equal(new byte[] { 0x11, 0x12, 0x13, 0x94 }, BitConverter.GetBytes(id1.Stamp)); + Assert.Equal(0x94131211u, id1.Stamp); var id2 = BlobContentId.FromHash(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); AssertEx.Equal(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, id2.Guid.ToByteArray()); - AssertEx.Equal(new byte[] { 0x00, 0x00, 0x00, 0x80 }, BitConverter.GetBytes(id2.Stamp)); + Assert.Equal(0x80000000u, id2.Stamp); var id3 = BlobContentId.FromHash(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); AssertEx.Equal(new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4f, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, id3.Guid.ToByteArray()); - AssertEx.Equal(new byte[] { 0xff, 0xff, 0xff, 0xff }, BitConverter.GetBytes(id3.Stamp)); + Assert.Equal(0xffffffffu, id3.Stamp); Assert.Throws(() => BlobContentId.FromHash(default(ImmutableArray))); Assert.Throws(() => BlobContentId.FromHash(null)); diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataAggregatorTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataAggregatorTests.cs index 03285f9e04c3f..6d52f318fd743 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataAggregatorTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/Ecma335/MetadataAggregatorTests.cs @@ -10,14 +10,14 @@ namespace System.Reflection.Metadata.Ecma335.Tests { public class MetadataAggregatorTests { - private static unsafe EnCMapTableReader CreateEncMapTable(int[] tokens) + private static unsafe EnCMapTableReader CreateEncMapTable(byte[] tokens) { GCHandle handle = GCHandle.Alloc(tokens, GCHandleType.Pinned); - var block = new MemoryBlock((byte*)handle.AddrOfPinnedObject(), tokens.Length * sizeof(uint)); - return new EnCMapTableReader(tokens.Length, block, containingBlockOffset: 0); + var block = new MemoryBlock((byte*)handle.AddrOfPinnedObject(), tokens.Length); + return new EnCMapTableReader(tokens.Length / sizeof(uint), block, containingBlockOffset: 0); } - private static EnCMapTableReader[] CreateEncMapTables(int[][] tables) + private static EnCMapTableReader[] CreateEncMapTables(byte[][] tables) { var result = new EnCMapTableReader[tables.Length]; @@ -47,35 +47,35 @@ public void RowCounts() { var encMaps = new[] { - new[] // Gen1 + new byte[] // Gen1 { - 0x0100009c, - 0x0200002e, - 0x0600009e, - 0x0600009f, - 0x23000011, + 0x9c, 0x00, 0x00, 0x01, + 0x2e, 0x00, 0x00, 0x02, + 0x9e, 0x00, 0x00, 0x06, + 0x9f, 0x00, 0x00, 0x06, + 0x11, 0x00, 0x00, 0x23, }, - new[] // Gen2 + new byte[] // Gen2 { - 0x0100009d, - 0x06000075, - 0x1700001a, - 0x18000037, - 0x18000038, - 0x23000012, - 0x23000013, + 0x9d, 0x00, 0x00, 0x01, + 0x75, 0x00, 0x00, 0x06, + 0x1a, 0x00, 0x00, 0x17, + 0x37, 0x00, 0x00, 0x18, + 0x38, 0x00, 0x00, 0x18, + 0x12, 0x00, 0x00, 0x23, + 0x13, 0x00, 0x00, 0x23, }, - new[] // Gen3 + new byte[] // Gen3 { - 0x0100009e, - 0x0100009f, - 0x06000075, - 0x11000031, - 0x1700001a, - 0x18000039, - 0x1800003a, - 0x23000014, - 0x23000015, + 0x9e, 0x00, 0x00, 0x01, + 0x9f, 0x00, 0x00, 0x01, + 0x75, 0x00, 0x00, 0x06, + 0x31, 0x00, 0x00, 0x11, + 0x1a, 0x00, 0x00, 0x17, + 0x39, 0x00, 0x00, 0x18, + 0x3a, 0x00, 0x00, 0x18, + 0x14, 0x00, 0x00, 0x23, + 0x15, 0x00, 0x00, 0x23, } }; diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/MetadataReaderTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/MetadataReaderTests.cs index 06fa451038fd9..bba7976b646df 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/MetadataReaderTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/MetadataReaderTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; @@ -203,11 +204,11 @@ public unsafe void InvalidExternalTableMask() byte[] peImage = (byte[])PortablePdbs.DocumentsPdb.Clone(); GCHandle pinned = GetPinnedPEImage(peImage); - //38654710855 is the external table mask from PortablePdbs.DocumentsPdb - int externalTableMaskIndex = IndexOf(peImage, BitConverter.GetBytes(38654710855), 0); + //0x900001447 is the external table mask from PortablePdbs.DocumentsPdb + int externalTableMaskIndex = IndexOf(peImage, new byte[] { 0x47, 0x14, 0, 0, 9, 0, 0, 0 }, 0); Assert.NotEqual(externalTableMaskIndex, -1); - Array.Copy(BitConverter.GetBytes(38654710855 + 1), 0, peImage, externalTableMaskIndex, BitConverter.GetBytes(38654710855 + 1).Length); + Array.Copy(new byte[] { 0x48, 0x14, 0, 0, 9, 0, 0, 0 }, 0, peImage, externalTableMaskIndex, 8); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject(), peImage.Length)); } @@ -245,30 +246,30 @@ public unsafe void InvalidMetaDataTableHeaders() GCHandle pinned = GetPinnedPEImage(peImage); PEHeaders headers = new PEHeaders(new MemoryStream(peImage)); - //1392 is the remaining bytes from NetModule.AppCS - int remainingBytesIndex = IndexOf(peImage, BitConverter.GetBytes(1392), headers.MetadataStartOffset); + //0x0570 is the remaining bytes from NetModule.AppCS + int remainingBytesIndex = IndexOf(peImage, new byte[] { 0x70, 0x05, 0, 0 }, headers.MetadataStartOffset); Assert.NotEqual(remainingBytesIndex, -1); - //14057656686423 is the presentTables from NetModule.AppCS, must be after remainingBytesIndex - int presentTablesIndex = IndexOf(peImage, BitConverter.GetBytes(14057656686423), headers.MetadataStartOffset + remainingBytesIndex); + //0xcc90da21757 is the presentTables from NetModule.AppCS, must be after remainingBytesIndex + int presentTablesIndex = IndexOf(peImage, new byte[] { 0x57, 0x17, 0xa2, 0x0d, 0xc9, 0x0c, 0, 0 }, headers.MetadataStartOffset + remainingBytesIndex); Assert.NotEqual(presentTablesIndex, -1); //Set this.ModuleTable.NumberOfRows to 0 - Array.Copy(BitConverter.GetBytes((ulong)0), 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset + 16, BitConverter.GetBytes((ulong)0).Length); + Array.Copy(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset + 16, 8); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize)); //set row counts greater than TokenTypeIds.RIDMask - Array.Copy(BitConverter.GetBytes((ulong)16777216), 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset + 16, BitConverter.GetBytes((ulong)16777216).Length); + Array.Copy(new byte[] { 0, 0, 1, 0 }, 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset + 16, 4); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize)); //set remaining bytes smaller than required for row counts. - Array.Copy(BitConverter.GetBytes(25), 0, peImage, remainingBytesIndex + headers.MetadataStartOffset, BitConverter.GetBytes(25).Length); + Array.Copy(new byte[] { 25, 0, 0, 0 }, 0, peImage, remainingBytesIndex + headers.MetadataStartOffset, 4); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize)); - //14057656686424 is a value to make (presentTables & ~validTables) != 0 but not (presentTables & (ulong)(TableMask.PtrTables | TableMask.EnCMap)) != 0 - Array.Copy(BitConverter.GetBytes((ulong)14057656686424), 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset, BitConverter.GetBytes((ulong)14057656686424).Length); + //0xcc90da21758 is a value to make (presentTables & ~validTables) != 0 but not (presentTables & (ulong)(TableMask.PtrTables | TableMask.EnCMap)) != 0 + Array.Copy(new byte[] { 0x58, 0x17, 0xa2, 0x0d, 0xc9, 0x0c, 0, 0 }, 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset, 8); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize)); - //14066246621015 makes (presentTables & ~validTables) != 0 fail - Array.Copy(BitConverter.GetBytes((ulong)14066246621015), 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset, BitConverter.GetBytes((ulong)14066246621015).Length); + //0xccb0da21757 makes (presentTables & ~validTables) != 0 fail + Array.Copy(new byte[] { 0x57, 0x17, 0xa2, 0x0d, 0xcb, 0x0c, 0, 0 }, 0, peImage, presentTablesIndex + remainingBytesIndex + headers.MetadataStartOffset, 8); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize)); //set remaining bytes smaller than MetadataStreamConstants.SizeOfMetadataTableHeader - Array.Copy(BitConverter.GetBytes(1), 0, peImage, remainingBytesIndex + headers.MetadataStartOffset, BitConverter.GetBytes(1).Length); + Array.Copy(new byte[] { 1, 0, 0, 0 }, 0, peImage, remainingBytesIndex + headers.MetadataStartOffset, 4); Assert.Throws(() => new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize)); } @@ -3033,18 +3034,25 @@ private static unsafe byte[] ObfuscateWithExtraData(byte[] unobfuscated, bool se Array.Copy(unobfuscated, obfuscated, offsetToModuleTable); Array.Copy(unobfuscated, offsetToModuleTable, obfuscated, offsetToModuleTable + sizeOfExtraData, unobfuscated.Length - offsetToModuleTable); - fixed (byte* ptr = obfuscated) - { - // increase size of metadata - *(int*)(ptr + offsetToMetadataSize) += sizeOfExtraData; + // increase size of metadata + Span MetadataSizeSpan = new Span(obfuscated, offsetToMetadataSize, 4); + uint MetadataSize = BinaryPrimitives.ReadUInt32LittleEndian(MetadataSizeSpan); + BinaryPrimitives.WriteUInt32LittleEndian(MetadataSizeSpan, MetadataSize + sizeOfExtraData); - // increase size of table stream - *(int*)(ptr + streamHeaders[tableStreamIndex].OffsetToSize) += sizeOfExtraData; + // increase size of table stream + Span TableStreamSpan = new Span(obfuscated, streamHeaders[tableStreamIndex].OffsetToSize, 4); + uint TableStreamSize = BinaryPrimitives.ReadUInt32LittleEndian(TableStreamSpan); + BinaryPrimitives.WriteUInt32LittleEndian(TableStreamSpan, TableStreamSize + sizeOfExtraData); - // adjust offset of any streams that follow it - for (int i = 0; i < streamHeaders.Length; i++) - if (streamHeaders[i].Offset > streamHeaders[tableStreamIndex].Offset) - *(int*)(ptr + streamHeaders[i].OffsetToOffset) += sizeOfExtraData; + // adjust offset of any streams that follow it + for (int i = 0; i < streamHeaders.Length; i++) + { + if (streamHeaders[i].Offset > streamHeaders[tableStreamIndex].Offset) + { + Span OffsetSpan = new Span(obfuscated, streamHeaders[i].OffsetToOffset, 4); + uint Offset = BinaryPrimitives.ReadUInt32LittleEndian(OffsetSpan); + BinaryPrimitives.WriteUInt32LittleEndian(OffsetSpan, Offset + sizeOfExtraData); + } } // write non-zero "extra data" to make sure so that our assertion of leading Module.Generation == 0 diff --git a/src/libraries/System.Reflection.Metadata/tests/Metadata/TagToTokenTests.cs b/src/libraries/System.Reflection.Metadata/tests/Metadata/TagToTokenTests.cs index ae9058c7ffefc..329b6e2c1af5a 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Metadata/TagToTokenTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Metadata/TagToTokenTests.cs @@ -61,11 +61,25 @@ private IEnumerable GetTags() Assert.Contains(vector.FieldType, new[] { typeof(uint), typeof(ulong) }); if (vector.FieldType == typeof(uint)) { - return BitConverter.GetBytes((uint)vector.GetValue(null)).Select(t => (uint)t << TokenTypeIds.RowIdBitCount).ToArray(); + uint value = (uint)vector.GetValue(null); + uint[] ret = new uint[4]; + for (uint i = 0; i < 4; i++) + { + ret[i] = ((uint)(byte)value) << TokenTypeIds.RowIdBitCount; + value >>= 8; + } + return ret; } else { - return BitConverter.GetBytes((ulong)vector.GetValue(null)).Select(t => (uint)t << TokenTypeIds.RowIdBitCount).ToArray(); + ulong value = (ulong)vector.GetValue(null); + uint[] ret = new uint[8]; + for (uint i = 0; i < 8; i++) + { + ret[i] = ((uint)(byte)value) << TokenTypeIds.RowIdBitCount; + value >>= 8; + } + return ret; } }, diff --git a/src/libraries/System.Reflection.Metadata/tests/Utilities/AbstractMemoryBlockTests.cs b/src/libraries/System.Reflection.Metadata/tests/Utilities/AbstractMemoryBlockTests.cs index eeeb2432f010b..cad6a3d4d68a3 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Utilities/AbstractMemoryBlockTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Utilities/AbstractMemoryBlockTests.cs @@ -118,21 +118,6 @@ public void Stream() } } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34493", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - public void FileStreamUnix() - { - try - { - FileStreamReadLightUp.readFileNotAvailable = true; - FileStream(); - } - finally - { - FileStreamReadLightUp.readFileNotAvailable = false; - } - } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34493", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] public void FileStream() diff --git a/src/libraries/System.Reflection.Metadata/tests/Utilities/BlobReaderTests.cs b/src/libraries/System.Reflection.Metadata/tests/Utilities/BlobReaderTests.cs index 318c793833651..096846d0ce49e 100644 --- a/src/libraries/System.Reflection.Metadata/tests/Utilities/BlobReaderTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/Utilities/BlobReaderTests.cs @@ -307,7 +307,7 @@ public unsafe void ReadFromMemoryReader() reader.Reset(); Assert.Equal(0, reader.Offset); - Assert.Equal(BitConverter.ToDouble(buffer2, 0), reader.ReadDouble()); + Assert.Equal(BitConverter.Int64BitsToDouble(0x0807060504030201L), reader.ReadDouble()); } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs index 9e90faad45061..9341a9a89684a 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/MemberPolicies.cs @@ -203,7 +203,7 @@ static MemberPolicies() } else if (t.Equals(typeof(PropertyInfo))) { - MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Property; ; + MemberTypeIndex = BindingFlagSupport.MemberTypeIndex.Property; Default = (MemberPolicies)(object)(new PropertyPolicies()); } else if (t.Equals(typeof(EventInfo))) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs index 9a45ff1e03014..a317976281ed2 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs @@ -395,7 +395,7 @@ private static void TestTypeCommonInvariants(this Type type) { ICustomAttributeProvider icp = mem; Assert.Throws(() => icp.IsDefined(null, inherit: false)); - Assert.Throws(() => icp.GetCustomAttributes(null, inherit: false)); ; + Assert.Throws(() => icp.GetCustomAttributes(null, inherit: false)); Assert.Throws(() => icp.GetCustomAttributes(inherit: false)); if (mem is MethodBase mb) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs index f0693691d8654..09e5d783fd4bf 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs @@ -178,7 +178,7 @@ public static void TestArraySetMethod() bool expectedDefaultValue = true; Type et = typeof(long).Project(); - Type t = typeof(long[]).Project(); ; + Type t = typeof(long[]).Project(); TypeInfo ti = t.GetTypeInfo(); MethodInfo m = ti.GetDeclaredMethod("Set"); Assert.Equal(MethodAttributes.Public | MethodAttributes.PrivateScope, m.Attributes); @@ -213,8 +213,8 @@ public static void TestArrayAddressMethod() { bool expectedDefaultValue = true; - Type et = typeof(long).Project(); ; - Type t = typeof(long[]).Project(); ; + Type et = typeof(long).Project(); + Type t = typeof(long[]).Project(); TypeInfo ti = t.GetTypeInfo(); MethodInfo m = ti.GetDeclaredMethod("Address"); Assert.Equal(MethodAttributes.Public | MethodAttributes.PrivateScope, m.Attributes); @@ -240,8 +240,8 @@ public static void TestArrayCtor() { bool expectedDefaultValue = true; - Type et = typeof(long).Project(); ; - Type t = typeof(long[]).Project(); ; + Type et = typeof(long).Project(); + Type t = typeof(long[]).Project(); TypeInfo ti = t.GetTypeInfo(); ConstructorInfo[] ctors = ti.DeclaredConstructors.ToArray(); Assert.Equal(1, ctors.Length); diff --git a/src/libraries/System.Reflection/tests/AssemblyTests.cs b/src/libraries/System.Reflection/tests/AssemblyTests.cs index 66d8d41a5675b..1892109d240f1 100644 --- a/src/libraries/System.Reflection/tests/AssemblyTests.cs +++ b/src/libraries/System.Reflection/tests/AssemblyTests.cs @@ -856,7 +856,7 @@ private static Assembly LoadSystemReflectionAssembly() private static Assembly LoadSystemRuntimeAssembly() { // Load System.Runtime - return Assembly.Load(new AssemblyName(typeof(int).GetTypeInfo().Assembly.FullName)); ; + return Assembly.Load(new AssemblyName(typeof(int).GetTypeInfo().Assembly.FullName)); } private static Assembly GetGetCallingAssembly() diff --git a/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj b/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj index 5b4ff27858395..deceb65e37f3e 100644 --- a/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj +++ b/src/libraries/System.Reflection/tests/System.Reflection.Tests.csproj @@ -71,4 +71,8 @@ + + + <__ExcludeFromBundle Include="TestAssembly.dll" /> + diff --git a/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj b/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj index cf4506650412b..2787ad7bb9598 100644 --- a/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj +++ b/src/libraries/System.Resources.Extensions/src/System.Resources.Extensions.csproj @@ -60,6 +60,7 @@ + diff --git a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs index 532f47737fa6a..ea9fbb4410376 100644 --- a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs +++ b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/DeserializingResourceReader.cs @@ -48,7 +48,7 @@ private object ReadBinaryFormattedObject() } - internal class UndoTruncatedTypeNameSerializationBinder : SerializationBinder + internal sealed class UndoTruncatedTypeNameSerializationBinder : SerializationBinder { public override Type? BindToType(string assemblyName, string typeName) { diff --git a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs index ec03615b89a0d..53f58ab2ba72f 100644 --- a/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs +++ b/src/libraries/System.Resources.Extensions/src/System/Resources/Extensions/PreserializedResourceWriter.cs @@ -8,7 +8,7 @@ namespace System.Resources.Extensions { - internal class UnknownType { } + internal sealed class UnknownType { } public partial class PreserializedResourceWriter { @@ -188,7 +188,7 @@ public void AddActivatorResource(string name, Stream value, string typeName, boo _requiresDeserializingResourceReader = true; } - private class ResourceDataRecord + private sealed class ResourceDataRecord { internal readonly SerializationFormat Format; internal readonly object Data; diff --git a/src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.csproj b/src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.csproj index c86377afad37a..32f5458551f67 100644 --- a/src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.csproj +++ b/src/libraries/System.Runtime.Caching/ref/System.Runtime.Caching.csproj @@ -1,8 +1,14 @@ - netstandard2.0 + netcoreapp3.1;netstandard2.0 + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj index 0d934ab9b2424..fde4c12992feb 100644 --- a/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj +++ b/src/libraries/System.Runtime.Caching/src/System.Runtime.Caching.csproj @@ -1,9 +1,31 @@ - + true - netstandard2.0;netstandard2.0-windows + netcoreapp3.1;netcoreapp3.1-windows;netstandard2.0;netstandard2.0-windows true + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs index ed930138f6e1a..42ad42d68747b 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheMemoryMonitor.cs @@ -224,9 +224,7 @@ internal override int GetPercentToTrim(DateTime lastTrimTime, int lastTrimPercen } #if PERF - Debug.WriteLine(string.Format("CacheMemoryMonitor.GetPercentToTrim: percent={0:N}, lastTrimPercent={1:N}{Environment.NewLine}", - percent, - lastTrimPercent)); + Debug.WriteLine($"CacheMemoryMonitor.GetPercentToTrim: percent={percent:N}, lastTrimPercent={lastTrimPercent:N}{Environment.NewLine}"); #endif } return percent; diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs index fa093a5a305bf..4b6df845ef503 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/CacheUsage.cs @@ -854,7 +854,7 @@ internal int FlushUnderUsedItems(int maxFlush, bool force) } } - internal class CacheUsage + internal sealed class CacheUsage { internal static readonly TimeSpan NEWADD_INTERVAL = new TimeSpan(0, 0, 10); internal static readonly TimeSpan CORRELATED_REQUEST_TIMEOUT = new TimeSpan(0, 0, 1); diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs index 451cdba34866e..339169a4c45ed 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/FileChangeNotificationSystem.cs @@ -15,12 +15,12 @@ internal sealed class FileChangeNotificationSystem : IFileChangeNotificationSyst private readonly Hashtable _dirMonitors; private readonly object _lock; - internal class DirectoryMonitor + internal sealed class DirectoryMonitor { internal FileSystemWatcher Fsw; } - internal class FileChangeEventTarget + internal sealed class FileChangeEventTarget { private readonly string _fileName; private readonly OnChangedCallback _onChangedCallback; diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs index 4838f9b2c267e..c53d038ec1b86 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCache.cs @@ -42,7 +42,7 @@ public class MemoryCache : ObjectCache, IEnumerable, IDisposable private bool IsDisposed { get { return (_disposed == 1); } } internal bool ConfigLess { get { return _configLess; } } - private class SentinelEntry + private sealed class SentinelEntry { private readonly string _key; private readonly ChangeMonitor _expensiveObjectDependency; diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs index 5e96f13190514..b17bf5ff94101 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheEntry.cs @@ -11,7 +11,7 @@ namespace System.Runtime.Caching { - internal class MemoryCacheEntry : MemoryCacheKey + internal sealed class MemoryCacheEntry : MemoryCacheKey { private readonly object _value; private readonly DateTime _utcCreated; @@ -29,7 +29,7 @@ internal class MemoryCacheEntry : MemoryCacheKey private readonly CacheEntryRemovedCallback _callback; private SeldomUsedFields _fields; // optimization to reduce workingset when the entry hasn't any dependencies - private class SeldomUsedFields + private sealed class SeldomUsedFields { internal Collection _dependencies; // the entry's dependency needs to be disposed when the entry is released internal Dictionary _dependents; // dependents must be notified when this entry is removed diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs index 8d7650f820e93..c7d0173724dcc 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheKeyEqualityComparer.cs @@ -6,7 +6,7 @@ namespace System.Runtime.Caching { - internal class MemoryCacheEqualityComparer : IEqualityComparer + internal sealed class MemoryCacheEqualityComparer : IEqualityComparer { bool IEqualityComparer.Equals(object x, object y) { diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs index eb91ea5030c3d..e29a5f871cad0 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/MemoryCacheStatistics.cs @@ -146,10 +146,12 @@ private void InitializeConfiguration(NameValueCollection config) _configCacheMemoryLimitMegabytes = ConfigUtil.GetIntValue(config, ConfigUtil.CacheMemoryLimitMegabytes, _configCacheMemoryLimitMegabytes, true, int.MaxValue); _configPhysicalMemoryLimitPercentage = ConfigUtil.GetIntValue(config, ConfigUtil.PhysicalMemoryLimitPercentage, _configPhysicalMemoryLimitPercentage, true, 100); } +#if !NETCOREAPP3_1_OR_GREATER if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _configPhysicalMemoryLimitPercentage > 0) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_PhysicalMemoryLimitPercentage); } +#endif } private void InitDisposableMembers() diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Unix.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Unix.cs index 96ebc6c56d527..9963bb7f41cd5 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Unix.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.Unix.cs @@ -2,14 +2,55 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.IO; +using System.Text.RegularExpressions; namespace System.Runtime.Caching { internal sealed partial class PhysicalMemoryMonitor : MemoryMonitor { + /* There are sysconf and /proc/meminfo ways to get this information before .Net Core 3, + * but it is very complicated to do it correctly, especially when accounting for + * container scenarios. The GC does this for us in .Net Core 3. + * + * Note: This is still a little bit off in some restrited memory scenarios, as + * 'TotalAvailableMemoryBytes' does not exactly report all of the available bytes. + * But if running in a memory-restricted Linux container, this "good citizen" + * memory monitor feels a little redundant anyway since memory is already capped + * via the system. + * But this comment (https://github.com/dotnet/coreclr/pull/25437#discussion_r299810957) + * highlights how our behavior might be slightly different from windows in these + * cases since this was a monitor that cared about actual physical memory. + */ +#if NETCOREAPP3_1_OR_GREATER + private int lastGCCount; + protected override int GetCurrentPressure() { - return 0; + // Try to refresh GC stats if they haven't been updated since our last check. + int ccount = GC.CollectionCount(0); + if (ccount == lastGCCount) + { + GC.Collect(0, GCCollectionMode.Optimized); // A quick, ephemeral Gen 0 collection + ccount = GC.CollectionCount(0); + } + lastGCCount = ccount; + + // Get stats from GC. + GCMemoryInfo memInfo = GC.GetGCMemoryInfo(); + + if (memInfo.TotalAvailableMemoryBytes >= memInfo.MemoryLoadBytes) + { + int memoryLoad = (int)((float)memInfo.MemoryLoadBytes * 100.0 / (float)memInfo.TotalAvailableMemoryBytes); + return Math.Max(1, memoryLoad); + } + + // It's possible the load was legitimately higher than "available". In that case, return 100. + // Otherwise, return 0 to minimize impact because something was unexpected. + return (memInfo.MemoryLoadBytes > 0) ? 100 : 0; } +#else + protected override int GetCurrentPressure() => 0; +#endif } } diff --git a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs index 6c908fd4874c3..b002740316335 100644 --- a/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs +++ b/src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/SRef.cs @@ -11,7 +11,7 @@ namespace System.Runtime.Caching { // until then we provide a stub - internal class SRefMultiple + internal sealed class SRefMultiple { internal SRefMultiple(object[] targets) { @@ -22,7 +22,7 @@ internal void Dispose() } } - internal class GCHandleRef : IDisposable + internal sealed class GCHandleRef : IDisposable where T : class, IDisposable { private GCHandle _handle; diff --git a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj index fdcb758576f51..34de6bc40a827 100644 --- a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj +++ b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching.Tests.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppCurrent)-windows;net48-windows + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-windows;net48-windows diff --git a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs index 43a1c2072178f..cf8804444c745 100644 --- a/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs +++ b/src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs @@ -39,11 +39,37 @@ using System.Threading.Tasks; using Xunit; using MonoTests.Common; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Runtime.Versioning; namespace MonoTests.System.Runtime.Caching { public class MemoryCacheTest { + public static bool SupportsPhysicalMemoryMonitor + { + get + { + // This is always supported on windows + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return true; + } + + // On non-windows, we only support .Net 5.0 and higher + if (Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return false; + } + } + public static bool DoesNotSupportPhysicalMemoryMonitor => !SupportsPhysicalMemoryMonitor; + + [Fact] public void ConstructorParameters() { @@ -144,8 +170,8 @@ public void ConstructorParameters() mc = new MemoryCache("MyCache", config); } - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Negative case for "physicalMemoryLimitPercentage" on non Windows + // Negative case for "physicalMemoryLimitPercentage" on non-supporting OS's + [ConditionalFact(nameof(DoesNotSupportPhysicalMemoryMonitor))] public void PhysicalMemoryLimitNotSupported() { var config = new NameValueCollection(); @@ -188,8 +214,7 @@ public void DefaultInstanceDefaults() mc.DefaultCacheCapabilities); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Uses "physicalMemoryLimitPercentage" not supported on other platforms + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] public void ConstructorValues() { var config = new NameValueCollection(); @@ -1006,8 +1031,7 @@ public void Trim() Assert.Equal(11, mc.GetCount()); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Uses "physicalMemoryLimitPercentage" not supported on other platforms + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] public void TestExpiredGetValues() { var config = new NameValueCollection(); @@ -1040,9 +1064,13 @@ public void TestExpiredGetValues() } } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Uses "physicalMemoryLimitPercentage" not supported on other platforms [OuterLoop] // makes long wait + [Fact] + // This little dance is needed to prevent this test from running against the OS-specific + // runtime binary on the wrong OS. Without it, this test will run for each 'TargetFramework' + // in the test csproj, and the non-windows framework will run against the non-windows library + // because 'netstandard' is still valid for windows execution. + [PlatformSpecific(TestPlatforms.Windows)] public void TestCacheSliding() { var config = new NameValueCollection(); @@ -1348,8 +1376,9 @@ public async Task GetCacheItem() public class MemoryCacheTestExpires4 { - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Uses "physicalMemoryLimitPercentage" not supported on other platforms + public static bool SupportsPhysicalMemoryMonitor => MemoryCacheTest.SupportsPhysicalMemoryMonitor; + + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] public async Task TestCacheShrink() { const int HEAP_RESIZE_THRESHOLD = 8192 + 2; @@ -1406,8 +1435,9 @@ public async Task TestCacheShrink() public class MemoryCacheTestExpires5 { - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] // Uses "physicalMemoryLimitPercentage" not supported on other platforms + public static bool SupportsPhysicalMemoryMonitor => MemoryCacheTest.SupportsPhysicalMemoryMonitor; + + [ConditionalFact(nameof(SupportsPhysicalMemoryMonitor))] public async Task TestCacheExpiryOrdering() { var config = new NameValueCollection(); diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il index c746a05cfab93..f7d5718f456a7 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il @@ -28,6 +28,10 @@ 01 00 0b 53 65 72 76 69 63 65 61 62 6c 65 04 54 72 75 65 00 00 ) // "Serviceable", "True" + .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyMetadataAttribute::.ctor(string, string) = ( + 01 00 0b 49 73 54 72 69 6d 6d 61 62 6c 65 04 54 + 72 75 65 00 00 + ) // "IsTrimmable", "True" .custom instance void [CORE_ASSEMBLY]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2F C2 A9 20 4D 69 63 72 6F 73 6F 66 74 20 // ../.. Microsoft 43 6F 72 70 6F 72 61 74 69 6F 6E 2E 20 20 41 6C // Corporation. Al 6C 20 72 69 67 68 74 73 20 72 65 73 65 72 76 65 // l rights reserve diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.ilproj b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.ilproj index 71f1a02ea6a69..aed036f572e6d 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.ilproj +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.ilproj @@ -1,17 +1,14 @@  - + $(NetCoreAppCurrent);netstandard2.0;netcoreapp2.0;netstandard1.0;net45 + true + IMPL OPT - - $(CoreCompileDependsOn);GenerateVersionFile $(MSBuildThisFileDirectory)System.Runtime.CompilerServices.Unsafe.xml - {04BA3E3C-6979-4792-B19E-C797AD607F42} #define netcoreapp $(IlasmFlags) -DEBUG=$(DebugOptimization) - $(NetCoreAppCurrent);netstandard2.0;netcoreapp2.0;netstandard1.0;net45 - true System.Runtime netstandard @@ -85,4 +82,4 @@ $(ExtraMacros) - \ No newline at end of file + diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index b2c811c66addf..4780b41b00ae8 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -53,7 +53,15 @@ public static unsafe void WriteBytesIntoInt32() Assert.Equal(2, b4.B2); Assert.Equal(3, b4.B3); - int expected = (b4.B3 << 24) + (b4.B2 << 16) + (b4.B1 << 8) + (b4.B0); + int expected; + if (BitConverter.IsLittleEndian) + { + expected = (b4.B3 << 24) + (b4.B2 << 16) + (b4.B1 << 8) + (b4.B0); + } + else + { + expected = (b4.B0 << 24) + (b4.B1 << 16) + (b4.B2 << 8) + (b4.B3); + } Assert.Equal(expected, value); } @@ -63,12 +71,24 @@ public static unsafe void LongIntoCompoundStruct() long value = 1234567891011121314L; long* longAddress = (long*)Unsafe.AsPointer(ref value); Byte4Short2 b4s2 = Unsafe.Read(longAddress); - Assert.Equal(162, b4s2.B0); - Assert.Equal(48, b4s2.B1); - Assert.Equal(210, b4s2.B2); - Assert.Equal(178, b4s2.B3); - Assert.Equal(4340, b4s2.S4); - Assert.Equal(4386, b4s2.S6); + if (BitConverter.IsLittleEndian) + { + Assert.Equal(162, b4s2.B0); + Assert.Equal(48, b4s2.B1); + Assert.Equal(210, b4s2.B2); + Assert.Equal(178, b4s2.B3); + Assert.Equal(4340, b4s2.S4); + Assert.Equal(4386, b4s2.S6); + } + else + { + Assert.Equal(17, b4s2.B0); + Assert.Equal(34, b4s2.B1); + Assert.Equal(16, b4s2.B2); + Assert.Equal(244, b4s2.B3); + Assert.Equal(-19758, b4s2.S4); + Assert.Equal(12450, b4s2.S6); + } b4s2.B0 = 1; b4s2.B1 = 1; @@ -78,7 +98,15 @@ public static unsafe void LongIntoCompoundStruct() b4s2.S6 = 1; Unsafe.Write(longAddress, b4s2); - long expected = 281479288520961; + long expected; + if (BitConverter.IsLittleEndian) + { + expected = 281479288520961; + } + else + { + expected = 72340172821299201; + } Assert.Equal(expected, value); Assert.Equal(expected, Unsafe.Read(longAddress)); } diff --git a/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs index d7aa3be529338..1101f747efccd 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/AppDomainTests.cs @@ -45,6 +45,8 @@ public void RelativeSearchPath_Is_Null() [Fact] [PlatformSpecific(~TestPlatforms.Browser)] // throws pNSE + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49868", TestPlatforms.Android)] public void TargetFrameworkTest() { const int ExpectedExitCode = 0; @@ -209,6 +211,7 @@ public void ProcessExit_Add_Remove() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ProcessExit_Called() { string path = GetTestFilePath(); @@ -792,7 +795,7 @@ public static void GetPermissionSet() } [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34030", TestPlatforms.Linux | TestPlatforms.Browser, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34030", TestPlatforms.Linux | TestPlatforms.Browser | TestPlatforms.Android, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [MemberData(nameof(TestingCreateInstanceFromObjectHandleData))] public static void TestingCreateInstanceFromObjectHandle(string physicalFileName, string assemblyFile, string type, string returnedFullNameType, Type exceptionType) { @@ -892,7 +895,7 @@ public static void TestingCreateInstanceObjectHandle(string assemblyName, string }; [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34030", TestPlatforms.Linux | TestPlatforms.Browser, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34030", TestPlatforms.Linux | TestPlatforms.Browser | TestPlatforms.Android, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [MemberData(nameof(TestingCreateInstanceFromObjectHandleFullSignatureData))] public static void TestingCreateInstanceFromObjectHandleFullSignature(string physicalFileName, string assemblyFile, string type, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes, string returnedFullNameType) { diff --git a/src/libraries/System.Runtime.Extensions/tests/System/BitConverterArray.cs b/src/libraries/System.Runtime.Extensions/tests/System/BitConverterArray.cs index 69120ed67ca62..cea5c86d3101e 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/BitConverterArray.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/BitConverterArray.cs @@ -7,6 +7,16 @@ namespace System.Tests { public class BitConverterArray : BitConverterBase { + private byte[] RangeToLittleEndian(byte[] array, int index, int length) + { + if (!BitConverter.IsLittleEndian) + { + array = (byte[]) array.Clone(); + Array.Reverse(array, index, length); + } + return array; + } + public override void ConvertFromBool(bool boolean, byte[] expected) { Assert.Equal(expected, BitConverter.GetBytes(boolean)); @@ -14,101 +24,121 @@ public override void ConvertFromBool(bool boolean, byte[] expected) public override void ConvertFromShort(short num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromChar(char character, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Assert.Equal(expected, BitConverter.GetBytes(character)); } public override void ConvertFromInt(int num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 4); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromLong(long num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 8); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromUShort(ushort num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromUInt(uint num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 4); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromULong(ulong num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 8); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromHalf(Half num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromFloat(float num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 4); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ConvertFromDouble(double num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 8); Assert.Equal(expected, BitConverter.GetBytes(num)); } public override void ToChar(int index, char expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); Assert.Equal(expected, BitConverter.ToChar(byteArray, index)); } public override void ToInt16(int index, short expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); Assert.Equal(expected, BitConverter.ToInt16(byteArray, index)); } public override void ToInt32(int expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, 0, 4); Assert.Equal(expected, BitConverter.ToInt32(byteArray, 0)); } public override void ToInt64(int index, long expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 8); Assert.Equal(expected, BitConverter.ToInt64(byteArray, index)); } public override void ToUInt16(int index, ushort expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); Assert.Equal(expected, BitConverter.ToUInt16(byteArray, index)); } public override void ToUInt32(int index, uint expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 4); Assert.Equal(expected, BitConverter.ToUInt32(byteArray, index)); } public override void ToUInt64(int index, ulong expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 8); Assert.Equal(expected, BitConverter.ToUInt64(byteArray, index)); } public override void ToHalf(int index, Half expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); Assert.Equal(expected, BitConverter.ToHalf(byteArray, index)); } public override void ToSingle(int index, float expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 4); Assert.Equal(expected, BitConverter.ToSingle(byteArray, index)); } public override void ToDouble(int index, double expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 8); Assert.Equal(expected, BitConverter.ToDouble(byteArray, index)); } diff --git a/src/libraries/System.Runtime.Extensions/tests/System/BitConverterSpan.cs b/src/libraries/System.Runtime.Extensions/tests/System/BitConverterSpan.cs index c527a5a599b32..326909ff2232f 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/BitConverterSpan.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/BitConverterSpan.cs @@ -37,6 +37,16 @@ public void ToMethods_DestinationSpanNotLargeEnough() Assert.Throws(() => { BitConverter.ToBoolean(Span.Empty); }); } + private byte[] RangeToLittleEndian(byte[] array, int index, int length) + { + if (!BitConverter.IsLittleEndian) + { + array = (byte[]) array.Clone(); + Array.Reverse(array, index, length); + } + return array; + } + public override void ConvertFromBool(bool boolean, byte[] expected) { Span span = new Span(new byte[1]); @@ -46,6 +56,7 @@ public override void ConvertFromBool(bool boolean, byte[] expected) public override void ConvertFromShort(short num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Span span = new Span(new byte[2]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -53,6 +64,7 @@ public override void ConvertFromShort(short num, byte[] expected) public override void ConvertFromChar(char character, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Span span = new Span(new byte[2]); Assert.True(BitConverter.TryWriteBytes(span, character)); Assert.Equal(expected, span.ToArray()); @@ -60,6 +72,7 @@ public override void ConvertFromChar(char character, byte[] expected) public override void ConvertFromInt(int num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 4); Span span = new Span(new byte[4]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -67,6 +80,7 @@ public override void ConvertFromInt(int num, byte[] expected) public override void ConvertFromLong(long num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 8); Span span = new Span(new byte[8]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -74,6 +88,7 @@ public override void ConvertFromLong(long num, byte[] expected) public override void ConvertFromUShort(ushort num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Span span = new Span(new byte[2]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -81,6 +96,7 @@ public override void ConvertFromUShort(ushort num, byte[] expected) public override void ConvertFromUInt(uint num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 4); Span span = new Span(new byte[4]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -88,6 +104,7 @@ public override void ConvertFromUInt(uint num, byte[] expected) public override void ConvertFromULong(ulong num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 8); Span span = new Span(new byte[8]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -95,6 +112,7 @@ public override void ConvertFromULong(ulong num, byte[] expected) public override void ConvertFromHalf(Half num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 2); Span span = new Span(new byte[2]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -102,6 +120,7 @@ public override void ConvertFromHalf(Half num, byte[] expected) public override void ConvertFromFloat(float num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 4); Span span = new Span(new byte[4]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -109,6 +128,7 @@ public override void ConvertFromFloat(float num, byte[] expected) public override void ConvertFromDouble(double num, byte[] expected) { + expected = RangeToLittleEndian(expected, 0, 8); Span span = new Span(new byte[8]); Assert.True(BitConverter.TryWriteBytes(span, num)); Assert.Equal(expected, span.ToArray()); @@ -116,6 +136,7 @@ public override void ConvertFromDouble(double num, byte[] expected) public override void ToChar(int index, char expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); ReadOnlySpan span = new ReadOnlySpan(byteArray); BitConverter.ToChar(span); Assert.Equal(expected, BitConverter.ToChar(span.Slice(index))); @@ -123,54 +144,63 @@ public override void ToChar(int index, char expected, byte[] byteArray) public override void ToInt16(int index, short expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToInt16(span.Slice(index))); } public override void ToInt32(int expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, 0, 4); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToInt32(byteArray)); } public override void ToInt64(int index, long expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 8); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToInt64(span.Slice(index))); } public override void ToUInt16(int index, ushort expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToUInt16(span.Slice(index))); } public override void ToUInt32(int index, uint expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 4); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToUInt32(span.Slice(index))); } public override void ToUInt64(int index, ulong expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 8); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToUInt64(span.Slice(index))); } public override void ToHalf(int index, Half expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 2); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToHalf(span.Slice(index))); } public override void ToSingle(int index, float expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 4); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToSingle(span.Slice(index))); } public override void ToDouble(int index, double expected, byte[] byteArray) { + byteArray = RangeToLittleEndian(byteArray, index, 8); ReadOnlySpan span = new ReadOnlySpan(byteArray); Assert.Equal(expected, BitConverter.ToDouble(span.Slice(index))); } diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs index 0ba249373cc7d..2b49f421fb341 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.Exit.cs @@ -22,6 +22,7 @@ public class Environment_Exit [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(ExitCodeValues))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void CheckExitCode(int expectedExitCode) { RemoteExecutor.Invoke(s => int.Parse(s), expectedExitCode.ToString(), new RemoteInvokeOptions { ExpectedExitCode = expectedExitCode }).Dispose(); @@ -42,6 +43,8 @@ public static void ExitCode_Roundtrips(int exitCode) [InlineData(2)] // setting ExitCode both from Main and from an Unloading event handler. [InlineData(3)] // using Exit(exitCode) [PlatformSpecific(~TestPlatforms.Browser)] // throws PNSE + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49868", TestPlatforms.Android)] public static void ExitCode_VoidMainAppReturnsSetValue(int mode) { int expectedExitCode = 123; diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs index 7cb8b1c610144..4dbda049a7074 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs @@ -27,8 +27,7 @@ public void ExpansionOfVariableSucceeds() // envvar1=animal; // and we are going to check that the expanded %envvar1% is animal. - Random r = new Random(); - string envVar1 = "TestVariable_ExpansionOfVariableSucceeds_" + r.Next().ToString(); + string envVar1 = "TestVariable_ExpansionOfVariableSucceeds_" + Random.Shared.Next().ToString(); string expectedValue = "animal"; try diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetCommandLineArgs.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetCommandLineArgs.cs index 695af6b8f2567..ae0d4a68e1f18 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetCommandLineArgs.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetCommandLineArgs.cs @@ -23,6 +23,7 @@ public static IEnumerable GetCommandLineArgs_TestData() [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(GetCommandLineArgs_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void GetCommandLineArgs_Invoke_ReturnsExpected(string[] args) { switch (args.Length) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs index 2f8a5a4800e18..854862bd7f370 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs @@ -102,9 +102,8 @@ public void VariableNamesAreCaseInsensitiveAsAppropriate() [Fact] public void CanGetAllVariablesIndividually() { - Random r = new Random(); - string envVar1 = "TestVariable_CanGetVariablesIndividually_" + r.Next().ToString(); - string envVar2 = "TestVariable_CanGetVariablesIndividually_" + r.Next().ToString(); + string envVar1 = "TestVariable_CanGetVariablesIndividually_" + Random.Shared.Next().ToString(); + string envVar2 = "TestVariable_CanGetVariablesIndividually_" + Random.Shared.Next().ToString(); try { diff --git a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs index 051c5e46f4e21..8415493fbf0e1 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/EnvironmentTests.cs @@ -83,6 +83,7 @@ public void ProcessId_Idempotent() } [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ProcessId_MatchesExpectedValue() { using RemoteInvokeHandle handle = RemoteExecutor.Invoke(() => Console.WriteLine(Environment.ProcessId), new RemoteInvokeOptions { StartInfo = new ProcessStartInfo { RedirectStandardOutput = true } }); @@ -96,6 +97,7 @@ public void ProcessPath_Idempotent() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ProcessPath_MatchesExpectedValue() { string expectedProcessPath = PlatformDetection.IsBrowser ? null : Process.GetCurrentProcess().MainModule.FileName; @@ -265,6 +267,7 @@ public void FailFast_ExpectFailureExitCode() [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)] // fail fast crashes the process [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void FailFast_ExceptionStackTrace_ArgumentException() { var psi = new ProcessStartInfo(); @@ -286,6 +289,7 @@ public void FailFast_ExceptionStackTrace_ArgumentException() [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)] // fail fast crashes the process [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void FailFast_ExceptionStackTrace_StackOverflowException() { // Test using another type of exception @@ -308,6 +312,7 @@ public void FailFast_ExceptionStackTrace_StackOverflowException() [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)] // fail fast crashes the process [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void FailFast_ExceptionStackTrace_InnerException() { // Test if inner exception details are also logged @@ -394,6 +399,7 @@ public void GetSystemDirectory() [InlineData(Environment.SpecialFolder.MyMusic, Environment.SpecialFolderOption.DoNotVerify)] [InlineData(Environment.SpecialFolder.MyPictures, Environment.SpecialFolderOption.DoNotVerify)] [InlineData(Environment.SpecialFolder.Fonts, Environment.SpecialFolderOption.DoNotVerify)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49868", TestPlatforms.Android)] public void GetFolderPath_Unix_NonEmptyFolderPaths(Environment.SpecialFolder folder, Environment.SpecialFolderOption option) { Assert.NotEmpty(Environment.GetFolderPath(folder, option)); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs index c0e05a97e37d0..ffbcfbccd2966 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/OperatingSystemTests.cs @@ -93,6 +93,7 @@ public static void IsOSPlatformVersionAtLeast_InvalidArgs_Throws() public static void TestIsOSVersionAtLeast_FreeBSD() => TestIsOSVersionAtLeast("FreeBSD"); [Fact, PlatformSpecific(TestPlatforms.Android)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49868", TestPlatforms.Android)] public static void TestIsOSPlatform_Android() => TestIsOSPlatform("Android", OperatingSystem.IsAndroid); [Fact, PlatformSpecific(TestPlatforms.Android)] diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs index 56318e7b34d32..8a6491aef0530 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Xunit; namespace System.Tests @@ -388,7 +390,48 @@ public void Empty_Success(bool derived, bool seeded) r.NextBytes(Span.Empty); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public void Shared_IsSingleton() + { + Assert.NotNull(Random.Shared); + Assert.Same(Random.Shared, Random.Shared); + Assert.Same(Random.Shared, Task.Run(() => Random.Shared).Result); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + public void Shared_ParallelUsage() + { + using var barrier = new Barrier(2); + Parallel.For(0, 2, _ => + { + byte[] buffer = new byte[1000]; + + barrier.SignalAndWait(); + for (int i = 0; i < 1_000; i++) + { + Assert.InRange(Random.Shared.Next(), 0, int.MaxValue - 1); + Assert.InRange(Random.Shared.Next(5), 0, 4); + Assert.InRange(Random.Shared.Next(42, 50), 42, 49); + + Assert.InRange(Random.Shared.NextInt64(), 0, long.MaxValue - 1); + Assert.InRange(Random.Shared.NextInt64(5), 0L, 5L); + Assert.InRange(Random.Shared.NextInt64(42L, 50L), 42L, 49L); + + Assert.InRange(Random.Shared.NextSingle(), 0.0f, 1.0f); + Assert.InRange(Random.Shared.NextDouble(), 0.0, 1.0); + + Array.Clear(buffer, 0, buffer.Length); + Random.Shared.NextBytes(buffer); + Assert.Contains(buffer, b => b != 0); + + Array.Clear(buffer, 0, buffer.Length); + Random.Shared.NextBytes((Span)buffer); + Assert.Contains(buffer, b => b != 0); + } + }); + } + + [ConditionalFact(typeof(BitConverter), nameof(BitConverter.IsLittleEndian))] // test makes little-endian assumptions public void Xoshiro_AlgorithmBehavesAsExpected() { // This test is validating implementation detail. If the algorithm used by `new Random()` is ever diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Runtime/ProfileOptimization.cs b/src/libraries/System.Runtime.Extensions/tests/System/Runtime/ProfileOptimization.cs index 530661c9762ea..7f980ffc9ff57 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Runtime/ProfileOptimization.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Runtime/ProfileOptimization.cs @@ -14,6 +14,7 @@ public class ProfileOptimizationTest : FileCleanupTestBase [InlineData(false)] [InlineData(true)] [ActiveIssue("https://github.com/dotnet/runtime/issues/31853", TestRuntimes.Mono)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void ProfileOptimization_CheckFileExists(bool stopProfile) { string profileFile = GetTestFileName(); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs b/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs index 62edd56f51cb0..aff008f36ada8 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/StringComparer.cs @@ -96,6 +96,7 @@ public static IEnumerable UpperLowerCasing_TestData() [Theory] [MemberData(nameof(UpperLowerCasing_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49931", TestPlatforms.Android)] public static void CreateWithCulturesTest(string lowerForm, string upperForm, string cultureName) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); @@ -195,6 +196,7 @@ public static void FromComparisonInvalidTest() [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] [MemberData(nameof(CreateFromCultureAndOptionsData))] [MemberData(nameof(CreateFromCultureAndOptionsStringSortData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49931", TestPlatforms.Android)] public static void CreateFromCultureAndOptions(string actualString, string expectedString, string cultureName, CompareOptions options, bool result) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); @@ -206,6 +208,7 @@ public static void CreateFromCultureAndOptions(string actualString, string expec [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] [MemberData(nameof(CreateFromCultureAndOptionsData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49931", TestPlatforms.Android)] public static void CreateFromCultureAndOptionsStringSort(string actualString, string expectedString, string cultureName, CompareOptions options, bool result) { CultureInfo ci = CultureInfo.GetCultureInfo(cultureName); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.cs b/src/libraries/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.cs index 577b5262d7bc3..7e37f41de4739 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/UnloadingAndProcessExitTests.cs @@ -12,6 +12,7 @@ namespace System.Tests public class UnloadingAndProcessExitTests : FileCleanupTestBase { [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void UnloadingEventMustHappenBeforeProcessExitEvent() { string fileName = GetTestFilePath(); diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index bdb0633a005d4..87757e9fde531 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -176,6 +176,7 @@ public CoClassAttribute(System.Type coClass) { } public static partial class CollectionsMarshal { public static System.Span AsSpan(System.Collections.Generic.List? list) { throw null; } + public static ref TValue GetValueRefOrNullRef(System.Collections.Generic.Dictionary dictionary, TKey key) where TKey : notnull { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue, Inherited=false)] public sealed partial class ComAliasNameAttribute : System.Attribute @@ -792,10 +793,13 @@ public ProgIdAttribute(string progId) { } } public static partial class RuntimeEnvironment { + [System.ObsoleteAttribute("SystemConfigurationFile is no longer supported.")] public static string SystemConfigurationFile { get { throw null; } } public static bool FromGlobalAccessCache(System.Reflection.Assembly a) { throw null; } public static string GetRuntimeDirectory() { throw null; } + [System.ObsoleteAttribute("GetRuntimeInterfaceAsIntPtr(Guid, Guid) is no longer supported.")] public static System.IntPtr GetRuntimeInterfaceAsIntPtr(System.Guid clsid, System.Guid riid) { throw null; } + [System.ObsoleteAttribute("GetRuntimeInterfaceAsObject(Guid, Guid) is no longer supported.")] public static object GetRuntimeInterfaceAsObject(System.Guid clsid, System.Guid riid) { throw null; } public static string GetSystemVersion() { throw null; } } diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs index e829fdbc9ca9f..9708a0b65650d 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs @@ -8,6 +8,7 @@ namespace System.Runtime.InteropServices { public static class RuntimeEnvironment { + [Obsolete("SystemConfigurationFile is no longer supported.")] public static string SystemConfigurationFile => throw new PlatformNotSupportedException(); public static bool FromGlobalAccessCache(Assembly a) => false; @@ -22,8 +23,10 @@ public static string GetRuntimeDirectory() return Path.GetDirectoryName(runtimeDirectory) + Path.DirectorySeparatorChar; } + [Obsolete("GetRuntimeInterfaceAsIntPtr(Guid, Guid) is no longer supported.")] public static IntPtr GetRuntimeInterfaceAsIntPtr(Guid clsid, Guid riid) => throw new PlatformNotSupportedException(); + [Obsolete("GetRuntimeInterfaceAsObject(Guid, Guid) is no longer supported.")] public static object GetRuntimeInterfaceAsObject(Guid clsid, Guid riid) => throw new PlatformNotSupportedException(); public static string GetSystemVersion() => typeof(object).Assembly.ImageRuntimeVersion; diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs index 1959cb790e9e9..5c5116b2a7200 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/CollectionsMarshalTests.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; + using Xunit; namespace System.Runtime.InteropServices.Tests @@ -142,9 +144,171 @@ public void ListAsSpanLinkBreaksOnResize() } } + [Fact] + public void GetValueRefOrNullRefValueType() + { + var dict = new Dictionary + { + { 1, default }, + { 2, default } + }; + + Assert.Equal(2, dict.Count); + + Assert.Equal(0, dict[1].Value); + Assert.Equal(0, dict[1].Property); + + var itemVal = dict[1]; + itemVal.Value = 1; + itemVal.Property = 2; + + // Does not change values in dictionary + Assert.Equal(0, dict[1].Value); + Assert.Equal(0, dict[1].Property); + + CollectionsMarshal.GetValueRefOrNullRef(dict, 1).Value = 3; + CollectionsMarshal.GetValueRefOrNullRef(dict, 1).Property = 4; + + Assert.Equal(3, dict[1].Value); + Assert.Equal(4, dict[1].Property); + + ref var itemRef = ref CollectionsMarshal.GetValueRefOrNullRef(dict, 2); + + Assert.Equal(0, itemRef.Value); + Assert.Equal(0, itemRef.Property); + + itemRef.Value = 5; + itemRef.Property = 6; + + Assert.Equal(5, itemRef.Value); + Assert.Equal(6, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + itemRef = new() { Value = 7, Property = 8 }; + + Assert.Equal(7, itemRef.Value); + Assert.Equal(8, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + // Check for null refs + + Assert.True(Unsafe.IsNullRef(ref CollectionsMarshal.GetValueRefOrNullRef(dict, 3))); + Assert.Throws(() => CollectionsMarshal.GetValueRefOrNullRef(dict, 3).Value = 9); + + Assert.Equal(2, dict.Count); + } + + [Fact] + public void GetValueRefOrNullRefClass() + { + var dict = new Dictionary + { + { 1, new() }, + { 2, new() } + }; + + Assert.Equal(2, dict.Count); + + Assert.Equal(0, dict[1].Value); + Assert.Equal(0, dict[1].Property); + + var itemVal = dict[1]; + itemVal.Value = 1; + itemVal.Property = 2; + + // Does change values in dictionary + Assert.Equal(1, dict[1].Value); + Assert.Equal(2, dict[1].Property); + + CollectionsMarshal.GetValueRefOrNullRef(dict, 1).Value = 3; + CollectionsMarshal.GetValueRefOrNullRef(dict, 1).Property = 4; + + Assert.Equal(3, dict[1].Value); + Assert.Equal(4, dict[1].Property); + + ref var itemRef = ref CollectionsMarshal.GetValueRefOrNullRef(dict, 2); + + Assert.Equal(0, itemRef.Value); + Assert.Equal(0, itemRef.Property); + + itemRef.Value = 5; + itemRef.Property = 6; + + Assert.Equal(5, itemRef.Value); + Assert.Equal(6, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + itemRef = new() { Value = 7, Property = 8 }; + + Assert.Equal(7, itemRef.Value); + Assert.Equal(8, itemRef.Property); + Assert.Equal(dict[2].Value, itemRef.Value); + Assert.Equal(dict[2].Property, itemRef.Property); + + // Check for null refs + + Assert.True(Unsafe.IsNullRef(ref CollectionsMarshal.GetValueRefOrNullRef(dict, 3))); + Assert.Throws(() => CollectionsMarshal.GetValueRefOrNullRef(dict, 3).Value = 9); + + Assert.Equal(2, dict.Count); + } + + [Fact] + public void GetValueRefOrNullRefLinkBreaksOnResize() + { + var dict = new Dictionary + { + { 1, new() } + }; + + Assert.Equal(1, dict.Count); + + ref var itemRef = ref CollectionsMarshal.GetValueRefOrNullRef(dict, 1); + + Assert.Equal(0, itemRef.Value); + Assert.Equal(0, itemRef.Property); + + itemRef.Value = 1; + itemRef.Property = 2; + + Assert.Equal(1, itemRef.Value); + Assert.Equal(2, itemRef.Property); + Assert.Equal(dict[1].Value, itemRef.Value); + Assert.Equal(dict[1].Property, itemRef.Property); + + // Resize + dict.EnsureCapacity(100); + for (int i = 2; i <= 50; i++) + { + dict.Add(i, new()); + } + + itemRef.Value = 3; + itemRef.Property = 4; + + Assert.Equal(3, itemRef.Value); + Assert.Equal(4, itemRef.Property); + + // Check connection broken + Assert.NotEqual(dict[1].Value, itemRef.Value); + Assert.NotEqual(dict[1].Property, itemRef.Property); + + Assert.Equal(50, dict.Count); + } + + private struct Struct + { + public int Value; + public int Property { get; set; } + } + private class IntAsObject { public int Value; + public int Property { get; set; } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs index f299734d5a9a1..dd3ccea3e2dc4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/Marshal/OffsetOfTests.cs @@ -33,6 +33,7 @@ public void OffsetOf_ClassWithSequentialLayout_ReturnsExpected() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49872", TestPlatforms.Android)] public void OffsetOf_ExplicitLayout_ReturnsExpected() { Type t = typeof(ExplicitLayoutTest); @@ -105,6 +106,7 @@ public void OffsetOf_ValidField_ReturnsExpected() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49872", TestPlatforms.Android)] public void OffsetOf_Decimal_ReturnsExpected() { Type t = typeof(FieldAlignmentTest_Decimal); diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/RuntimeEnvironmentTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/RuntimeEnvironmentTests.cs index fec7ca85fbe98..71458d63dfaef 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/RuntimeEnvironmentTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/RuntimeEnvironmentTests.cs @@ -23,19 +23,25 @@ public void RuntimeEnvironmentSysVersion() [Fact] public void SystemConfigurationFile_Get_ThrowsPlatformNotSupportedException() { +#pragma warning disable 618 // SystemConfigurationFile is marked as Obsolete Assert.Throws(() => RuntimeEnvironment.SystemConfigurationFile); +#pragma warning restore 618 } [Fact] public void GetRuntimeInterfaceAsObject_Invoke_ThrowsPlatformNotSupportedException() { +#pragma warning disable 618 // GetRuntimeInterfaceAsObject is marked as Obsolete Assert.Throws(() => RuntimeEnvironment.GetRuntimeInterfaceAsObject(Guid.Empty, Guid.Empty)); +#pragma warning restore 618 } [Fact] public void GetRuntimeInterfaceAsIntPtr_Invoke_ThrowsPlatformNotSupportedException() { +#pragma warning disable 618 // GetRuntimeInterfaceAsIntPtr is marked as Obsolete Assert.Throws(() => RuntimeEnvironment.GetRuntimeInterfaceAsIntPtr(Guid.Empty, Guid.Empty)); +#pragma warning restore 618 } [Fact] diff --git a/src/libraries/System.Runtime.Loader/tests/SatelliteAssemblies.cs b/src/libraries/System.Runtime.Loader/tests/SatelliteAssemblies.cs index b97b36e1a308f..4f5e626513579 100644 --- a/src/libraries/System.Runtime.Loader/tests/SatelliteAssemblies.cs +++ b/src/libraries/System.Runtime.Loader/tests/SatelliteAssemblies.cs @@ -189,7 +189,6 @@ public static IEnumerable SatelliteLoadsCorrectly_TestData() [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))] [MemberData(nameof(SatelliteLoadsCorrectly_TestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/43103", TestPlatforms.Browser)] public void SatelliteLoadsCorrectly_FromName(string alc, string assemblyName, string culture) { AssemblyName satelliteAssemblyName = new AssemblyName(assemblyName + ".resources"); diff --git a/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs index 590ec9028c5f8..6651d7cce6454 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.BigInteger.cs @@ -6,7 +6,7 @@ namespace System.Globalization { - internal partial class FormatProvider + internal static partial class FormatProvider { internal static void FormatBigInteger(ref ValueStringBuilder sb, int precision, int scale, bool sign, ReadOnlySpan format, NumberFormatInfo numberFormatInfo, char[] digits, int startIndex) { diff --git a/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs b/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs index 81e59331adf66..9f4f62cf64fd8 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Globalization/FormatProvider.NumberBuffer.cs @@ -6,9 +6,9 @@ namespace System.Globalization { - internal partial class FormatProvider + internal static partial class FormatProvider { - private partial class Number + private static partial class Number { [StructLayout(LayoutKind.Sequential)] internal unsafe struct NumberBuffer diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index 726e759def658..5f4fb2aee9aec 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -606,7 +606,7 @@ internal static bool TryFormatBigInteger(BigInteger value, ReadOnlySpan fo { if (fmt == 'g' || fmt == 'G' || fmt == 'r' || fmt == 'R') { - formatSpan = formatString = digits > 0 ? string.Format("D{0}", digits) : "D"; + formatSpan = formatString = digits > 0 ? $"D{digits}" : "D"; } if (targetSpan) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs index 78b7c8124db91..0e2886e5d761d 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs @@ -388,20 +388,11 @@ public override int GetHashCode() return finalHash; } - public override string ToString() - { - return string.Format(CultureInfo.CurrentCulture, "({0}, {1})", m_real, m_imaginary); - } + public override string ToString() => $"({m_real}, {m_imaginary})"; - public string ToString(string? format) - { - return string.Format(CultureInfo.CurrentCulture, "({0}, {1})", m_real.ToString(format, CultureInfo.CurrentCulture), m_imaginary.ToString(format, CultureInfo.CurrentCulture)); - } + public string ToString(string? format) => ToString(format, null); - public string ToString(IFormatProvider? provider) - { - return string.Format(provider, "({0}, {1})", m_real, m_imaginary); - } + public string ToString(IFormatProvider? provider) => ToString(null, provider); public string ToString(string? format, IFormatProvider? provider) { diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/ctor.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/ctor.cs index a980cc9b723a3..83b1ebd8673a8 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/ctor.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/ctor.cs @@ -677,6 +677,10 @@ public static void RunCtorByteArrayTests() // ctor(byte[]): array is 1 byte tempUInt64 = (uint)s_random.Next(0, 256); tempByteArray = BitConverter.GetBytes(tempUInt64); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } if (tempByteArray[0] > 127) { VerifyCtorByteArray(new byte[] { tempByteArray[0] }); @@ -719,6 +723,10 @@ public static void RunCtorByteArrayTests() { tempUInt64 = unchecked((uint)s_random.Next(int.MinValue, int.MaxValue)); tempByteArray = BitConverter.GetBytes(tempUInt64); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } VerifyCtorByteArray( new byte[] { @@ -741,6 +749,10 @@ public static void RunCtorByteArrayTests() { tempUInt64 = unchecked((uint)s_random.Next(int.MinValue, int.MaxValue)); tempByteArray = BitConverter.GetBytes(tempUInt64); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } if (tempUInt64 > int.MaxValue) { @@ -781,6 +793,10 @@ public static void RunCtorByteArrayTests() tempUInt64 <<= 8; tempUInt64 += (ulong)s_random.Next(0, 256); tempByteArray = BitConverter.GetBytes(tempUInt64); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } if (tempUInt64 >= (ulong)0x00080000) { @@ -823,6 +839,10 @@ public static void RunCtorByteArrayTests() tempUInt64 <<= 32; tempUInt64 += unchecked((uint)s_random.Next(int.MinValue, int.MaxValue)); tempByteArray = BitConverter.GetBytes(tempUInt64); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } if (tempUInt64 > long.MaxValue) { @@ -1073,11 +1093,21 @@ private static void VerifyCtorByteArray(byte[] value) tempBigInteger = tempBigInteger + (new BigInteger(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0xFF })); } + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } Assert.Equal(BitConverter.ToInt64(tempByteArray, 0), (long)tempBigInteger); } else { - Assert.Equal(BitConverter.ToInt64(value, 0), (long)bigInteger); + byte[] tempByteArray = new byte[8]; + Array.Copy(value, tempByteArray, 8); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } + Assert.Equal(BitConverter.ToInt64(tempByteArray, 0), (long)bigInteger); } if (IsOutOfRangeUInt64(value)) @@ -1108,11 +1138,21 @@ private static void VerifyCtorByteArray(byte[] value) } } + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } Assert.Equal(BitConverter.ToUInt64(tempByteArray, 0), (ulong)tempBigInteger); } else { - Assert.Equal(BitConverter.ToUInt64(value, 0), (ulong)bigInteger); + byte[] tempByteArray = new byte[8]; + Array.Copy(value, tempByteArray, 8); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray); + } + Assert.Equal(BitConverter.ToUInt64(tempByteArray, 0), (ulong)bigInteger); } VerifyBigIntegerUsingIdentities(bigInteger, isZero); diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs index 3beccaa84b1c7..47b0a8ea7678c 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs @@ -131,6 +131,10 @@ public static void RunLeftShiftTests() { tempByteArray1 = GetRandomPosByteArray(s_random, 100); tempByteArray2 = BitConverter.GetBytes(s_random.Next(-1000, -8 * tempByteArray1.Length)); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray2); + } VerifyLeftShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b<<"); } @@ -139,6 +143,10 @@ public static void RunLeftShiftTests() { tempByteArray1 = GetRandomNegByteArray(s_random, 100); tempByteArray2 = BitConverter.GetBytes(s_random.Next(-1000, -8 * tempByteArray1.Length)); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray2); + } VerifyLeftShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b<<"); } } diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs index 7939efc23043f..5107f0ed7d767 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs @@ -146,6 +146,10 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomPosByteArray(s_random, 100); tempByteArray2 = BitConverter.GetBytes(s_random.Next(8 * tempByteArray1.Length, 1000)); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray2); + } VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); } @@ -154,6 +158,10 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomNegByteArray(s_random, 100); tempByteArray2 = BitConverter.GetBytes(s_random.Next(8 * tempByteArray1.Length, 1000)); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(tempByteArray2); + } VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); } } diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatterEventSource.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatterEventSource.cs index ca8efd4c0978a..70ebce0d4e004 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatterEventSource.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/Formatters/Binary/BinaryFormatterEventSource.cs @@ -93,7 +93,7 @@ private void DeserializingObject(string? typeName) WriteEvent(EventId_DeserializingObject, typeName); } - public class Keywords + public static class Keywords { public const EventKeywords Serialization = (EventKeywords)1; public const EventKeywords Deserialization = (EventKeywords)2; diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs index 805b1ad4df481..d955b831e45a6 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterEventSourceTests.cs @@ -12,6 +12,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static class BinaryFormatterEventSourceTests { private const string BinaryFormatterEventSourceName = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource"; diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs index 7b049103648e4..155346f681de8 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -18,6 +19,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public partial class BinaryFormatterTests : FileCleanupTestBase { // On 32-bit we can't test these high inputs as they cause OutOfMemoryExceptions. @@ -95,6 +97,17 @@ private static void ValidateAndRoundtrip(object obj, TypeSerializableValue[] blo CheckObjectTypeIntegrity(customSerializableObj); } + // TimeZoneInfo objects have three properties (DisplayName, StandardName, DaylightName) + // that are localized. Since the blobs were generated from the invariant culture, they + // will have English strings embedded. Thus, we can only test them against English + // language cultures or the invariant culture. + if (obj is TimeZoneInfo && ( + CultureInfo.CurrentUICulture.TwoLetterISOLanguageName != "en" || + CultureInfo.CurrentUICulture.Name.Length != 0)) + { + return; + } + SanityCheckBlob(obj, blobs); // ReflectionTypeLoadException and LicenseException aren't deserializable from Desktop --> Core. diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterServicesTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterServicesTests.cs index 0a04f250a3f8a..6d2bca0f36660 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterServicesTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterServicesTests.cs @@ -9,6 +9,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public partial class FormatterServicesTests { [Fact] diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterTests.cs index 68ca34ee79d6a..e1195cd5908e7 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/FormatterTests.cs @@ -8,6 +8,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class FormatterTests { [Fact] diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationInfoTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationInfoTests.cs index 30b72ce09fc54..e135157920a7b 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationInfoTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/SerializationInfoTests.cs @@ -6,6 +6,7 @@ namespace System.Runtime.Serialization.Formatters.Tests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public class SerializationInfoTests { [Fact] diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index b75df7a01250a..91d7ee5312116 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -21,6 +21,7 @@ using System.Runtime.Serialization.Tests; +[ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static partial class DataContractSerializerTests { #if ReflectionOnly @@ -3811,7 +3812,7 @@ public static void DCS_BasicPerSerializerRoundTripAndCompare_EnumStruct() foreach (var type in typelist) { var possibleValues = Enum.GetValues(type); - var input = possibleValues.GetValue(new Random().Next(possibleValues.Length)); + var input = possibleValues.GetValue(Random.Shared.Next(possibleValues.Length)); string baseline = $"<_data i:type=\"a:{type}***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/{type}***\">{input}<_data2 i:type=\"a:{type}***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/{type}***\">{input}"; var value = new SerializationTestTypes.ObjectContainer(input); var actual = DataContractSerializerHelper.SerializeAndDeserialize(value, baseline, setting); diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs index 24bf45c156448..81d2acff15d42 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/SerializationTestTypes/SampleTypes.cs @@ -1432,7 +1432,7 @@ public bool IsReadOnly public bool Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } public IEnumerator GetEnumerator() @@ -1518,7 +1518,7 @@ public bool IsReadOnly public bool Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } public IEnumerator GetEnumerator() @@ -1604,7 +1604,7 @@ public bool IsReadOnly public bool Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } public IEnumerator GetEnumerator() @@ -1690,7 +1690,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -1776,7 +1776,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -1862,7 +1862,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -1948,7 +1948,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(PublicDCClassPrivateDM item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -2034,7 +2034,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(PrivateDC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -2094,7 +2094,7 @@ public bool IsReadOnly public bool Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } public IEnumerator GetEnumerator() @@ -2153,7 +2153,7 @@ public bool IsReadOnly public bool Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } public IEnumerator GetEnumerator() @@ -2212,7 +2212,7 @@ public bool IsReadOnly public bool Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } public IEnumerator GetEnumerator() @@ -2271,7 +2271,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -2330,7 +2330,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -2389,7 +2389,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(DC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -2448,7 +2448,7 @@ bool ICollection.IsReadOnly bool ICollection.Remove(PrivateDC item) { - return _internalList.Remove(item); ; + return _internalList.Remove(item); } IEnumerator IEnumerable.GetEnumerator() @@ -3245,7 +3245,7 @@ public object this[object key] } set { - _data[key] = value; ; + _data[key] = value; } } @@ -3343,7 +3343,7 @@ object IDictionary.this[object key] } set { - _data[key] = value; ; + _data[key] = value; } } @@ -3441,7 +3441,7 @@ public object this[object key] } set { - _data[key] = value; ; + _data[key] = value; } } @@ -3536,7 +3536,7 @@ public PublicDC this[PublicDC key] } set { - _data[key] = value; ; + _data[key] = value; } } @@ -3684,7 +3684,7 @@ public PublicDC this[PublicDC key] } set { - _data[key] = value; ; + _data[key] = value; } } @@ -3844,7 +3844,7 @@ public object this[object key] } set { - _data[key] = value; ; + _data[key] = value; } } diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj index 3080bd813ac9a..3992b33bcfa3a 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/System.Runtime.Serialization.Xml.Tests.csproj @@ -15,10 +15,8 @@ - - + + @@ -29,6 +27,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs new file mode 100644 index 0000000000000..089e45de11310 --- /dev/null +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/XmlSerializerTests.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using Xunit; + +public static class XmlSerializerTests +{ + + public const string FakeNS = "http://example.com/XmlSerializerTests"; + + [Fact] + public static void FlagEnums_With_Different_Namespaces() + { + StringWriter sw = new StringWriter(); + XmlTextWriter xml = new XmlTextWriter(sw); + + TwoClasses twoClasses = new TwoClasses + { + First = new FirstClass { TestingEnumValues = TestEnum.First }, + Second = new SecondClass { TestingEnumValues = TestEnum.Second } + }; + + // 43675 - This line throws with inconsistent Flag.type/namespace usage + XmlSerializer ser = new XmlSerializer(typeof(TwoClasses)); + + ser.Serialize(xml, twoClasses); + string s = sw.ToString(); + + Assert.Contains("enumtest", s); + } + + [Flags] + public enum TestEnum + { + First = 1, + Second = 2 + } + + public class FirstClass + { + [XmlAttribute("enumtest")] + public TestEnum TestingEnumValues; + } + + public class SecondClass + { + [XmlAttribute("enumtest", Namespace = XmlSerializerTests.FakeNS)] + public TestEnum TestingEnumValues; + } + + public class TwoClasses + { + public TwoClasses() { } + + [XmlElement("first")] + public FirstClass First; + + [XmlElement("second", Namespace = XmlSerializerTests.FakeNS)] + public SecondClass Second; + } + +} diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index dbedabfa893a9..6292cc6b7e7db 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3191,6 +3191,7 @@ public virtual void NextBytes(System.Span buffer) { } public virtual float NextSingle() { throw null; } public virtual double NextDouble() { throw null; } protected virtual double Sample() { throw null; } + public static System.Random Shared { get { throw null; } } } public readonly partial struct Range : System.IEquatable { @@ -3699,6 +3700,8 @@ protected StringComparer() { } public static System.StringComparer FromComparison(System.StringComparison comparisonType) { throw null; } public int GetHashCode(object obj) { throw null; } public abstract int GetHashCode(string obj); + public static bool IsWellKnownCultureAwareComparer(System.Collections.Generic.IEqualityComparer? comparer, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Globalization.CompareInfo? compareInfo, out System.Globalization.CompareOptions compareOptions) { throw null; } + public static bool IsWellKnownOrdinalComparer(System.Collections.Generic.IEqualityComparer? comparer, out bool ignoreCase) { throw null; } } public enum StringComparison { @@ -5651,7 +5654,8 @@ public DefaultValueAttribute(object? value) { } public DefaultValueAttribute(sbyte value) { } public DefaultValueAttribute(float value) { } public DefaultValueAttribute(string? value) { } - public DefaultValueAttribute(System.Type type, string? value) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All.")] + public DefaultValueAttribute([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type type, string? value) { } [System.CLSCompliantAttribute(false)] public DefaultValueAttribute(ushort value) { } [System.CLSCompliantAttribute(false)] @@ -5911,7 +5915,7 @@ public sealed partial class DoesNotReturnIfAttribute : System.Attribute public DoesNotReturnIfAttribute(bool parameterValue) { } public bool ParameterValue { get { throw null; } } } - [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.GenericParameter | System.AttributeTargets.Method | System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue, Inherited=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.GenericParameter | System.AttributeTargets.Method | System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, Inherited=false)] public sealed partial class DynamicallyAccessedMembersAttribute : System.Attribute { public DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes memberTypes) { } @@ -6007,7 +6011,7 @@ public RequiresAssemblyFilesAttribute() { } public string? Message { get { throw null; } set { } } public string? Url { get { throw null; } set { } } } - [System.AttributeUsageAttribute(System.AttributeTargets.Constructor | System.AttributeTargets.Method, Inherited=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Constructor | System.AttributeTargets.Method, Inherited=false)] public sealed partial class RequiresUnreferencedCodeAttribute : System.Attribute { public RequiresUnreferencedCodeAttribute(string message) { } @@ -9113,7 +9117,7 @@ public sealed partial class AsyncIteratorStateMachineAttribute : System.Runtime. { public AsyncIteratorStateMachineAttribute(System.Type stateMachineType) : base (default(System.Type)) { } } - [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Interface | System.AttributeTargets.Struct, Inherited=false, AllowMultiple=false)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Interface | System.AttributeTargets.Method | System.AttributeTargets.Struct, Inherited=false, AllowMultiple=false)] public sealed partial class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethodBuilderAttribute(System.Type builderType) { } @@ -9197,6 +9201,10 @@ public partial class CallConvFastcall { public CallConvFastcall() { } } + public partial class CallConvMemberFunction + { + public CallConvMemberFunction() { } + } public partial class CallConvStdcall { public CallConvStdcall() { } @@ -9381,6 +9389,7 @@ public DependencyAttribute(string dependentAssemblyArgument, System.Runtime.Comp public string DependentAssembly { get { throw null; } } public System.Runtime.CompilerServices.LoadHint LoadHint { get { throw null; } } } + [System.ObsoleteAttribute("DisablePrivateReflectionAttribute has no effect in .NET 6.0+ applications.", DiagnosticId = "SYSLIB0015", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [System.AttributeUsageAttribute(System.AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] public sealed partial class DisablePrivateReflectionAttribute : System.Attribute { @@ -9518,6 +9527,33 @@ public sealed partial class ModuleInitializerAttribute : System.Attribute { public ModuleInitializerAttribute() { } } + public partial struct PoolingAsyncValueTaskMethodBuilder + { + private object _dummy; + private int _dummyPrimitive; + public System.Threading.Tasks.ValueTask Task { get { throw null; } } + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public static System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder Create() { throw null; } + public void SetException(System.Exception exception) { } + public void SetResult() { } + public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } + public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + } + public partial struct PoolingAsyncValueTaskMethodBuilder + { + private TResult _result; + private object _dummy; + private int _dummyPrimitive; + public System.Threading.Tasks.ValueTask Task { get { throw null; } } + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + public static System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder Create() { throw null; } + public void SetException(System.Exception exception) { } + public void SetResult(TResult result) { } + public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { } + public void Start(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { } + } [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=false, Inherited=false)] public sealed partial class PreserveBaseOverridesAttribute : System.Attribute { @@ -9747,6 +9783,7 @@ internal ExceptionDispatchInfo() { } public System.Exception SourceException { get { throw null; } } public static System.Runtime.ExceptionServices.ExceptionDispatchInfo Capture(System.Exception source) { throw null; } public static System.Exception SetCurrentStackTrace(System.Exception source) { throw null; } + public static System.Exception SetRemoteStackTrace(System.Exception source, string stackTrace) { throw null; } [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] public void Throw() => throw null; [System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] @@ -11014,6 +11051,7 @@ public void CancelAfter(System.TimeSpan delay) { } public static System.Threading.CancellationTokenSource CreateLinkedTokenSource(params System.Threading.CancellationToken[] tokens) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } + public bool TryReset() { throw null; } } public enum LazyThreadSafetyMode { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index d8ab9e64e36a8..e4b0b3580f585 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -6,9 +6,6 @@ true $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser disable - - true (() => Activator.CreateInstance()); [Fact] - public void CreateInstanceT_StructWithoutDefaultConstructor_ThrowsMissingMethodException() => + public void CreateInstanceT_StructWithoutDefaultConstructor_InvokesConstructor() => Activator.CreateInstance(); [Fact] diff --git a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs index 05e07447a9616..905c06a4bbdab 100644 --- a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs +++ b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs @@ -560,7 +560,7 @@ public static void TestingActivationAttributes1() Activator.CreateInstance(typeof(ClassWithIsTestedAttribute), null, new object[] { }); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void CreateInstance_NonPublicValueTypeWithPrivateDefaultConstructor_Success() { AssemblyName assemblyName = new AssemblyName("Assembly"); @@ -589,7 +589,7 @@ public void CreateInstance_NonPublicValueTypeWithPrivateDefaultConstructor_Succe Assert.Equal(-1, field.GetValue(v2)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void CreateInstance_PublicOnlyValueTypeWithPrivateDefaultConstructor_ThrowsMissingMethodException() { AssemblyName assemblyName = new AssemblyName("Assembly"); @@ -796,7 +796,7 @@ public static void CreateInstanceAssemblyResolve() }).Dispose(); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public void CreateInstance_TypeBuilder_ThrowsNotSupportedException() { AssemblyName assemblyName = new AssemblyName("Assembly"); diff --git a/src/libraries/System.Runtime/tests/System/ArrayTests.cs b/src/libraries/System.Runtime/tests/System/ArrayTests.cs index 54bf303d2e266..7f97f95f4507c 100644 --- a/src/libraries/System.Runtime/tests/System/ArrayTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArrayTests.cs @@ -3241,7 +3241,7 @@ public static void Reverse_MultidimensionalArray_ThrowsRankException() Assert.Throws(() => Array.Reverse((Array)new int[10, 10], 0, 0)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] [InlineData(0)] [InlineData(-1)] public static void Reverse_IndexLessThanLowerBound_ThrowsArgumentOutOfRangeException(int lowerBound) @@ -3249,7 +3249,7 @@ public static void Reverse_IndexLessThanLowerBound_ThrowsArgumentOutOfRangeExcep AssertExtensions.Throws("index", () => Array.Reverse(NonZeroLowerBoundArray(new int[0], lowerBound), lowerBound - 1, 0)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] public static void Reverse_IndexLessThanPositiveLowerBound_ThrowsArgumentOutOfRangeException() { AssertExtensions.Throws("index", "length", () => Array.Reverse(NonZeroLowerBoundArray(new int[0], 1), 0, 0)); @@ -4312,7 +4312,7 @@ public static void Reverse_Generic_InvalidOffsetPlusLength_ThrowsArgumentExcepti AssertExtensions.Throws(null, () => Array.Reverse(new string[arrayLength], index, length)); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNonZeroLowerBoundArraySupported))] public static void Reverse_NonSZArrayWithMinValueLowerBound() { Array array = NonZeroLowerBoundArray(new int[] { 1, 2, 3 }, int.MinValue); diff --git a/src/libraries/System.Runtime/tests/System/Attributes.cs b/src/libraries/System.Runtime/tests/System/Attributes.cs index a55e86f2f2230..9265e8f6d9610 100644 --- a/src/libraries/System.Runtime/tests/System/Attributes.cs +++ b/src/libraries/System.Runtime/tests/System/Attributes.cs @@ -267,7 +267,7 @@ public static void NegTest1() Assert.Throws(() => Attribute.GetCustomAttribute(element, attributeType)); attributeType = null; - element = typeof(AttributeGetCustomAttributes).Assembly; ; + element = typeof(AttributeGetCustomAttributes).Assembly; Assert.Throws(() => Attribute.GetCustomAttribute(element, attributeType)); attributeType = typeof(myClass); AssertExtensions.Throws(null, () => Attribute.GetCustomAttribute(element, attributeType)); @@ -309,7 +309,7 @@ public static void NegTest2() Assert.Throws(() => Attribute.GetCustomAttribute(element, attributeType, true)); attributeType = null; - element = typeof(AttributeGetCustomAttributes).Assembly; ; + element = typeof(AttributeGetCustomAttributes).Assembly; Assert.Throws(() => Attribute.GetCustomAttribute(element, attributeType, false)); attributeType = typeof(myClass); AssertExtensions.Throws(null, () => Attribute.GetCustomAttribute(element, attributeType, true)); diff --git a/src/libraries/System.Runtime/tests/System/BooleanTests.cs b/src/libraries/System.Runtime/tests/System/BooleanTests.cs index 40bb15d1c5325..c0594ffcc0fa7 100644 --- a/src/libraries/System.Runtime/tests/System/BooleanTests.cs +++ b/src/libraries/System.Runtime/tests/System/BooleanTests.cs @@ -74,6 +74,40 @@ public void Parse_InvalidValue_ThrowsException(string value, Type exceptionType) Assert.False(bool.TryParse(value, out bool result)); Assert.False(result); } + + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1] }; + } + + yield return new object[] { " \0 \0 TrueFalse \0 ", 6, 4, true }; + yield return new object[] { " \0 \0 TrueFalse \0 ", 10, 5, false }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, bool expected) + { + Assert.Equal(expected, bool.Parse(value.AsSpan(offset, count))); + + Assert.True(bool.TryParse(value.AsSpan(offset, count), out bool result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => bool.Parse(value.AsSpan())); + + Assert.False(bool.TryParse(value.AsSpan(), out bool result)); + Assert.False(result); + } + } [Theory] [InlineData(true, "True")] @@ -146,40 +180,6 @@ public void GetTypeCode_Invoke_ReturnsBoolean() Assert.Equal(TypeCode.Boolean, true.GetTypeCode()); } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() - { - foreach (object[] inputs in Parse_Valid_TestData()) - { - yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1] }; - } - - yield return new object[] { " \0 \0 TrueFalse \0 ", 6, 4, true }; - yield return new object[] { " \0 \0 TrueFalse \0 ", 10, 5, false }; - } - - [Theory] - [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] - public static void Parse_Span_Valid(string value, int offset, int count, bool expected) - { - Assert.Equal(expected, bool.Parse(value.AsSpan(offset, count))); - - Assert.True(bool.TryParse(value.AsSpan(offset, count), out bool result)); - Assert.Equal(expected, result); - } - - [Theory] - [MemberData(nameof(Parse_Invalid_TestData))] - public static void Parse_Span_Invalid(string value, Type exceptionType) - { - if (value != null) - { - Assert.Throws(exceptionType, () => bool.Parse(value.AsSpan())); - - Assert.False(bool.TryParse(value.AsSpan(), out bool result)); - Assert.False(result); - } - } - [Theory] [InlineData(true, "True")] [InlineData(false, "False")] diff --git a/src/libraries/System.Runtime/tests/System/BufferTests.cs b/src/libraries/System.Runtime/tests/System/BufferTests.cs index 36cccb65f9443..503c28d4154f7 100644 --- a/src/libraries/System.Runtime/tests/System/BufferTests.cs +++ b/src/libraries/System.Runtime/tests/System/BufferTests.cs @@ -91,6 +91,10 @@ public static void ByteLength_Invalid() [InlineData(new uint[] { 0x01234567, 0x89abcdef }, 7, 0x89)] public static void GetByte(Array array, int index, int expected) { + if (!BitConverter.IsLittleEndian) + { + index = index ^ 3; + } Assert.Equal(expected, Buffer.GetByte(array, index)); } @@ -211,6 +215,10 @@ public static unsafe void MemoryCopy_Invalid() [InlineData(new uint[] { 0x01234542, 0x89abcdef }, 7, 0xa2, new uint[] { 0x01234542, 0xa2abcdef })] public static void SetByte(Array array, int index, byte value, Array expected) { + if (!BitConverter.IsLittleEndian) + { + index = index ^ 3; + } Buffer.SetByte(array, index, value); Assert.Equal(expected, array); } diff --git a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs index 4803a880bfb64..d2b40c647dfd3 100644 --- a/src/libraries/System.Runtime/tests/System/DateTimeTests.cs +++ b/src/libraries/System.Runtime/tests/System/DateTimeTests.cs @@ -1038,11 +1038,11 @@ public static void Parse_InvalidArguments_Throws() Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000-07:00c")); Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000-07:00c", new MyFormatter())); Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000-07:00c", new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); - + Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000+00:00#")); Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000+00:00#", new MyFormatter())); Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000+00:00#", new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); - + Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000+00:00#\0")); Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000+00:00#\0", new MyFormatter())); Assert.Throws(() => DateTime.Parse("2020-5-7T09:37:00.0000000+00:00#\0", new MyFormatter(), DateTimeStyles.NoCurrentDateDefault)); @@ -2060,6 +2060,54 @@ public static IEnumerable ToString_MatchesExpected_MemberData() } } + [Theory] + [MemberData(nameof(Parse_ValidInput_Succeeds_MemberData))] + public static void Parse_Span_ValidInput_Succeeds(string input, CultureInfo culture, DateTime? expected) + { + Assert.Equal(expected, DateTime.Parse(input.AsSpan(), culture)); + } + + [Theory] + [MemberData(nameof(ParseExact_ValidInput_Succeeds_MemberData))] + public static void ParseExact_Span_ValidInput_Succeeds(string input, string format, CultureInfo culture, DateTimeStyles style, DateTime? expected) + { + DateTime result1 = DateTime.ParseExact(input.AsSpan(), format, culture, style); + DateTime result2 = DateTime.ParseExact(input.AsSpan(), new[] { format }, culture, style); + + Assert.True(DateTime.TryParseExact(input.AsSpan(), format, culture, style, out DateTime result3)); + Assert.True(DateTime.TryParseExact(input.AsSpan(), new[] { format }, culture, style, out DateTime result4)); + + Assert.Equal(result1, result2); + Assert.Equal(result1, result3); + Assert.Equal(result1, result4); + + if (expected != null) // some inputs don't roundtrip well + { + // Normalize values to make comparison easier + if (expected.Value.Kind != DateTimeKind.Utc) + { + expected = expected.Value.ToUniversalTime(); + } + if (result1.Kind != DateTimeKind.Utc) + { + result1 = result1.ToUniversalTime(); + } + + Assert.Equal(expected, result1); + } + } + + [Theory] + [MemberData(nameof(ParseExact_InvalidInputs_Fail_MemberData))] + public static void ParseExact_Span_InvalidInputs_Fail(string input, string format, CultureInfo culture, DateTimeStyles style) + { + Assert.Throws(() => DateTime.ParseExact(input.AsSpan(), format, culture, style)); + Assert.Throws(() => DateTime.ParseExact(input.AsSpan(), new[] { format }, culture, style)); + + Assert.False(DateTime.TryParseExact(input.AsSpan(), format, culture, style, out DateTime result)); + Assert.False(DateTime.TryParseExact(input.AsSpan(), new[] { format }, culture, style, out result)); + } + [Theory] [MemberData(nameof(ToString_MatchesExpected_MemberData))] public void ToString_Invoke_ReturnsExpected(DateTime dateTime, string format, IFormatProvider provider, string expected) @@ -2318,54 +2366,6 @@ public static void TryFormat_MatchesExpected(DateTime dateTime, string format, I Assert.Equal(expected, new string(destination)); } - [Theory] - [MemberData(nameof(Parse_ValidInput_Succeeds_MemberData))] - public static void Parse_Span_ValidInput_Succeeds(string input, CultureInfo culture, DateTime? expected) - { - Assert.Equal(expected, DateTime.Parse(input.AsSpan(), culture)); - } - - [Theory] - [MemberData(nameof(ParseExact_ValidInput_Succeeds_MemberData))] - public static void ParseExact_Span_ValidInput_Succeeds(string input, string format, CultureInfo culture, DateTimeStyles style, DateTime? expected) - { - DateTime result1 = DateTime.ParseExact(input.AsSpan(), format, culture, style); - DateTime result2 = DateTime.ParseExact(input.AsSpan(), new[] { format }, culture, style); - - Assert.True(DateTime.TryParseExact(input.AsSpan(), format, culture, style, out DateTime result3)); - Assert.True(DateTime.TryParseExact(input.AsSpan(), new[] { format }, culture, style, out DateTime result4)); - - Assert.Equal(result1, result2); - Assert.Equal(result1, result3); - Assert.Equal(result1, result4); - - if (expected != null) // some inputs don't roundtrip well - { - // Normalize values to make comparison easier - if (expected.Value.Kind != DateTimeKind.Utc) - { - expected = expected.Value.ToUniversalTime(); - } - if (result1.Kind != DateTimeKind.Utc) - { - result1 = result1.ToUniversalTime(); - } - - Assert.Equal(expected, result1); - } - } - - [Theory] - [MemberData(nameof(ParseExact_InvalidInputs_Fail_MemberData))] - public static void ParseExact_Span_InvalidInputs_Fail(string input, string format, CultureInfo culture, DateTimeStyles style) - { - Assert.Throws(() => DateTime.ParseExact(input.AsSpan(), format, culture, style)); - Assert.Throws(() => DateTime.ParseExact(input.AsSpan(), new[] { format }, culture, style)); - - Assert.False(DateTime.TryParseExact(input.AsSpan(), format, culture, style, out DateTime result)); - Assert.False(DateTime.TryParseExact(input.AsSpan(), new[] { format }, culture, style, out result)); - } - [Fact] public static void UnixEpoch() { diff --git a/src/libraries/System.Runtime/tests/System/DecimalTests.cs b/src/libraries/System.Runtime/tests/System/DecimalTests.cs index 35069a81e6161..dcdc4aca29c1f 100644 --- a/src/libraries/System.Runtime/tests/System/DecimalTests.cs +++ b/src/libraries/System.Runtime/tests/System/DecimalTests.cs @@ -910,6 +910,60 @@ public static void Parse_Invalid(string value, NumberStyles style, IFormatProvid } } + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + yield return new object[] { "-123", 1, 3, NumberStyles.Number, null, 123m }; + yield return new object[] { "-123", 0, 3, NumberStyles.Number, null, -12m }; + yield return new object[] { 1000.ToString("N0"), 0, 4, NumberStyles.AllowThousands, null, 100m }; + yield return new object[] { 1000.ToString("N0"), 2, 3, NumberStyles.AllowThousands, null, 0m }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, 123m }; + yield return new object[] { "1234567890123456789012345.678456", 1, 4, NumberStyles.Number, new NumberFormatInfo() { NumberDecimalSeparator = "." }, 2345m }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, decimal expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + decimal result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(decimal.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, decimal.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, decimal.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.Equal(expected, decimal.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(decimal.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => decimal.Parse(value.AsSpan(), style, provider)); + + Assert.False(decimal.TryParse(value.AsSpan(), style, provider, out decimal result)); + Assert.Equal(0, result); + } + } + public static IEnumerable Remainder_Valid_TestData() { decimal NegativeZero = new decimal(0, 0, 0, true, 0); @@ -2264,60 +2318,6 @@ public void Round_InvalidMidpointRounding_ThrowsArgumentException(MidpointRoundi AssertExtensions.Throws("mode", () => decimal.Round(1, 2, mode)); } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() - { - foreach (object[] inputs in Parse_Valid_TestData()) - { - yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; - } - - yield return new object[] { "-123", 1, 3, NumberStyles.Number, null, 123m }; - yield return new object[] { "-123", 0, 3, NumberStyles.Number, null, -12m }; - yield return new object[] { 1000.ToString("N0"), 0, 4, NumberStyles.AllowThousands, null, 100m }; - yield return new object[] { 1000.ToString("N0"), 2, 3, NumberStyles.AllowThousands, null, 0m }; - yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, 123m }; - yield return new object[] { "1234567890123456789012345.678456", 1, 4, NumberStyles.Number, new NumberFormatInfo() { NumberDecimalSeparator = "." }, 2345m }; - } - - [Theory] - [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] - public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, decimal expected) - { - bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; - decimal result; - if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) - { - // Use Parse(string) or Parse(string, IFormatProvider) - if (isDefaultProvider) - { - Assert.True(decimal.TryParse(value.AsSpan(offset, count), out result)); - Assert.Equal(expected, result); - - Assert.Equal(expected, decimal.Parse(value.AsSpan(offset, count))); - } - - Assert.Equal(expected, decimal.Parse(value.AsSpan(offset, count), provider: provider)); - } - - Assert.Equal(expected, decimal.Parse(value.AsSpan(offset, count), style, provider)); - - Assert.True(decimal.TryParse(value.AsSpan(offset, count), style, provider, out result)); - Assert.Equal(expected, result); - } - - [Theory] - [MemberData(nameof(Parse_Invalid_TestData))] - public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) - { - if (value != null) - { - Assert.Throws(exceptionType, () => decimal.Parse(value.AsSpan(), style, provider)); - - Assert.False(decimal.TryParse(value.AsSpan(), style, provider, out decimal result)); - Assert.Equal(0, result); - } - } - [Fact] public static void TryFormat() { diff --git a/src/libraries/System.Runtime/tests/System/DelegateTests.cs b/src/libraries/System.Runtime/tests/System/DelegateTests.cs index 055fd8274e266..269b5f7b218af 100644 --- a/src/libraries/System.Runtime/tests/System/DelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System/DelegateTests.cs @@ -102,6 +102,22 @@ public static void DynamicInvoke() emptyDelegate.DynamicInvoke(null); } + private class SomeCustomConstantAttribute : CustomConstantAttribute + { + public static object Do(object o) => o; + + public override object Value => "SomeValue"; + } + + private delegate object ObjectDelegateWithSomeCustomConstantAttribute([SomeCustomConstant] object o); + + [Fact] + [SkipOnMono("https://github.com/dotnet/runtime/issues/49806")] + public static void DynamicInvoke_MissingTypeForCustomConstantAttribute_Succeeds() + { + Assert.Equal("SomeValue", (string)(new ObjectDelegateWithSomeCustomConstantAttribute(SomeCustomConstantAttribute.Do).DynamicInvoke(Type.Missing))); + } + [Fact] public static void DynamicInvoke_MissingTypeForDefaultParameter_Succeeds() { @@ -1101,6 +1117,28 @@ public static void CreateDelegate9_Type_Null() Assert.Null(ex.InnerException); Assert.NotNull(ex.Message); } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/49839", TestRuntimes.Mono)] + [Fact] + public static void CreateDelegate10_Nullable_Method() + { + int? num = 123; + MethodInfo mi = typeof(int?).GetMethod("ToString"); + NullableIntToString toString = (NullableIntToString)Delegate.CreateDelegate( + typeof(NullableIntToString), mi); + string s = toString(ref num); + Assert.Equal(num.ToString(), s); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/49839", TestRuntimes.Mono)] + [Fact] + public static void CreateDelegate10_Nullable_ClosedDelegate() + { + int? num = 123; + MethodInfo mi = typeof(int?).GetMethod("ToString"); + AssertExtensions.Throws( + () => Delegate.CreateDelegate(typeof(NullableIntToString), num, mi)); + } #endregion Tests #region Test Setup @@ -1207,6 +1245,8 @@ public interface Iface public delegate void D(C c); public delegate int E(C c); + + delegate string NullableIntToString(ref int? obj); #endregion Test Setup } } diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.cs index d0bfcfb41419c..2a0ba4a217c09 100644 --- a/src/libraries/System.Runtime/tests/System/DoubleTests.cs +++ b/src/libraries/System.Runtime/tests/System/DoubleTests.cs @@ -416,6 +416,60 @@ public static void Parse_Invalid(string value, NumberStyles style, IFormatProvid } } + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; + yield return new object[] { "-123", 0, 3, DefaultStyle, null, (double)-12 }; + yield return new object[] { "-123", 1, 3, DefaultStyle, null, (double)123 }; + yield return new object[] { "1E23", 0, 3, DefaultStyle, null, 1E2 }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, 123 }; + yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, double.PositiveInfinity }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, double expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + double result; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(double.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, double.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, double.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.Equal(expected, double.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(double.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => double.Parse(value.AsSpan(), style, provider)); + + Assert.False(double.TryParse(value.AsSpan(), style, provider, out double result)); + Assert.Equal(0, result); + } + } + [Fact] public static void PositiveInfinity() { @@ -606,60 +660,6 @@ public static void IsSubnormal(double d, bool expected) Assert.Equal(expected, double.IsSubnormal(d)); } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() - { - foreach (object[] inputs in Parse_Valid_TestData()) - { - yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; - } - - const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; - yield return new object[] { "-123", 0, 3, DefaultStyle, null, (double)-12 }; - yield return new object[] { "-123", 1, 3, DefaultStyle, null, (double)123 }; - yield return new object[] { "1E23", 0, 3, DefaultStyle, null, 1E2 }; - yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, 123 }; - yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, double.PositiveInfinity }; - } - - [Theory] - [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] - public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, double expected) - { - bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; - double result; - if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) - { - // Use Parse(string) or Parse(string, IFormatProvider) - if (isDefaultProvider) - { - Assert.True(double.TryParse(value.AsSpan(offset, count), out result)); - Assert.Equal(expected, result); - - Assert.Equal(expected, double.Parse(value.AsSpan(offset, count))); - } - - Assert.Equal(expected, double.Parse(value.AsSpan(offset, count), provider: provider)); - } - - Assert.Equal(expected, double.Parse(value.AsSpan(offset, count), style, provider)); - - Assert.True(double.TryParse(value.AsSpan(offset, count), style, provider, out result)); - Assert.Equal(expected, result); - } - - [Theory] - [MemberData(nameof(Parse_Invalid_TestData))] - public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) - { - if (value != null) - { - Assert.Throws(exceptionType, () => double.Parse(value.AsSpan(), style, provider)); - - Assert.False(double.TryParse(value.AsSpan(), style, provider, out double result)); - Assert.Equal(0, result); - } - } - [Fact] public static void TryFormat() { diff --git a/src/libraries/System.Runtime/tests/System/EnumTests.cs b/src/libraries/System.Runtime/tests/System/EnumTests.cs index 493729bc8dc4a..c065524a8f869 100644 --- a/src/libraries/System.Runtime/tests/System/EnumTests.cs +++ b/src/libraries/System.Runtime/tests/System/EnumTests.cs @@ -1979,23 +1979,20 @@ public static void Format_Invalid() public static IEnumerable UnsupportedEnumType_TestData() { - if (PlatformDetection.IsReflectionEmitSupported) - { - yield return new object[] { s_floatEnumType, 1.0f }; - yield return new object[] { s_doubleEnumType, 1.0 }; - yield return new object[] { s_intPtrEnumType, (IntPtr)1 }; - yield return new object[] { s_uintPtrEnumType, (UIntPtr)1 }; - } + yield return new object[] { s_floatEnumType, 1.0f }; + yield return new object[] { s_doubleEnumType, 1.0 }; + yield return new object[] { s_intPtrEnumType, (IntPtr)1 }; + yield return new object[] { s_uintPtrEnumType, (UIntPtr)1 }; } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(UnsupportedEnumType_TestData))] public static void GetName_Unsupported_ThrowsArgumentException(Type enumType, object value) { AssertExtensions.Throws("value", () => Enum.GetName(enumType, value)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(UnsupportedEnumType_TestData))] public static void IsDefined_UnsupportedEnumType_ThrowsInvalidOperationException(Type enumType, object value) { @@ -2012,7 +2009,7 @@ public static IEnumerable UnsupportedEnum_TestData() yield return new object[] { Enum.ToObject(s_uintPtrEnumType, 2) }; } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(UnsupportedEnum_TestData))] public static void ToString_UnsupportedEnumType_ThrowsArgumentException(Enum e) { @@ -2021,7 +2018,7 @@ public static void ToString_UnsupportedEnumType_ThrowsArgumentException(Enum e) Assert.True(formatXExceptionName == nameof(InvalidOperationException) || formatXExceptionName == "ContractException"); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(UnsupportedEnumType_TestData))] public static void Format_UnsupportedEnumType_ThrowsArgumentException(Type enumType, object value) { diff --git a/src/libraries/System.Runtime/tests/System/ExceptionTests.cs b/src/libraries/System.Runtime/tests/System/ExceptionTests.cs index 8c01f9a280c5a..fb83278ab318e 100644 --- a/src/libraries/System.Runtime/tests/System/ExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System/ExceptionTests.cs @@ -61,7 +61,7 @@ public static void Exception_GetBaseException() } [Fact] - public static void Exception_TargetSite_Jit() + public static void Exception_TargetSite() { bool caught = false; @@ -78,6 +78,32 @@ public static void Exception_TargetSite_Jit() Assert.True(caught); } + + static void RethrowException() + { + try + { + ThrowException(); + } + catch + { + throw; + } + } + + [Fact] + public static void Exception_TargetSite_OtherMethod() + { + Exception ex = Assert.ThrowsAny(() => ThrowException()); + Assert.Equal(nameof(ThrowException), ex.TargetSite.Name); + } + + [Fact] + public static void Exception_TargetSite_Rethrow() + { + Exception ex = Assert.ThrowsAny(() => RethrowException()); + Assert.Equal(nameof(ThrowException), ex.TargetSite.Name); + } [Fact] [ActiveIssue("https://github.com/mono/mono/issues/15140", TestRuntimes.Mono)] @@ -151,7 +177,15 @@ private static void VerifyCallStack( { try { - const string frameParserRegex = @"\s+at\s.+\.(?[^(.]+)\([^)]*\)\sin\s(?.*)\:line\s(?[\d]+)"; + string frameParserRegex; + if (PlatformDetection.IsLineNumbersSupported) + { + frameParserRegex = @"\s+at\s.+\.(?[^(.]+)\([^)]*\)\sin\s(?.*)\:line\s(?[\d]+)"; + } + else + { + frameParserRegex = @"\s+at\s.+\.(?[^(.]+)"; + } using (var sr = new StringReader(reportedCallStack)) { @@ -162,8 +196,12 @@ private static void VerifyCallStack( var match = Regex.Match(frame, frameParserRegex); Assert.True(match.Success); Assert.Equal(expectedStackFrame.CallerMemberName, match.Groups["memberName"].Value); - Assert.Equal(expectedStackFrame.SourceFilePath, match.Groups["filePath"].Value); - Assert.Equal(expectedStackFrame.SourceLineNumber, Convert.ToInt32(match.Groups["lineNumber"].Value)); + + if (PlatformDetection.IsLineNumbersSupported) + { + Assert.Equal(expectedStackFrame.SourceFilePath, match.Groups["filePath"].Value); + Assert.Equal(expectedStackFrame.SourceLineNumber, Convert.ToInt32(match.Groups["lineNumber"].Value)); + } } } catch diff --git a/src/libraries/System.Runtime/tests/System/GuidTests.cs b/src/libraries/System.Runtime/tests/System/GuidTests.cs index 0943b78271a18..12742ad5df1f1 100644 --- a/src/libraries/System.Runtime/tests/System/GuidTests.cs +++ b/src/libraries/System.Runtime/tests/System/GuidTests.cs @@ -208,6 +208,54 @@ public static void ParseExact_Invalid(string input, string format, Type exceptio Assert.Equal(Guid.Empty, result); } + [Theory] + [MemberData(nameof(GuidStrings_Valid_TestData))] + public static void Parse_Span_ValidInput_Success(string input, string format, Guid expected) + { + Assert.Equal(expected, Guid.Parse(input.AsSpan())); + Assert.Equal(expected, Guid.ParseExact(input.AsSpan(), format.ToUpperInvariant())); + Assert.Equal(expected, Guid.ParseExact(input.AsSpan(), format.ToLowerInvariant())); // Format should be case insensitive + + Guid result; + + Assert.True(Guid.TryParse(input.AsSpan(), out result)); + Assert.Equal(expected, result); + + Assert.True(Guid.TryParseExact(input.AsSpan(), format.ToUpperInvariant(), out result)); + Assert.Equal(expected, result); + + Assert.True(Guid.TryParseExact(input.AsSpan(), format.ToLowerInvariant(), out result)); // Format should be case insensitive + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(GuidStrings_Invalid_TestData))] + public static void Parse_Span_InvalidInput_Fails(string input, Type exceptionType) + { + if (input == null) + { + return; + } + + // Overflow exceptions throw as format exceptions in Parse + if (exceptionType.Equals(typeof(OverflowException))) + { + exceptionType = typeof(FormatException); + } + Assert.Throws(exceptionType, () => Guid.Parse(input.AsSpan())); + + Assert.False(Guid.TryParse(input.AsSpan(), out Guid result)); + Assert.Equal(Guid.Empty, result); + + foreach (string format in new[] { "N", "D", "B", "P", "X" }) + { + Assert.Throws(exceptionType, () => Guid.ParseExact(input.AsSpan(), format)); + + Assert.False(Guid.TryParseExact(input.AsSpan(), format, out result)); + Assert.Equal(Guid.Empty, result); + } + } + public static IEnumerable CompareTo_TestData() { yield return new object[] { s_testGuid, s_testGuid, 0 }; @@ -827,53 +875,5 @@ public static void TryFormat_ValidLength_ReturnsTrue(Guid guid, string format, s Assert.True(guid.TryFormat(new Span(chars), out int charsWritten, format)); Assert.Equal(chars, expected.ToCharArray()); } - - [Theory] - [MemberData(nameof(GuidStrings_Valid_TestData))] - public static void Parse_Span_ValidInput_Success(string input, string format, Guid expected) - { - Assert.Equal(expected, Guid.Parse(input.AsSpan())); - Assert.Equal(expected, Guid.ParseExact(input.AsSpan(), format.ToUpperInvariant())); - Assert.Equal(expected, Guid.ParseExact(input.AsSpan(), format.ToLowerInvariant())); // Format should be case insensitive - - Guid result; - - Assert.True(Guid.TryParse(input.AsSpan(), out result)); - Assert.Equal(expected, result); - - Assert.True(Guid.TryParseExact(input.AsSpan(), format.ToUpperInvariant(), out result)); - Assert.Equal(expected, result); - - Assert.True(Guid.TryParseExact(input.AsSpan(), format.ToLowerInvariant(), out result)); // Format should be case insensitive - Assert.Equal(expected, result); - } - - [Theory] - [MemberData(nameof(GuidStrings_Invalid_TestData))] - public static void Parse_Span_InvalidInput_Fails(string input, Type exceptionType) - { - if (input == null) - { - return; - } - - // Overflow exceptions throw as format exceptions in Parse - if (exceptionType.Equals(typeof(OverflowException))) - { - exceptionType = typeof(FormatException); - } - Assert.Throws(exceptionType, () => Guid.Parse(input.AsSpan())); - - Assert.False(Guid.TryParse(input.AsSpan(), out Guid result)); - Assert.Equal(Guid.Empty, result); - - foreach (string format in new[] { "N", "D", "B", "P", "X" }) - { - Assert.Throws(exceptionType, () => Guid.ParseExact(input.AsSpan(), format)); - - Assert.False(Guid.TryParseExact(input.AsSpan(), format, out result)); - Assert.Equal(Guid.Empty, result); - } - } } } diff --git a/src/libraries/System.Runtime/tests/System/HalfTests.cs b/src/libraries/System.Runtime/tests/System/HalfTests.cs index 926034b5b7091..ebc04c2727470 100644 --- a/src/libraries/System.Runtime/tests/System/HalfTests.cs +++ b/src/libraries/System.Runtime/tests/System/HalfTests.cs @@ -808,6 +808,64 @@ public static void Parse_Invalid(string value, NumberStyles style, IFormatProvid } } + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; + + yield return new object[] { "-123", 1, 3, DefaultStyle, null, (float)123 }; + yield return new object[] { "-123", 0, 3, DefaultStyle, null, (float)-12 }; + yield return new object[] { "1E23", 0, 3, DefaultStyle, null, (float)1E2 }; + yield return new object[] { "123", 0, 2, NumberStyles.Float, new NumberFormatInfo(), (float)12 }; + yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "," }, (float)10 }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, (float)123 }; + yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, float.PositiveInfinity }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, float expectedFloat) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Half result; + Half expected = (Half)expectedFloat; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Half.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Half.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, Half.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.True(expected.Equals(Half.Parse(value.AsSpan(offset, count), style, provider)) || (Half.IsNaN(expected) && Half.IsNaN(Half.Parse(value.AsSpan(offset, count), style, provider)))); + + Assert.True(Half.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.True(expected.Equals(result) || (Half.IsNaN(expected) && Half.IsNaN(result))); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => float.Parse(value.AsSpan(), style, provider)); + + Assert.False(float.TryParse(value.AsSpan(), style, provider, out float result)); + Assert.Equal(0, result); + } + } + public static IEnumerable ToString_TestData() { yield return new object[] { -4570.0f, "G", null, "-4570" }; @@ -913,65 +971,6 @@ public static void ToString_InvalidFormat_ThrowsFormatException() Assert.Throws(() => f.ToString("E" + intMaxPlus1String)); } - - public static IEnumerable Parse_ValidWithOffsetCount_TestData() - { - foreach (object[] inputs in Parse_Valid_TestData()) - { - yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; - } - - const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; - - yield return new object[] { "-123", 1, 3, DefaultStyle, null, (float)123 }; - yield return new object[] { "-123", 0, 3, DefaultStyle, null, (float)-12 }; - yield return new object[] { "1E23", 0, 3, DefaultStyle, null, (float)1E2 }; - yield return new object[] { "123", 0, 2, NumberStyles.Float, new NumberFormatInfo(), (float)12 }; - yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "," }, (float)10 }; - yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, (float)123 }; - yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, float.PositiveInfinity }; - } - - [Theory] - [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] - public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, float expectedFloat) - { - bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; - Half result; - Half expected = (Half)expectedFloat; - if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) - { - // Use Parse(string) or Parse(string, IFormatProvider) - if (isDefaultProvider) - { - Assert.True(Half.TryParse(value.AsSpan(offset, count), out result)); - Assert.Equal(expected, result); - - Assert.Equal(expected, Half.Parse(value.AsSpan(offset, count))); - } - - Assert.Equal(expected, Half.Parse(value.AsSpan(offset, count), provider: provider)); - } - - Assert.True(expected.Equals(Half.Parse(value.AsSpan(offset, count), style, provider)) || (Half.IsNaN(expected) && Half.IsNaN(Half.Parse(value.AsSpan(offset, count), style, provider)))); - - Assert.True(Half.TryParse(value.AsSpan(offset, count), style, provider, out result)); - Assert.True(expected.Equals(result) || (Half.IsNaN(expected) && Half.IsNaN(result))); - } - - [Theory] - [MemberData(nameof(Parse_Invalid_TestData))] - public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) - { - if (value != null) - { - Assert.Throws(exceptionType, () => float.Parse(value.AsSpan(), style, provider)); - - Assert.False(float.TryParse(value.AsSpan(), style, provider, out float result)); - Assert.Equal(0, result); - } - } - [Fact] public static void TryFormat() { diff --git a/src/libraries/System.Runtime/tests/System/Int32Tests.cs b/src/libraries/System.Runtime/tests/System/Int32Tests.cs index fca800ca2921f..0a4ef89decefe 100644 --- a/src/libraries/System.Runtime/tests/System/Int32Tests.cs +++ b/src/libraries/System.Runtime/tests/System/Int32Tests.cs @@ -664,35 +664,6 @@ public static void TryParse_InvalidNumberStyle_ThrowsArgumentException(NumberSty AssertExtensions.Throws(paramName, () => int.Parse("1", style, null)); } - [Theory] - [InlineData("N")] - [InlineData("F")] - public static void ToString_N_F_EmptyNumberGroup_Success(string specifier) - { - var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); - nfi.NumberGroupSizes = new int[0]; - nfi.NumberGroupSeparator = ","; - Assert.Equal("1234", 1234.ToString($"{specifier}0", nfi)); - } - - [Fact] - public static void ToString_P_EmptyPercentGroup_Success() - { - var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); - nfi.PercentGroupSizes = new int[0]; - nfi.PercentSymbol = "%"; - Assert.Equal("123400 %", 1234.ToString("P0", nfi)); - } - - [Fact] - public static void ToString_C_EmptyPercentGroup_Success() - { - var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); - nfi.CurrencyGroupSizes = new int[0]; - nfi.CurrencySymbol = "$"; - Assert.Equal("$1234", 1234.ToString("C0", nfi)); - } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() { foreach (object[] inputs in Parse_Valid_TestData()) @@ -798,6 +769,35 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP } } + [Theory] + [InlineData("N")] + [InlineData("F")] + public static void ToString_N_F_EmptyNumberGroup_Success(string specifier) + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.NumberGroupSizes = new int[0]; + nfi.NumberGroupSeparator = ","; + Assert.Equal("1234", 1234.ToString($"{specifier}0", nfi)); + } + + [Fact] + public static void ToString_P_EmptyPercentGroup_Success() + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.PercentGroupSizes = new int[0]; + nfi.PercentSymbol = "%"; + Assert.Equal("123400 %", 1234.ToString("P0", nfi)); + } + + [Fact] + public static void ToString_C_EmptyPercentGroup_Success() + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.CurrencyGroupSizes = new int[0]; + nfi.CurrencySymbol = "$"; + Assert.Equal("$1234", 1234.ToString("C0", nfi)); + } + [Theory] [MemberData(nameof(ToString_TestData))] public static void TryFormat(int i, string format, IFormatProvider provider, string expected) diff --git a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs index 50e387d3c94b8..9c8eafdbe8cc4 100644 --- a/src/libraries/System.Runtime/tests/System/IntPtrTests.cs +++ b/src/libraries/System.Runtime/tests/System/IntPtrTests.cs @@ -798,35 +798,6 @@ public static void TryParse_InvalidNumberStyle_ThrowsArgumentException(NumberSty AssertExtensions.Throws(paramName, () => IntPtr.Parse("1", style, null)); } - [Theory] - [InlineData("N")] - [InlineData("F")] - public static void ToString_N_F_EmptyNumberGroup_Success(string specifier) - { - var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); - nfi.NumberGroupSizes = new int[0]; - nfi.NumberGroupSeparator = ","; - Assert.Equal("1234", ((IntPtr)1234).ToString($"{specifier}0", nfi)); - } - - [Fact] - public static void ToString_P_EmptyPercentGroup_Success() - { - var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); - nfi.PercentGroupSizes = new int[0]; - nfi.PercentSymbol = "%"; - Assert.Equal("123400 %", ((IntPtr)1234).ToString("P0", nfi)); - } - - [Fact] - public static void ToString_C_EmptyPercentGroup_Success() - { - var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); - nfi.CurrencyGroupSizes = new int[0]; - nfi.CurrencySymbol = "$"; - Assert.Equal("$1234", ((IntPtr)1234).ToString("C0", nfi)); - } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() { foreach (object[] inputs in Parse_Valid_TestData()) @@ -932,6 +903,35 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP } } + [Theory] + [InlineData("N")] + [InlineData("F")] + public static void ToString_N_F_EmptyNumberGroup_Success(string specifier) + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.NumberGroupSizes = new int[0]; + nfi.NumberGroupSeparator = ","; + Assert.Equal("1234", ((IntPtr)1234).ToString($"{specifier}0", nfi)); + } + + [Fact] + public static void ToString_P_EmptyPercentGroup_Success() + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.PercentGroupSizes = new int[0]; + nfi.PercentSymbol = "%"; + Assert.Equal("123400 %", ((IntPtr)1234).ToString("P0", nfi)); + } + + [Fact] + public static void ToString_C_EmptyPercentGroup_Success() + { + var nfi = (NumberFormatInfo)NumberFormatInfo.InvariantInfo.Clone(); + nfi.CurrencyGroupSizes = new int[0]; + nfi.CurrencySymbol = "$"; + Assert.Equal("$1234", ((IntPtr)1234).ToString("C0", nfi)); + } + [Theory] [MemberData(nameof(ToString_TestData))] public static void TryFormat(IntPtr i, string format, IFormatProvider provider, string expected) diff --git a/src/libraries/System.Runtime/tests/System/Reflection/InvokeWithRefLikeArgs.cs b/src/libraries/System.Runtime/tests/System/Reflection/InvokeWithRefLikeArgs.cs index c62ddb55b1db6..4f2e4d2da1b0f 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/InvokeWithRefLikeArgs.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/InvokeWithRefLikeArgs.cs @@ -38,7 +38,7 @@ public static void MethodTakesRefStructAsArgWithDefaultValue_DoesNotCopyValueBac Assert.Null(args[0]); // no value should have been copied back } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Moq uses Reflection.Emit [SkipOnMono("https://github.com/dotnet/runtime/issues/40738")] public static void MethodTakesRefToRefStructAsArg_ThrowsNSE() { diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs index 9368a6184d085..00eda3da5cd1d 100644 --- a/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs +++ b/src/libraries/System.Runtime/tests/System/Reflection/ModuleTests.cs @@ -176,6 +176,60 @@ public void GetFields() Assert.Equal(TestModule.GetField("TestLong", BindingFlags.NonPublic | BindingFlags.Static), fields[1]); } + [Fact] + public void GetMethod_NullName() + { + var ex = AssertExtensions.Throws("name", () => Module.GetMethod(null)); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + + ex = AssertExtensions.Throws("name", () => Module.GetMethod(null, Type.EmptyTypes)); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + } + + [Fact] + public void GetMethod_NullTypes() + { + var ex = AssertExtensions.Throws("types", () => Module.GetMethod("TestMethodFoo", null)); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + } + + [Fact] + public void GetMethod_AmbiguousMatch() + { + var ex = Assert.Throws(() => TestModule.GetMethod("TestMethodFoo")); + Assert.Null(ex.InnerException); + Assert.NotNull(ex.Message); + } + + [Fact] + public void GetMethod() + { + var method = TestModule.GetMethod("TestMethodFoo", Type.EmptyTypes); + Assert.True(method.IsPublic); + Assert.True(method.IsStatic); + Assert.Equal(typeof(void), method.ReturnType); + Assert.Empty(method.GetParameters()); + + method = TestModule.GetMethod("TestMethodBar", BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.Any, new[] { typeof(int) }, null); + Assert.False(method.IsPublic); + Assert.True(method.IsStatic); + Assert.Equal(typeof(int), method.ReturnType); + Assert.Equal(typeof(int), method.GetParameters().Single().ParameterType); + } + + [Fact] + public void GetMethods() + { + var methodNames = TestModule.GetMethods().Select(m => m.Name).ToArray(); + AssertExtensions.SequenceEqual(new[]{ "TestMethodFoo", "TestMethodFoo" }, methodNames ); + + methodNames = TestModule.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Select(m => m.Name).ToArray(); + AssertExtensions.SequenceEqual(new[]{ "TestMethodFoo", "TestMethodFoo", "TestMethodBar" }, methodNames ); + } + public static IEnumerable Types => Module.GetTypes().Select(t => new object[] { t }); diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/AttributesTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/AttributesTests.cs index d0b1834e08cdc..233d0c4fd256c 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/AttributesTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/AttributesTests.cs @@ -114,11 +114,13 @@ public static void DependencyAttributeTests() Assert.Equal((LoadHint)(-2), attr2.LoadHint); } +#pragma warning disable SYSLIB0015 // Obsolete: DisablePrivateReflectionAttribute [Fact] public static void DisablePrivateReflectionAttributeTests() { new DisablePrivateReflectionAttribute(); } +#pragma warning restore SYSLIB0015 // Obsolete: DisablePrivateReflectionAttribute [Fact] public static void DiscardableAttributeTests() diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 33327d00fed47..d9b579c56476a 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -198,7 +198,12 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() yield return new[] { typeof(string), typeof(ArgumentException) }; // variable-length type yield return new[] { typeof(int[]), typeof(ArgumentException) }; // variable-length type yield return new[] { typeof(int[,]), typeof(ArgumentException) }; // variable-length type - yield return new[] { Array.CreateInstance(typeof(int), new[] { 1 }, new[] { 1 }).GetType(), typeof(ArgumentException) }; // variable-length type (non-szarray) + + if (PlatformDetection.IsNonZeroLowerBoundArraySupported) + { + yield return new[] { Array.CreateInstance(typeof(int), new[] { 1 }, new[] { 1 }).GetType(), typeof(ArgumentException) }; // variable-length type (non-szarray) + } + yield return new[] { typeof(Array), typeof(MemberAccessException) }; // abstract type yield return new[] { typeof(Enum), typeof(MemberAccessException) }; // abstract type diff --git a/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs index c2bb2bdc48cd1..e293a1814f29e 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/ExceptionServices/ExceptionDispatchInfoTests.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Xunit; @@ -29,23 +29,27 @@ public static void StaticThrow_UpdatesStackTraceAppropriately() } [Fact] - public static void SetCurrentStackTrace_Invalid_Throws() + public static void SetCurrentOrRemoteStackTrace_Invalid_Throws() { Exception e; // Null argument e = null; AssertExtensions.Throws("source", () => ExceptionDispatchInfo.SetCurrentStackTrace(e)); + AssertExtensions.Throws("source", () => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); + AssertExtensions.Throws("stackTrace", () => ExceptionDispatchInfo.SetRemoteStackTrace(new Exception(), stackTrace: null)); // Previously set current stack e = new Exception(); ExceptionDispatchInfo.SetCurrentStackTrace(e); Assert.Throws(() => ExceptionDispatchInfo.SetCurrentStackTrace(e)); + Assert.Throws(() => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); // Previously thrown e = new Exception(); try { throw e; } catch { } Assert.Throws(() => ExceptionDispatchInfo.SetCurrentStackTrace(e)); + Assert.Throws(() => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); } [Fact] @@ -55,12 +59,29 @@ public static void SetCurrentStackTrace_IncludedInExceptionStackTrace() e = new Exception(); ABCDEFGHIJKLMNOPQRSTUVWXYZ(e); - Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace); + Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace, StringComparison.Ordinal); e = new Exception(); ABCDEFGHIJKLMNOPQRSTUVWXYZ(e); try { throw e; } catch { } - Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace); + Assert.Contains(nameof(ABCDEFGHIJKLMNOPQRSTUVWXYZ), e.StackTrace, StringComparison.Ordinal); + } + + [Fact] + public static void SetRemoteStackTrace_IncludedInExceptionStackTrace() + { + Exception e; + + e = new Exception(); + Assert.Same(e, ExceptionDispatchInfo.SetRemoteStackTrace(e, "pumpkin-anaconda-maritime")); // 3 randomly selected words + Assert.Contains("pumpkin-anaconda-maritime", e.StackTrace, StringComparison.Ordinal); + Assert.DoesNotContain("pumpkin-anaconda-maritime", new StackTrace(e).ToString(), StringComparison.Ordinal); // we shouldn't attempt to parse it in a StackTrace object + + e = new Exception(); + Assert.Same(e, ExceptionDispatchInfo.SetRemoteStackTrace(e, "pumpkin-anaconda-maritime")); + try { throw e; } catch { } + Assert.Contains("pumpkin-anaconda-maritime", e.StackTrace, StringComparison.Ordinal); + Assert.DoesNotContain("pumpkin-anaconda-maritime", new StackTrace(e).ToString(), StringComparison.Ordinal); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] diff --git a/src/libraries/System.Runtime/tests/System/SingleTests.cs b/src/libraries/System.Runtime/tests/System/SingleTests.cs index 962621d05c15b..aa850e453cda5 100644 --- a/src/libraries/System.Runtime/tests/System/SingleTests.cs +++ b/src/libraries/System.Runtime/tests/System/SingleTests.cs @@ -418,6 +418,63 @@ public static void Parse_Invalid(string value, NumberStyles style, IFormatProvid } } + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; + + yield return new object[] { "-123", 1, 3, DefaultStyle, null, (float)123 }; + yield return new object[] { "-123", 0, 3, DefaultStyle, null, (float)-12 }; + yield return new object[] { "1E23", 0, 3, DefaultStyle, null, (float)1E2 }; + yield return new object[] { "123", 0, 2, NumberStyles.Float, new NumberFormatInfo(), (float)12 }; + yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "," }, (float)10 }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, (float)123 }; + yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, float.PositiveInfinity }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, float expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + float result; + if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(float.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, float.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, float.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.Equal(expected, float.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(float.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => float.Parse(value.AsSpan(), style, provider)); + + Assert.False(float.TryParse(value.AsSpan(), style, provider, out float result)); + Assert.Equal(0, result); + } + } + [Fact] public static void PositiveInfinity() { @@ -608,63 +665,6 @@ public static void IsSubnormal(float d, bool expected) Assert.Equal(expected, float.IsSubnormal(d)); } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() - { - foreach (object[] inputs in Parse_Valid_TestData()) - { - yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; - } - - const NumberStyles DefaultStyle = NumberStyles.Float | NumberStyles.AllowThousands; - - yield return new object[] { "-123", 1, 3, DefaultStyle, null, (float)123 }; - yield return new object[] { "-123", 0, 3, DefaultStyle, null, (float)-12 }; - yield return new object[] { "1E23", 0, 3, DefaultStyle, null, (float)1E2 }; - yield return new object[] { "123", 0, 2, NumberStyles.Float, new NumberFormatInfo(), (float)12 }; - yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "," }, (float)10 }; - yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, (float)123 }; - yield return new object[] { "-Infinity", 1, 8, NumberStyles.Any, NumberFormatInfo.InvariantInfo, float.PositiveInfinity }; - } - - [Theory] - [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] - public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, float expected) - { - bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; - float result; - if ((style & ~(NumberStyles.Float | NumberStyles.AllowThousands)) == 0 && style != NumberStyles.None) - { - // Use Parse(string) or Parse(string, IFormatProvider) - if (isDefaultProvider) - { - Assert.True(float.TryParse(value.AsSpan(offset, count), out result)); - Assert.Equal(expected, result); - - Assert.Equal(expected, float.Parse(value.AsSpan(offset, count))); - } - - Assert.Equal(expected, float.Parse(value.AsSpan(offset, count), provider: provider)); - } - - Assert.Equal(expected, float.Parse(value.AsSpan(offset, count), style, provider)); - - Assert.True(float.TryParse(value.AsSpan(offset, count), style, provider, out result)); - Assert.Equal(expected, result); - } - - [Theory] - [MemberData(nameof(Parse_Invalid_TestData))] - public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) - { - if (value != null) - { - Assert.Throws(exceptionType, () => float.Parse(value.AsSpan(), style, provider)); - - Assert.False(float.TryParse(value.AsSpan(), style, provider, out float result)); - Assert.Equal(0, result); - } - } - [Fact] public static void TryFormat() { diff --git a/src/libraries/System.Runtime/tests/System/StringComparerTests.cs b/src/libraries/System.Runtime/tests/System/StringComparerTests.cs index 364ecdfb26026..3027c870855b5 100644 --- a/src/libraries/System.Runtime/tests/System/StringComparerTests.cs +++ b/src/libraries/System.Runtime/tests/System/StringComparerTests.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using System.Globalization; +using System.Reflection; using Xunit; namespace System.Tests @@ -135,5 +137,116 @@ public void CreateCultureOptions_CreatesValidComparer() Assert.Equal(1, c.Compare("42", null)); Assert.Throws(() => c.Compare(42, "84")); } + + [Fact] + public void IsWellKnownOrdinalComparer_TestCases() + { + CompareInfo ci_enUS = CompareInfo.GetCompareInfo("en-US"); + + // First, instantiate and test the comparers directly + + RunTest(null, false, false); + RunTest(EqualityComparer.Default, true, false); // EC.Default is Ordinal-equivalent + RunTest(EqualityComparer.Default, false, false); // EC.Default isn't a string comparer + RunTest(StringComparer.Ordinal, true, false); + RunTest(StringComparer.OrdinalIgnoreCase, true, true); + RunTest(StringComparer.InvariantCulture, false, false); // not ordinal + RunTest(StringComparer.InvariantCultureIgnoreCase, false, false); // not ordinal + RunTest(GetNonRandomizedComparer("WrappedAroundDefaultComparer"), true, false); // EC.Default is Ordinal-equivalent + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinal"), true, false); + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinalIgnoreCase"), true, true); + RunTest(new CustomStringComparer(), false, false); // not an inbox comparer + RunTest(ci_enUS.GetStringComparer(CompareOptions.None), false, false); // linguistic + RunTest(ci_enUS.GetStringComparer(CompareOptions.Ordinal), true, false); + RunTest(ci_enUS.GetStringComparer(CompareOptions.OrdinalIgnoreCase), true, true); + + // Then, make sure that this API works with common collection types + + RunTest(new Dictionary().Comparer, true, false); + RunTest(new Dictionary(StringComparer.Ordinal).Comparer, true, false); + RunTest(new Dictionary(StringComparer.OrdinalIgnoreCase).Comparer, true, true); + RunTest(new Dictionary(StringComparer.InvariantCulture).Comparer, false, false); + RunTest(new Dictionary(StringComparer.InvariantCultureIgnoreCase).Comparer, false, false); + + RunTest(new HashSet().Comparer, true, false); + RunTest(new HashSet(StringComparer.Ordinal).Comparer, true, false); + RunTest(new HashSet(StringComparer.OrdinalIgnoreCase).Comparer, true, true); + RunTest(new HashSet(StringComparer.InvariantCulture).Comparer, false, false); + RunTest(new HashSet(StringComparer.InvariantCultureIgnoreCase).Comparer, false, false); + + static void RunTest(IEqualityComparer comparer, bool expectedIsOrdinal, bool expectedIgnoreCase) + { + Assert.Equal(expectedIsOrdinal, StringComparer.IsWellKnownOrdinalComparer(comparer, out bool actualIgnoreCase)); + Assert.Equal(expectedIgnoreCase, actualIgnoreCase); + } + } + + [Fact] + public void IsWellKnownCultureAwareComparer_TestCases() + { + CompareInfo ci_enUS = CompareInfo.GetCompareInfo("en-US"); + CompareInfo ci_inv = CultureInfo.InvariantCulture.CompareInfo; + + // First, instantiate and test the comparers directly + + RunTest(null, null, default); + RunTest(EqualityComparer.Default, null, default); // EC.Default is not culture-aware + RunTest(EqualityComparer.Default, null, default); // EC.Default isn't a string comparer + RunTest(StringComparer.Ordinal, null, default); + RunTest(StringComparer.OrdinalIgnoreCase, null, default); + RunTest(StringComparer.InvariantCulture, ci_inv, CompareOptions.None); + RunTest(StringComparer.InvariantCultureIgnoreCase, ci_inv, CompareOptions.IgnoreCase); + RunTest(GetNonRandomizedComparer("WrappedAroundDefaultComparer"), null, default); // EC.Default is Ordinal-equivalent + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinal"), null, default); + RunTest(GetNonRandomizedComparer("WrappedAroundStringComparerOrdinalIgnoreCase"), null, default); + RunTest(new CustomStringComparer(), null, default); // not an inbox comparer + RunTest(ci_enUS.GetStringComparer(CompareOptions.None), ci_enUS, CompareOptions.None); + RunTest(ci_enUS.GetStringComparer(CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType), ci_enUS, CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType); + RunTest(ci_enUS.GetStringComparer(CompareOptions.Ordinal), null, default); // not linguistic + RunTest(ci_enUS.GetStringComparer(CompareOptions.OrdinalIgnoreCase), null, default); // not linguistic + RunTest(StringComparer.Create(CultureInfo.InvariantCulture, false), ci_inv, CompareOptions.None); + RunTest(StringComparer.Create(CultureInfo.InvariantCulture, true), ci_inv, CompareOptions.IgnoreCase); + RunTest(StringComparer.Create(CultureInfo.InvariantCulture, CompareOptions.IgnoreSymbols), ci_inv, CompareOptions.IgnoreSymbols); + + // Then, make sure that this API works with common collection types + + RunTest(new Dictionary().Comparer, null, default); + RunTest(new Dictionary(StringComparer.Ordinal).Comparer, null, default); + RunTest(new Dictionary(StringComparer.OrdinalIgnoreCase).Comparer, null, default); + RunTest(new Dictionary(StringComparer.InvariantCulture).Comparer, ci_inv, CompareOptions.None); + RunTest(new Dictionary(StringComparer.InvariantCultureIgnoreCase).Comparer, ci_inv, CompareOptions.IgnoreCase); + + RunTest(new HashSet().Comparer, null, default); + RunTest(new HashSet(StringComparer.Ordinal).Comparer, null, default); + RunTest(new HashSet(StringComparer.OrdinalIgnoreCase).Comparer, null, default); + RunTest(new HashSet(StringComparer.InvariantCulture).Comparer, ci_inv, CompareOptions.None); + RunTest(new HashSet(StringComparer.InvariantCultureIgnoreCase).Comparer, ci_inv, CompareOptions.IgnoreCase); + + static void RunTest(IEqualityComparer comparer, CompareInfo expectedCompareInfo, CompareOptions expectedCompareOptions) + { + bool actualReturnValue = StringComparer.IsWellKnownCultureAwareComparer(comparer, out CompareInfo actualCompareInfo, out CompareOptions actualCompareOptions); + Assert.Equal(expectedCompareInfo != null, actualReturnValue); + Assert.Equal(expectedCompareInfo, actualCompareInfo); + Assert.Equal(expectedCompareOptions, actualCompareOptions); + } + } + + private static IEqualityComparer GetNonRandomizedComparer(string name) + { + Type nonRandomizedComparerType = typeof(StringComparer).Assembly.GetType("System.Collections.Generic.NonRandomizedStringEqualityComparer"); + Assert.NotNull(nonRandomizedComparerType); + + FieldInfo fi = nonRandomizedComparerType.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + Assert.NotNull(fi); + + return (IEqualityComparer)fi.GetValue(null); + } + + private class CustomStringComparer : StringComparer + { + public override int Compare(string x, string y) => throw new NotImplementedException(); + public override bool Equals(string x, string y) => throw new NotImplementedException(); + public override int GetHashCode(string obj) => throw new NotImplementedException(); + } } } diff --git a/src/libraries/System.Runtime/tests/System/StringTests.cs b/src/libraries/System.Runtime/tests/System/StringTests.cs index 8cf3f9049a48c..58a58b2d257fb 100644 --- a/src/libraries/System.Runtime/tests/System/StringTests.cs +++ b/src/libraries/System.Runtime/tests/System/StringTests.cs @@ -920,32 +920,35 @@ public static unsafe void GetPinnableReference_ReturnsSameAsGCHandleAndLegacyFix // Finally, ensure that GetPinnableReference matches the legacy 'fixed' keyword. - DynamicMethod dynamicMethod = new DynamicMethod("tester", typeof(bool), new[] { typeof(string) }); - ILGenerator ilGen = dynamicMethod.GetILGenerator(); - LocalBuilder pinnedLocal = ilGen.DeclareLocal(typeof(object), pinned: true); + if (PlatformDetection.IsReflectionEmitSupported) + { + DynamicMethod dynamicMethod = new DynamicMethod("tester", typeof(bool), new[] { typeof(string) }); + ILGenerator ilGen = dynamicMethod.GetILGenerator(); + LocalBuilder pinnedLocal = ilGen.DeclareLocal(typeof(object), pinned: true); - ilGen.Emit(OpCodes.Ldarg_0); // load 'input' and pin it - ilGen.Emit(OpCodes.Stloc, pinnedLocal); + ilGen.Emit(OpCodes.Ldarg_0); // load 'input' and pin it + ilGen.Emit(OpCodes.Stloc, pinnedLocal); - ilGen.Emit(OpCodes.Ldloc, pinnedLocal); // get the address of field 0 from pinned 'input' - ilGen.Emit(OpCodes.Conv_I); + ilGen.Emit(OpCodes.Ldloc, pinnedLocal); // get the address of field 0 from pinned 'input' + ilGen.Emit(OpCodes.Conv_I); - ilGen.Emit(OpCodes.Call, typeof(RuntimeHelpers).GetProperty("OffsetToStringData").GetMethod); // get pointer to start of string data - ilGen.Emit(OpCodes.Add); + ilGen.Emit(OpCodes.Call, typeof(RuntimeHelpers).GetProperty("OffsetToStringData").GetMethod); // get pointer to start of string data + ilGen.Emit(OpCodes.Add); - ilGen.Emit(OpCodes.Ldarg_0); // get value of input.GetPinnableReference() - ilGen.Emit(OpCodes.Callvirt, typeof(string).GetMethod("GetPinnableReference")); + ilGen.Emit(OpCodes.Ldarg_0); // get value of input.GetPinnableReference() + ilGen.Emit(OpCodes.Callvirt, typeof(string).GetMethod("GetPinnableReference")); - // At this point, the top of the evaluation stack is traditional (fixed char* = input) and input.GetPinnableReference(). - // Compare for equality and return. + // At this point, the top of the evaluation stack is traditional (fixed char* = input) and input.GetPinnableReference(). + // Compare for equality and return. - ilGen.Emit(OpCodes.Ceq); - ilGen.Emit(OpCodes.Ret); + ilGen.Emit(OpCodes.Ceq); + ilGen.Emit(OpCodes.Ret); - Assert.True((bool)dynamicMethod.Invoke(null, new[] { input })); + Assert.True((bool)dynamicMethod.Invoke(null, new[] { input })); + } } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] public static unsafe void GetPinnableReference_WithNullInput_ThrowsNullRef() { // This test uses an explicit call instead of the normal callvirt that C# would emit. diff --git a/src/libraries/System.Runtime/tests/System/Text/ASCIIUtilityTests.cs b/src/libraries/System.Runtime/tests/System/Text/ASCIIUtilityTests.cs index 07dd4ae144c91..54a86110f4093 100644 --- a/src/libraries/System.Runtime/tests/System/Text/ASCIIUtilityTests.cs +++ b/src/libraries/System.Runtime/tests/System/Text/ASCIIUtilityTests.cs @@ -432,7 +432,7 @@ private static Type GetAsciiUtilityType() return typeof(object).Assembly.GetType("System.Text.ASCIIUtility"); } - private sealed class UnsafeLazyDelegate where TDelegate : class + private sealed class UnsafeLazyDelegate where TDelegate : Delegate { private readonly Lazy _lazyDelegate; @@ -449,7 +449,7 @@ public UnsafeLazyDelegate(string methodName) // Construct the TDelegate pointing to this method - return (TDelegate)Activator.CreateInstance(typeof(TDelegate), new object[] { null, methodInfo.MethodHandle.GetFunctionPointer() }); + return methodInfo.CreateDelegate(); }); } diff --git a/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs b/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs index 28be1b4f7d01c..b598a21988515 100644 --- a/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs +++ b/src/libraries/System.Runtime/tests/System/Text/EncodingTests.cs @@ -29,7 +29,7 @@ public void GetEncoding_BuiltIn_ByCodePage_WithDisallowedEncoding_Throws(string Assert.Throws(() => Encoding.GetEncoding(codePage, EncoderFallback.ReplacementFallback, DecoderFallback.ReplacementFallback)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Moq uses Reflection.Emit [MemberData(nameof(DisallowedEncodings))] #pragma warning disable xUnit1026 // Theory methods should use all of their parameters public void GetEncoding_FromProvider_ByCodePage_WithDisallowedEncoding_Throws(string encodingName, int codePage) @@ -59,7 +59,7 @@ public void GetEncoding_BuiltIn_ByEncodingName_WithDisallowedEncoding_Throws(str Assert.Throws(() => Encoding.GetEncoding(encodingName, EncoderFallback.ReplacementFallback, DecoderFallback.ReplacementFallback)); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Moq uses Reflection.Emit [MemberData(nameof(DisallowedEncodings))] public void GetEncoding_FromProvider_ByEncodingName_WithDisallowedEncoding_Throws(string encodingName, int codePage) { @@ -88,7 +88,7 @@ public void GetEncodings_BuiltIn_DoesNotContainDisallowedEncodings(string encodi } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] // Moq uses Reflection.Emit [MemberData(nameof(DisallowedEncodings))] public void GetEncodings_FromProvider_DoesNotContainDisallowedEncodings(string encodingName, int codePage) { diff --git a/src/libraries/System.Runtime/tests/System/Text/StringBuilderTests.cs b/src/libraries/System.Runtime/tests/System/Text/StringBuilderTests.cs index 493a0594142e1..89bf734808fe2 100644 --- a/src/libraries/System.Runtime/tests/System/Text/StringBuilderTests.cs +++ b/src/libraries/System.Runtime/tests/System/Text/StringBuilderTests.cs @@ -750,6 +750,29 @@ public static IEnumerable AppendFormat_TestData() yield return new object[] { "", new CustomFormatter(), "{0}", new object[] { 1.2 }, "abc" }; // Custom format provider yield return new object[] { "", new CustomFormatter(), "{0:0}", new object[] { 1.2 }, "abc" }; // Custom format provider + + // ISpanFormattable inputs: simple validation of known types that implement the interface + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (byte)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { 'A' }, "A" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0:r}", new object[] { DateTime.ParseExact("2021-03-15T14:52:51.5058563Z", "o", null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal) }, "Mon, 15 Mar 2021 14:52:51 GMT" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0:r}", new object[] { DateTimeOffset.ParseExact("2021-03-15T14:52:51.5058563Z", "o", null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal) }, "Mon, 15 Mar 2021 14:52:51 GMT" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (decimal)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (double)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { Guid.Parse("68d9cfaf-feab-4d5b-96d8-a3fd889ae89f") }, "68d9cfaf-feab-4d5b-96d8-a3fd889ae89f" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (Half)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (short)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (int)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (long)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (IntPtr)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { new Rune('A') }, "A" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (sbyte)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (float)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { TimeSpan.FromSeconds(42) }, "00:00:42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (ushort)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (uint)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (ulong)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { (UIntPtr)42 }, "42" }; + yield return new object[] { "", CultureInfo.InvariantCulture, "{0}", new object[] { new Version(1, 2, 3, 4) }, "1.2.3.4" }; } [Theory] diff --git a/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs b/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs index f538d8cccd329..094f9cda87ecc 100644 --- a/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeSpanTests.cs @@ -898,6 +898,154 @@ public void ParseExact_InvalidStyles_ThrowsArgumentException(TimeSpanStyles styl AssertExtensions.Throws("styles", () => TimeSpan.TryParseExact(inputString, new string[] { "s" }, new CultureInfo("en-US"), styles, out result)); } + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2] }; + } + + yield return new object[] { " 12:24:02 ", 5, 8, null, new TimeSpan(0, 12, 24, 2, 0) }; + yield return new object[] { " 12:24:02 ", 6, 7, null, new TimeSpan(0, 2, 24, 2, 0) }; + yield return new object[] { " 12:24:02 ", 6, 6, null, new TimeSpan(0, 2, 24, 0, 0) }; + yield return new object[] { "12:24:02.01", 0, 8, CultureInfo.InvariantCulture, new TimeSpan(0, 12, 24, 2, 0) }; + yield return new object[] { "1:1:1.00000001", 0, 7, CultureInfo.InvariantCulture, new TimeSpan(1, 1, 1) }; + yield return new object[] { "1:1:.00000001", 0, 6, CultureInfo.InvariantCulture, new TimeSpan(36600000000) }; + yield return new object[] { "24:00:00", 1, 7, null, new TimeSpan(4, 0, 0) }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span(string inputString, int offset, int count, IFormatProvider provider, TimeSpan expected) + { + ReadOnlySpan input = inputString.AsSpan(offset, count); + TimeSpan result; + + // Default provider. + if (provider == null) + { + Assert.True(TimeSpan.TryParse(input, out result)); + Assert.Equal(expected, result); + } + + Assert.Equal(expected, TimeSpan.Parse(input, provider)); + Assert.True(TimeSpan.TryParse(input, provider, out result)); + Assert.Equal(expected, result); + + // Also negate + if (!char.IsWhiteSpace(input[0])) + { + input = ("-" + inputString.Substring(offset, count)).AsSpan(); + expected = -expected; + + Assert.Equal(expected, TimeSpan.Parse(input, provider)); + Assert.True(TimeSpan.TryParse(input, provider, out result)); + Assert.Equal(expected, result); + } + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string inputString, IFormatProvider provider, Type exceptionType) + { + if (inputString != null) + { + Assert.Throws(exceptionType, () => TimeSpan.Parse(inputString.AsSpan(), provider)); + Assert.False(TimeSpan.TryParse(inputString.AsSpan(), provider, out TimeSpan result)); + Assert.Equal(TimeSpan.Zero, result); + } + } + + [Theory] + [MemberData(nameof(ParseExact_Valid_TestData))] + public static void ParseExact_Span_Valid(string inputString, string format, TimeSpan expected) + { + ReadOnlySpan input = inputString.AsSpan(); + + TimeSpan result; + Assert.Equal(expected, TimeSpan.ParseExact(input, format, new CultureInfo("en-US"))); + Assert.Equal(expected, TimeSpan.ParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.None)); + Assert.Equal(expected, TimeSpan.ParseExact(input, new[] { format }, new CultureInfo("en-US"))); + Assert.Equal(expected, TimeSpan.ParseExact(input, new[] { format }, new CultureInfo("en-US"), TimeSpanStyles.None)); + + Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), out result)); + Assert.Equal(expected, result); + + Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); + Assert.Equal(expected, result); + + Assert.True(TimeSpan.TryParseExact(input, new[] { format }, new CultureInfo("en-US"), out result)); + Assert.Equal(expected, result); + + Assert.True(TimeSpan.TryParseExact(input, new[] { format }, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); + Assert.Equal(expected, result); + + if (format != "c" && format != "t" && format != "T" && format != "g" && format != "G") + { + // TimeSpanStyles is interpreted only for custom formats + Assert.Equal(expected.Negate(), TimeSpan.ParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.AssumeNegative)); + + Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.AssumeNegative, out result)); + Assert.Equal(expected.Negate(), result); + } + else + { + // Inputs that can be parsed in standard formats with ParseExact should also be parsable with Parse + Assert.Equal(expected, TimeSpan.Parse(input, CultureInfo.InvariantCulture)); + + Assert.True(TimeSpan.TryParse(input, CultureInfo.InvariantCulture, out result)); + Assert.Equal(expected, result); + } + } + + [Theory] + [MemberData(nameof(ParseExact_Invalid_TestData))] + public static void ParseExactTest_Span_Invalid(string inputString, string format, Type exceptionType) + { + if (inputString != null && format != null) + { + Assert.Throws(exceptionType, () => TimeSpan.ParseExact(inputString.AsSpan(), format, new CultureInfo("en-US"))); + + TimeSpan result; + Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), format, new CultureInfo("en-US"), out result)); + Assert.Equal(TimeSpan.Zero, result); + + Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), format, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); + Assert.Equal(TimeSpan.Zero, result); + + Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), new[] { format }, new CultureInfo("en-US"), out result)); + Assert.Equal(TimeSpan.Zero, result); + + Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), new[] { format }, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); + Assert.Equal(TimeSpan.Zero, result); + } + } + + [Fact] + public static void ParseExactMultiple_Span_InvalidNullEmptyFormats() + { + TimeSpan result; + + AssertExtensions.Throws("formats", () => TimeSpan.ParseExact("12:34:56".AsSpan(), (string[])null, null)); + Assert.False(TimeSpan.TryParseExact("12:34:56".AsSpan(), (string[])null, null, out result)); + + Assert.Throws(() => TimeSpan.ParseExact("12:34:56".AsSpan(), new string[0], null)); + Assert.False(TimeSpan.TryParseExact("12:34:56".AsSpan(), new string[0], null, out result)); + } + + [Theory] + [MemberData(nameof(ParseExact_InvalidStyles_TestData))] + public void ParseExact_InvalidStylesSpan_ThrowsArgumentException(TimeSpanStyles styles) + { + TimeSpan result; + + string inputString = "00:00:00"; + AssertExtensions.Throws("styles", () => TimeSpan.ParseExact(inputString.AsSpan(), "s", new CultureInfo("en-US"), styles)); + AssertExtensions.Throws("styles", () => TimeSpan.ParseExact(inputString.AsSpan(), new string[] { "s" }, new CultureInfo("en-US"), styles)); + AssertExtensions.Throws("styles", () => TimeSpan.TryParseExact(inputString.AsSpan(), "s", new CultureInfo("en-US"), styles, out result)); + AssertExtensions.Throws("styles", () => TimeSpan.TryParseExact(inputString.AsSpan(), new string[] { "s" }, new CultureInfo("en-US"), styles, out result)); + } + public static IEnumerable Subtract_TestData() { yield return new object[] { new TimeSpan(0, 0, 0), new TimeSpan(1, 2, 3), new TimeSpan(-1, -2, -3) }; @@ -1217,154 +1365,6 @@ public static void NamedNaNDivision() AssertExtensions.Throws("divisor", () => TimeSpan.FromDays(1).Divide(double.NaN)); } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() - { - foreach (object[] inputs in Parse_Valid_TestData()) - { - yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2] }; - } - - yield return new object[] { " 12:24:02 ", 5, 8, null, new TimeSpan(0, 12, 24, 2, 0) }; - yield return new object[] { " 12:24:02 ", 6, 7, null, new TimeSpan(0, 2, 24, 2, 0) }; - yield return new object[] { " 12:24:02 ", 6, 6, null, new TimeSpan(0, 2, 24, 0, 0) }; - yield return new object[] { "12:24:02.01", 0, 8, CultureInfo.InvariantCulture, new TimeSpan(0, 12, 24, 2, 0) }; - yield return new object[] { "1:1:1.00000001", 0, 7, CultureInfo.InvariantCulture, new TimeSpan(1, 1, 1) }; - yield return new object[] { "1:1:.00000001", 0, 6, CultureInfo.InvariantCulture, new TimeSpan(36600000000) }; - yield return new object[] { "24:00:00", 1, 7, null, new TimeSpan(4, 0, 0) }; - } - - [Theory] - [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] - public static void Parse_Span(string inputString, int offset, int count, IFormatProvider provider, TimeSpan expected) - { - ReadOnlySpan input = inputString.AsSpan(offset, count); - TimeSpan result; - - // Default provider. - if (provider == null) - { - Assert.True(TimeSpan.TryParse(input, out result)); - Assert.Equal(expected, result); - } - - Assert.Equal(expected, TimeSpan.Parse(input, provider)); - Assert.True(TimeSpan.TryParse(input, provider, out result)); - Assert.Equal(expected, result); - - // Also negate - if (!char.IsWhiteSpace(input[0])) - { - input = ("-" + inputString.Substring(offset, count)).AsSpan(); - expected = -expected; - - Assert.Equal(expected, TimeSpan.Parse(input, provider)); - Assert.True(TimeSpan.TryParse(input, provider, out result)); - Assert.Equal(expected, result); - } - } - - [Theory] - [MemberData(nameof(Parse_Invalid_TestData))] - public static void Parse_Span_Invalid(string inputString, IFormatProvider provider, Type exceptionType) - { - if (inputString != null) - { - Assert.Throws(exceptionType, () => TimeSpan.Parse(inputString.AsSpan(), provider)); - Assert.False(TimeSpan.TryParse(inputString.AsSpan(), provider, out TimeSpan result)); - Assert.Equal(TimeSpan.Zero, result); - } - } - - [Theory] - [MemberData(nameof(ParseExact_Valid_TestData))] - public static void ParseExact_Span_Valid(string inputString, string format, TimeSpan expected) - { - ReadOnlySpan input = inputString.AsSpan(); - - TimeSpan result; - Assert.Equal(expected, TimeSpan.ParseExact(input, format, new CultureInfo("en-US"))); - Assert.Equal(expected, TimeSpan.ParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.None)); - Assert.Equal(expected, TimeSpan.ParseExact(input, new[] { format }, new CultureInfo("en-US"))); - Assert.Equal(expected, TimeSpan.ParseExact(input, new[] { format }, new CultureInfo("en-US"), TimeSpanStyles.None)); - - Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), out result)); - Assert.Equal(expected, result); - - Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); - Assert.Equal(expected, result); - - Assert.True(TimeSpan.TryParseExact(input, new[] { format }, new CultureInfo("en-US"), out result)); - Assert.Equal(expected, result); - - Assert.True(TimeSpan.TryParseExact(input, new[] { format }, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); - Assert.Equal(expected, result); - - if (format != "c" && format != "t" && format != "T" && format != "g" && format != "G") - { - // TimeSpanStyles is interpreted only for custom formats - Assert.Equal(expected.Negate(), TimeSpan.ParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.AssumeNegative)); - - Assert.True(TimeSpan.TryParseExact(input, format, new CultureInfo("en-US"), TimeSpanStyles.AssumeNegative, out result)); - Assert.Equal(expected.Negate(), result); - } - else - { - // Inputs that can be parsed in standard formats with ParseExact should also be parsable with Parse - Assert.Equal(expected, TimeSpan.Parse(input, CultureInfo.InvariantCulture)); - - Assert.True(TimeSpan.TryParse(input, CultureInfo.InvariantCulture, out result)); - Assert.Equal(expected, result); - } - } - - [Theory] - [MemberData(nameof(ParseExact_Invalid_TestData))] - public static void ParseExactTest_Span_Invalid(string inputString, string format, Type exceptionType) - { - if (inputString != null && format != null) - { - Assert.Throws(exceptionType, () => TimeSpan.ParseExact(inputString.AsSpan(), format, new CultureInfo("en-US"))); - - TimeSpan result; - Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), format, new CultureInfo("en-US"), out result)); - Assert.Equal(TimeSpan.Zero, result); - - Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), format, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); - Assert.Equal(TimeSpan.Zero, result); - - Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), new[] { format }, new CultureInfo("en-US"), out result)); - Assert.Equal(TimeSpan.Zero, result); - - Assert.False(TimeSpan.TryParseExact(inputString.AsSpan(), new[] { format }, new CultureInfo("en-US"), TimeSpanStyles.None, out result)); - Assert.Equal(TimeSpan.Zero, result); - } - } - - [Fact] - public static void ParseExactMultiple_Span_InvalidNullEmptyFormats() - { - TimeSpan result; - - AssertExtensions.Throws("formats", () => TimeSpan.ParseExact("12:34:56".AsSpan(), (string[])null, null)); - Assert.False(TimeSpan.TryParseExact("12:34:56".AsSpan(), (string[])null, null, out result)); - - Assert.Throws(() => TimeSpan.ParseExact("12:34:56".AsSpan(), new string[0], null)); - Assert.False(TimeSpan.TryParseExact("12:34:56".AsSpan(), new string[0], null, out result)); - } - - [Theory] - [MemberData(nameof(ParseExact_InvalidStyles_TestData))] - public void ParseExact_InvalidStylesSpan_ThrowsArgumentException(TimeSpanStyles styles) - { - TimeSpan result; - - string inputString = "00:00:00"; - AssertExtensions.Throws("styles", () => TimeSpan.ParseExact(inputString.AsSpan(), "s", new CultureInfo("en-US"), styles)); - AssertExtensions.Throws("styles", () => TimeSpan.ParseExact(inputString.AsSpan(), new string[] { "s" }, new CultureInfo("en-US"), styles)); - AssertExtensions.Throws("styles", () => TimeSpan.TryParseExact(inputString.AsSpan(), "s", new CultureInfo("en-US"), styles, out result)); - AssertExtensions.Throws("styles", () => TimeSpan.TryParseExact(inputString.AsSpan(), new string[] { "s" }, new CultureInfo("en-US"), styles, out result)); - } - [Theory] [MemberData(nameof(ToString_TestData))] public static void TryFormat_Valid(TimeSpan input, string format, CultureInfo info, string expected) diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs index 61c6032d0a489..ea673dea727b9 100644 --- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; +using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; @@ -81,45 +82,62 @@ public static void Names() Assert.NotNull(utc.ToString()); } - // Due to ICU size limitations, full daylight/standard names are not included. - // name abbreviations, if available, are used instead + // Due to ICU size limitations, full daylight/standard names are not included for the browser. + // Name abbreviations, if available, are used instead public static IEnumerable Platform_TimeZoneNamesTestData() { if (PlatformDetection.IsBrowser) return new TheoryData { - { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) PST", "PST", "PDT" }, - { TimeZoneInfo.FindSystemTimeZoneById(s_strSydney), "(UTC+10:00) AEST", "AEST", "AEDT" }, - { TimeZoneInfo.FindSystemTimeZoneById(s_strPerth), "(UTC+08:00) AWST", "AWST", "AWDT" }, - { TimeZoneInfo.FindSystemTimeZoneById(s_strIran), "(UTC+03:30) +0330", "+0330", "+0430" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) America/Los_Angeles", "PST", "PDT" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strSydney), "(UTC+10:00) Australia/Sydney", "AEST", "AEDT" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strPerth), "(UTC+08:00) Australia/Perth", "AWST", "AWDT" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strIran), "(UTC+03:30) Asia/Tehran", "+0330", "+0430" }, - { s_NewfoundlandTz, "(UTC-03:30) NST", "NST", "NDT" }, - { s_catamarcaTz, "(UTC-03:00) -03", "-03", "-02" } + { s_NewfoundlandTz, "(UTC-03:30) America/St_Johns", "NST", "NDT" }, + { s_catamarcaTz, "(UTC-03:00) America/Argentina/Catamarca", "-03", "-02" } + }; + else if (PlatformDetection.IsWindows) + return new TheoryData + { + { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) Pacific Time (US & Canada)", "Pacific Standard Time", "Pacific Daylight Time" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strSydney), "(UTC+10:00) Canberra, Melbourne, Sydney", "AUS Eastern Standard Time", "AUS Eastern Daylight Time" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strPerth), "(UTC+08:00) Perth", "W. Australia Standard Time", "W. Australia Daylight Time" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strIran), "(UTC+03:30) Tehran", "Iran Standard Time", "Iran Daylight Time" }, + + { s_NewfoundlandTz, "(UTC-03:30) Newfoundland", "Newfoundland Standard Time", "Newfoundland Daylight Time" }, + { s_catamarcaTz, "(UTC-03:00) City of Buenos Aires", "Argentina Standard Time", "Argentina Daylight Time" } }; else return new TheoryData { - { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) Pacific Standard Time", "Pacific Standard Time", "Pacific Daylight Time" }, - { TimeZoneInfo.FindSystemTimeZoneById(s_strSydney), "(UTC+10:00) Australian Eastern Standard Time", "Australian Eastern Standard Time", "Australian Eastern Daylight Time" }, - { TimeZoneInfo.FindSystemTimeZoneById(s_strPerth), "(UTC+08:00) Australian Western Standard Time", "Australian Western Standard Time", "Australian Western Daylight Time" }, - { TimeZoneInfo.FindSystemTimeZoneById(s_strIran), "(UTC+03:30) +0330", "+0330", "+0430" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strPacific), "(UTC-08:00) Pacific Time (Los Angeles)", "Pacific Standard Time", "Pacific Daylight Time" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strSydney), "(UTC+10:00) Eastern Australia Time (Sydney)", "Australian Eastern Standard Time", "Australian Eastern Daylight Time" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strPerth), "(UTC+08:00) Australian Western Standard Time (Perth)", "Australian Western Standard Time", "Australian Western Daylight Time" }, + { TimeZoneInfo.FindSystemTimeZoneById(s_strIran), "(UTC+03:30) Iran Time", "Iran Standard Time", "Iran Daylight Time" }, - { s_NewfoundlandTz, "(UTC-03:30) NST", "NST", "NDT" }, - { s_catamarcaTz, "(UTC-03:00) -03", "-03", "-02" } + { s_NewfoundlandTz, "(UTC-03:30) Newfoundland Time (St. John’s)", "Newfoundland Standard Time", "Newfoundland Daylight Time" }, + { s_catamarcaTz, "(UTC-03:00) Argentina Standard Time (Catamarca)", "Argentina Standard Time", "Argentina Summer Time" } }; } - [Theory] + // We test the existence of a specific English time zone name to avoid failures on non-English platforms. + [ConditionalTheory(nameof(IsEnglishUILanguage))] [MemberData(nameof(Platform_TimeZoneNamesTestData))] - [PlatformSpecific(TestPlatforms.AnyUnix)] public static void Platform_TimeZoneNames(TimeZoneInfo tzi, string displayName, string standardName, string daylightName) { - if (PlatformDetection.IsBrowser) + // Edge case - Optionally allow some characters to be absent in the display name. + const string chars = ".’"; + foreach (char c in chars) { - // Console.WriteLine($"DisplayName: {tzi.DisplayName}, StandardName: {tzi.StandardName}, DaylightName: {tzi.DaylightName}"); - Assert.Equal($"DisplayName: {tzi.DisplayName}, StandardName: {tzi.StandardName}, DaylightName: {tzi.DaylightName}", - $"DisplayName: {displayName}, StandardName: {standardName}, DaylightName: {daylightName}"); + if (displayName.Contains(c, StringComparison.Ordinal) && !tzi.DisplayName.Contains(c, StringComparison.Ordinal)) + { + displayName = displayName.Replace(c.ToString(), "", StringComparison.Ordinal); + } } + + Assert.Equal($"DisplayName: \"{displayName}\", StandardName: {standardName}\", DaylightName: {daylightName}\"", + $"DisplayName: \"{tzi.DisplayName}\", StandardName: {tzi.StandardName}\", DaylightName: {tzi.DaylightName}\""); } [Fact] @@ -160,8 +178,9 @@ public static void LibyaTimeZone() Assert.True(libyaLocalTime.Equals(expectResult), string.Format("Expected {0} and got {1}", expectResult, libyaLocalTime)); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows))] - public static void TestYukunTZ() + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void TestYukonTZ() { try { @@ -190,7 +209,7 @@ public static void TestYukunTZ() } catch (TimeZoneNotFoundException) { - // Some Windows versions don't carry the complete TZ data. Ignore the tests on such versiosn. + // Some Windows versions don't carry the complete TZ data. Ignore the tests on such versions. } } @@ -1819,6 +1838,69 @@ public static void IsDaylightSavingTime_CasablancaMultiYearDaylightSavings(strin Assert.Equal(offset, s_casablancaTz.GetUtcOffset(dt)); } + [Fact] + [PlatformSpecific(~TestPlatforms.Windows)] + public static void TestSplittingRulesWhenReported() + { + // This test confirm we are splitting the rules which span multiple years on Linux + // we use "America/Los_Angeles" which has the rule covering 2/9/1942 to 8/14/1945 + // with daylight transition by 01:00:00. This rule should be split into 3 rules: + // - rule 1 from 2/9/1942 to 12/31/1942 + // - rule 2 from 1/1/1943 to 12/31/1944 + // - rule 3 from 1/1/1945 to 8/14/1945 + TimeZoneInfo.AdjustmentRule[] rules = TimeZoneInfo.FindSystemTimeZoneById(s_strPacific).GetAdjustmentRules(); + + bool ruleEncountered = false; + for (int i = 0; i < rules.Length; i++) + { + if (rules[i].DateStart == new DateTime(1942, 2, 9)) + { + Assert.True(i + 2 <= rules.Length - 1); + TimeSpan daylightDelta = TimeSpan.FromHours(1); + + // DateStart : 2/9/1942 12:00:00 AM (Unspecified) + // DateEnd : 12/31/1942 12:00:00 AM (Unspecified) + // DaylightDelta : 01:00:00 + // DaylightTransitionStart : ToD:02:00:00 M:2, D:9, W:1, DoW:Sunday, FixedDate:True + // DaylightTransitionEnd : ToD:23:59:59.9990000 M:12, D:31, W:1, DoW:Sunday, FixedDate:True + + Assert.Equal(new DateTime(1942, 12, 31), rules[i].DateEnd); + Assert.Equal(daylightDelta, rules[i].DaylightDelta); + Assert.Equal(TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 2, 0, 0), 2, 9), rules[i].DaylightTransitionStart); + Assert.Equal(TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 23, 59, 59, 999), 12, 31), rules[i].DaylightTransitionEnd); + + // DateStart : 1/1/1943 12:00:00 AM (Unspecified) + // DateEnd : 12/31/1944 12:00:00 AM (Unspecified) + // DaylightDelta : 01:00:00 + // DaylightTransitionStart : ToD:00:00:00 M:1, D:1, W:1, DoW:Sunday, FixedDate:True + // DaylightTransitionEnd : ToD:23:59:59.9990000 M:12, D:31, W:1, DoW:Sunday, FixedDate:True + + Assert.Equal(new DateTime(1943, 1, 1), rules[i + 1].DateStart); + Assert.Equal(new DateTime(1944, 12, 31), rules[i + 1].DateEnd); + Assert.Equal(daylightDelta, rules[i + 1].DaylightDelta); + Assert.Equal(TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 0, 0, 0), 1, 1), rules[i + 1].DaylightTransitionStart); + Assert.Equal(TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 23, 59, 59, 999), 12, 31), rules[i + 1].DaylightTransitionEnd); + + // DateStart : 1/1/1945 12:00:00 AM (Unspecified) + // DateEnd : 8/14/1945 12:00:00 AM (Unspecified) + // DaylightDelta : 01:00:00 + // DaylightTransitionStart : ToD:00:00:00 M:1, D:1, W:1, DoW:Sunday, FixedDate:True + // DaylightTransitionEnd : ToD:15:59:59.9990000 M:8, D:14, W:1, DoW:Sunday, FixedDate:True + + Assert.Equal(new DateTime(1945, 1, 1), rules[i + 2].DateStart); + Assert.Equal(new DateTime(1945, 8, 14), rules[i + 2].DateEnd); + Assert.Equal(daylightDelta, rules[i + 2].DaylightDelta); + Assert.Equal(TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 0, 0, 0), 1, 1), rules[i + 2].DaylightTransitionStart); + Assert.Equal(TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 15, 59, 59, 999), 8, 14), rules[i + 2].DaylightTransitionEnd); + + ruleEncountered = true; + break; + } + } + + Assert.True(ruleEncountered, "The 1942 rule of America/Los_Angeles not found."); + } + [Theory] [PlatformSpecific(TestPlatforms.AnyUnix)] // Linux will use local mean time for DateTimes before standard time came into effect. // in 1996 Europe/Lisbon changed from standard time to DST without changing the UTC offset @@ -2225,6 +2307,101 @@ public static IEnumerable SystemTimeZonesTestData() { yield return new object[] { tz }; } + + // Include fixed offset IANA zones in the test data when they are available. + if (!PlatformDetection.IsWindows) + { + for (int i = -14; i <= 12; i++) + { + TimeZoneInfo tz = null; + + try + { + string id = $"Etc/GMT{i:+0;-0}"; + tz = TimeZoneInfo.FindSystemTimeZoneById(id); + } + catch (TimeZoneNotFoundException) + { + } + + if (tz != null) + { + yield return new object[] { tz }; + } + } + } + } + + private const string IanaAbbreviationPattern = @"^(?:[A-Z][A-Za-z]+|[+-]\d{2}|[+-]\d{4})$"; + private static readonly Regex s_IanaAbbreviationRegex = new Regex(IanaAbbreviationPattern); + + // UTC aliases per https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml + // (This list is not likely to change.) + private static readonly string[] s_UtcAliases = new[] { + "Etc/UTC", + "Etc/UCT", + "Etc/Universal", + "Etc/Zulu", + "UCT", + "UTC", + "Universal", + "Zulu" + }; + + [Theory] + [MemberData(nameof(SystemTimeZonesTestData))] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public static void TimeZoneDisplayNames_Unix(TimeZoneInfo timeZone) + { + bool isUtc = s_UtcAliases.Contains(timeZone.Id, StringComparer.OrdinalIgnoreCase); + + if (PlatformDetection.IsBrowser) + { + // Browser platform doesn't have full ICU names, but uses the IANA IDs and abbreviations instead. + + // The display name will be the offset plus the ID. + // The offset is checked separately in TimeZoneInfo_DisplayNameStartsWithOffset + Assert.True(timeZone.DisplayName.EndsWith(" " + timeZone.Id), + $"Id: \"{timeZone.Id}\", DisplayName should have ended with the ID, Actual DisplayName: \"{timeZone.DisplayName}\""); + + if (isUtc) + { + // Make sure UTC and its aliases have exactly "UTC" for the standard and daylight names + Assert.True(timeZone.StandardName == "UTC", + $"Id: \"{timeZone.Id}\", Expected StandardName: \"UTC\", Actual StandardName: \"{timeZone.StandardName}\""); + Assert.True(timeZone.DaylightName == "UTC", + $"Id: \"{timeZone.Id}\", Expected DaylightName: \"UTC\", Actual DaylightName: \"{timeZone.DaylightName}\""); + } + else + { + // For other time zones, match any valid IANA time zone abbreviation, including numeric forms + Assert.True(s_IanaAbbreviationRegex.IsMatch(timeZone.StandardName), + $"Id: \"{timeZone.Id}\", StandardName should have matched the pattern @\"{IanaAbbreviationPattern}\", Actual StandardName: \"{timeZone.StandardName}\""); + Assert.True(s_IanaAbbreviationRegex.IsMatch(timeZone.DaylightName), + $"Id: \"{timeZone.Id}\", DaylightName should have matched the pattern @\"{IanaAbbreviationPattern}\", Actual DaylightName: \"{timeZone.DaylightName}\""); + } + } + else if (isUtc) + { + // UTC's display name is the string "(UTC) " and the same text as the standard name. + Assert.True(timeZone.DisplayName == $"(UTC) {timeZone.StandardName}", + $"Id: \"{timeZone.Id}\", Expected DisplayName: \"(UTC) {timeZone.StandardName}\", Actual DisplayName: \"{timeZone.DisplayName}\""); + + // All aliases of UTC should have the same names as UTC itself + Assert.True(timeZone.DisplayName == TimeZoneInfo.Utc.DisplayName, + $"Id: \"{timeZone.Id}\", Expected DisplayName: \"{TimeZoneInfo.Utc.DisplayName}\", Actual DisplayName: \"{timeZone.DisplayName}\""); + Assert.True(timeZone.StandardName == TimeZoneInfo.Utc.StandardName, + $"Id: \"{timeZone.Id}\", Expected StandardName: \"{TimeZoneInfo.Utc.StandardName}\", Actual StandardName: \"{timeZone.StandardName}\""); + Assert.True(timeZone.DaylightName == TimeZoneInfo.Utc.DaylightName, + $"Id: \"{timeZone.Id}\", Expected DaylightName: \"{TimeZoneInfo.Utc.DaylightName}\", Actual DaylightName: \"{timeZone.DaylightName}\""); + } + else + { + // All we can really say generically here is that they aren't empty. + Assert.False(string.IsNullOrWhiteSpace(timeZone.DisplayName), $"Id: \"{timeZone.Id}\", DisplayName should not have been empty."); + Assert.False(string.IsNullOrWhiteSpace(timeZone.StandardName), $"Id: \"{timeZone.Id}\", StandardName should not have been empty."); + Assert.False(string.IsNullOrWhiteSpace(timeZone.DaylightName), $"Id: \"{timeZone.Id}\", DaylightName should not have been empty."); + } } [ActiveIssue("https://github.com/dotnet/runtime/issues/19794", TestPlatforms.AnyUnix)] @@ -2290,6 +2467,79 @@ public static void GetSystemTimeZones_AllTimeZonesHaveOffsetInValidRange() } } + private static byte [] timeZoneFileContents = new byte[] + { + 0x54, 0x5A, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x5A, 0x69, 0x66, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x4C, + 0x4D, 0x54, 0x00, 0x2B, 0x30, 0x31, 0x00, 0x2B, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + // POSIX Rule + // 0x0A, 0x3C, 0x2B, 0x30, 0x30, 0x3E, 0x30, 0x3C, 0x2B, 0x30, 0x31, + // 0x3E, 0x2C, 0x30, 0x2F, 0x30, 0x2C, 0x4A, 0x33, 0x36, 0x35, 0x2F, 0x32, 0x35, 0x0A + }; + + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [PlatformSpecific(TestPlatforms.AnyUnix)] + [InlineData("<+00>0<+01>,0/0,J365/25", 1, 1, true)] + [InlineData("<+00>0<+01>,30/0,J365/25", 31, 1, true)] + [InlineData("<+00>0<+01>,31/0,J365/25", 1, 2, true)] + [InlineData("<+00>0<+01>,58/0,J365/25", 28, 2, true)] + [InlineData("<+00>0<+01>,59/0,J365/25", 0, 0, false)] + [InlineData("<+00>0<+01>,9999999/0,J365/25", 0, 0, false)] + [InlineData("<+00>0<+01>,A/0,J365/25", 0, 0, false)] + public static void NJulianRuleTest(string posixRule, int dayNumber, int monthNumber, bool shouldSucceed) + { + string zoneFilePath = Path.GetTempPath() + Path.GetRandomFileName(); + using (FileStream fs = new FileStream(zoneFilePath, FileMode.Create)) + { + fs.Write(timeZoneFileContents.AsSpan()); + + // Append the POSIX rule + fs.WriteByte(0x0A); + foreach (char c in posixRule) + { + fs.WriteByte((byte) c); + } + fs.WriteByte(0x0A); + } + + try + { + ProcessStartInfo psi = new ProcessStartInfo() { UseShellExecute = false }; + psi.Environment.Add("TZ", zoneFilePath); + + RemoteExecutor.Invoke((day, month, succeed) => + { + bool expectedToSucceed = bool.Parse(succeed); + int d = int.Parse(day); + int m = int.Parse(month); + + TimeZoneInfo.AdjustmentRule [] rules = TimeZoneInfo.Local.GetAdjustmentRules(); + + if (expectedToSucceed) + { + Assert.Equal(1, rules.Length); + Assert.Equal(d, rules[0].DaylightTransitionStart.Day); + Assert.Equal(m, rules[0].DaylightTransitionStart.Month); + } + else + { + Assert.Equal(0, rules.Length); + } + }, dayNumber.ToString(), monthNumber.ToString(), shouldSucceed.ToString(), new RemoteInvokeOptions { StartInfo = psi}).Dispose(); + } + finally + { + try { File.Delete(zoneFilePath); } catch { } // don't fail the test if we couldn't delete the file. + } + } + [Fact] public static void TimeZoneInfo_DaylightDeltaIsNoMoreThan12Hours() { @@ -2302,33 +2552,47 @@ public static void TimeZoneInfo_DaylightDeltaIsNoMoreThan12Hours() } } - [Fact] - public static void TimeZoneInfo_DisplayNameStartsWithOffset() + [Theory] + [MemberData(nameof(SystemTimeZonesTestData))] + public static void TimeZoneInfo_DisplayNameStartsWithOffset(TimeZoneInfo tzi) { - foreach (TimeZoneInfo tzi in TimeZoneInfo.GetSystemTimeZones()) + if (s_UtcAliases.Contains(tzi.Id, StringComparer.OrdinalIgnoreCase)) { - if (tzi.Id != "UTC") - { - Assert.False(string.IsNullOrWhiteSpace(tzi.StandardName)); - Assert.Matches(@"^\(UTC(\+|-)[0-9]{2}:[0-9]{2}\) \S.*", tzi.DisplayName); + // UTC and all of its aliases (Etc/UTC, and others) start with just "(UTC) " + Assert.StartsWith("(UTC) ", tzi.DisplayName); + } + else + { + Assert.False(string.IsNullOrWhiteSpace(tzi.StandardName)); + Assert.Matches(@"^\(UTC(\+|-)[0-9]{2}:[0-9]{2}\) \S.*", tzi.DisplayName); - // see https://github.com/dotnet/corefx/pull/33204#issuecomment-438782500 - if (PlatformDetection.IsNotWindowsNanoServer && !PlatformDetection.IsWindows7) + // see https://github.com/dotnet/corefx/pull/33204#issuecomment-438782500 + if (PlatformDetection.IsNotWindowsNanoServer && !PlatformDetection.IsWindows7) + { + string offset = Regex.Match(tzi.DisplayName, @"(-|)[0-9]{2}:[0-9]{2}").Value; + TimeSpan ts = TimeSpan.Parse(offset); + if (PlatformDetection.IsWindows && + tzi.BaseUtcOffset != ts && + (tzi.Id.Contains("Morocco") || tzi.Id.Contains("Volgograd"))) { - string offset = Regex.Match(tzi.DisplayName, @"(-|)[0-9]{2}:[0-9]{2}").Value; - TimeSpan ts = TimeSpan.Parse(offset); - if (tzi.BaseUtcOffset != ts && tzi.Id.IndexOf("Morocco", StringComparison.Ordinal) >= 0) + // Windows data can report display name with UTC+01:00 offset which is not matching the actual BaseUtcOffset. + // We special case this in the test to avoid the test failures like: + // 01:00 != 00:00:00, dn:(UTC+01:00) Casablanca, sn:Morocco Standard Time + // 04:00 != 03:00:00, dn:(UTC+04:00) Volgograd, sn:Volgograd Standard Time + if (tzi.Id.Contains("Morocco")) { - // Windows data can report display name with UTC+01:00 offset which is not matching the actual BaseUtcOffset. - // We special case this in the test to avoid the test failures like: - // 01:00 != 00:00:00, dn:(UTC+01:00) Casablanca, sn:Morocco Standard Time Assert.True(tzi.BaseUtcOffset == new TimeSpan(0, 0, 0), $"{offset} != {tzi.BaseUtcOffset}, dn:{tzi.DisplayName}, sn:{tzi.StandardName}"); } else { - Assert.True(tzi.BaseUtcOffset == ts || tzi.GetUtcOffset(DateTime.Now) == ts, $"{offset} != {tzi.BaseUtcOffset}, dn:{tzi.DisplayName}, sn:{tzi.StandardName}"); + // Volgograd, Russia + Assert.True(tzi.BaseUtcOffset == new TimeSpan(3, 0, 0), $"{offset} != {tzi.BaseUtcOffset}, dn:{tzi.DisplayName}, sn:{tzi.StandardName}"); } } + else + { + Assert.True(tzi.BaseUtcOffset == ts || tzi.GetUtcOffset(DateTime.Now) == ts, $"{offset} != {tzi.BaseUtcOffset}, dn:{tzi.DisplayName}, sn:{tzi.StandardName}"); + } } } } @@ -2409,7 +2673,8 @@ public static void TestTimeZoneIdBackwardCompatibility(string oldId, string curr TimeZoneInfo currenttz = TimeZoneInfo.FindSystemTimeZoneById(currentId); Assert.Equal(oldtz.StandardName, currenttz.StandardName); - Assert.Equal(oldtz.DisplayName, currenttz.DisplayName); + Assert.Equal(oldtz.DaylightName, currenttz.DaylightName); + // Note we cannot test the DisplayName, as it will contain the ID. } [Theory] @@ -2423,7 +2688,8 @@ public static void TestTimeZoneIdBackwardCompatibility(string oldId, string curr public static void ChangeLocalTimeZone(string id) { string originalTZ = Environment.GetEnvironmentVariable("TZ"); - try { + try + { TimeZoneInfo.ClearCachedData(); Environment.SetEnvironmentVariable("TZ", id); @@ -2433,13 +2699,16 @@ public static void ChangeLocalTimeZone(string id) Assert.Equal(tz.StandardName, localtz.StandardName); Assert.Equal(tz.DisplayName, localtz.DisplayName); } - finally { + finally + { TimeZoneInfo.ClearCachedData(); Environment.SetEnvironmentVariable("TZ", originalTZ); } } - private static bool IsEnglishUILanguageAndRemoteExecutorSupported => (CultureInfo.CurrentUICulture.Name == "en" || CultureInfo.CurrentUICulture.Name.StartsWith("en-", StringComparison.Ordinal)) && RemoteExecutor.IsSupported; + private static bool IsEnglishUILanguage => CultureInfo.CurrentUICulture.Name.Length == 0 || CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "en"; + + private static bool IsEnglishUILanguageAndRemoteExecutorSupported => IsEnglishUILanguage && RemoteExecutor.IsSupported; private static void VerifyConvertException(DateTimeOffset inputTime, string destinationTimeZoneId) where TException : Exception { diff --git a/src/libraries/System.Runtime/tests/System/Uri.CreateStringTests.cs b/src/libraries/System.Runtime/tests/System/Uri.CreateStringTests.cs index 9255f0b42e86e..32c08a3dabf12 100644 --- a/src/libraries/System.Runtime/tests/System/Uri.CreateStringTests.cs +++ b/src/libraries/System.Runtime/tests/System/Uri.CreateStringTests.cs @@ -423,7 +423,7 @@ public static IEnumerable Scheme_Authority_TestData() yield return new object[] { "http://abc\u1234\u2345\u3456@host/", "http", "abc%E1%88%B4%E2%8D%85%E3%91%96", "host", UriHostNameType.Dns, 80, true, false }; yield return new object[] { "http://\u1234abc\u2345\u3456@host/", "http", "%E1%88%B4abc%E2%8D%85%E3%91%96", "host", UriHostNameType.Dns, 80, true, false }; yield return new object[] { "http://\u1234\u2345\u3456abc@host/", "http", "%E1%88%B4%E2%8D%85%E3%91%96abc", "host", UriHostNameType.Dns, 80, true, false }; - yield return new object[] { "http://userinfo!~+-_*()[]:;&$=123USERINFO@host/", "http", "userinfo!~+-_*()[]:;&$=123USERINFO", "host", UriHostNameType.Dns, 80, true, false }; + yield return new object[] { "http://userinfo!~+-_*()[]:;&$=123PLACEHOLDER@host/", "http", "userinfo!~+-_*()[]:;&$=123PLACEHOLDER", "host", UriHostNameType.Dns, 80, true, false }; yield return new object[] { "http://%68%65%6C%6C%6F@host/", "http", "hello", "host", UriHostNameType.Dns, 80, true, false }; yield return new object[] { "http://\u00A3@host/", "http", "%C2%A3", "host", UriHostNameType.Dns, 80, true, false }; yield return new object[] { "http://\u1234@host/", "http", "%E1%88%B4", "host", UriHostNameType.Dns, 80, true, false }; diff --git a/src/libraries/System.Runtime/tests/System/VersionTests.cs b/src/libraries/System.Runtime/tests/System/VersionTests.cs index 2498f7f052b22..8e6fd266c80a7 100644 --- a/src/libraries/System.Runtime/tests/System/VersionTests.cs +++ b/src/libraries/System.Runtime/tests/System/VersionTests.cs @@ -282,46 +282,6 @@ public static void Parse_InvalidInput_ThrowsException(string input, Type excepti Assert.Null(version); } - public static IEnumerable ToString_TestData() - { - yield return new object[] { new Version(1, 2), new string[] { "", "1", "1.2" } }; - yield return new object[] { new Version(1, 2, 3), new string[] { "", "1", "1.2", "1.2.3" } }; - yield return new object[] { new Version(1, 2, 3, 4), new string[] { "", "1", "1.2", "1.2.3", "1.2.3.4" } }; - } - - [Theory] - [MemberData(nameof(ToString_TestData))] - public static void ToString_Invoke_ReturnsExpected(Version version, string[] expected) - { - for (int i = 0; i < expected.Length; i++) - { - Assert.Equal(expected[i], version.ToString(i)); - } - - int maxFieldCount = expected.Length - 1; - Assert.Equal(expected[maxFieldCount], version.ToString()); - - AssertExtensions.Throws("fieldCount", () => version.ToString(-1)); // Index < 0 - AssertExtensions.Throws("fieldCount", () => version.ToString(maxFieldCount + 1)); // Index > version.fieldCount - } - - private static void VerifyVersion(Version version, int major, int minor, int build, int revision) - { - Assert.Equal(major, version.Major); - Assert.Equal(minor, version.Minor); - Assert.Equal(build, version.Build); - Assert.Equal(revision, version.Revision); - Assert.Equal((short)(revision >> 16), version.MajorRevision); - Assert.Equal(unchecked((short)(revision & 0xFFFF)), version.MinorRevision); - - Version clone = Assert.IsType(version.Clone()); - Assert.NotSame(version, clone); - Assert.Equal(version.Major, clone.Major); - Assert.Equal(version.Minor, clone.Minor); - Assert.Equal(version.Build, clone.Build); - Assert.Equal(version.Revision, clone.Revision); - } - public static IEnumerable Parse_ValidWithOffsetCount_TestData() { foreach (object[] inputs in Parse_Valid_TestData()) @@ -365,6 +325,46 @@ public static void Parse_Span_InvalidInput_ThrowsException(string input, Type ex Assert.Null(version); } + public static IEnumerable ToString_TestData() + { + yield return new object[] { new Version(1, 2), new string[] { "", "1", "1.2" } }; + yield return new object[] { new Version(1, 2, 3), new string[] { "", "1", "1.2", "1.2.3" } }; + yield return new object[] { new Version(1, 2, 3, 4), new string[] { "", "1", "1.2", "1.2.3", "1.2.3.4" } }; + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public static void ToString_Invoke_ReturnsExpected(Version version, string[] expected) + { + for (int i = 0; i < expected.Length; i++) + { + Assert.Equal(expected[i], version.ToString(i)); + } + + int maxFieldCount = expected.Length - 1; + Assert.Equal(expected[maxFieldCount], version.ToString()); + + AssertExtensions.Throws("fieldCount", () => version.ToString(-1)); // Index < 0 + AssertExtensions.Throws("fieldCount", () => version.ToString(maxFieldCount + 1)); // Index > version.fieldCount + } + + private static void VerifyVersion(Version version, int major, int minor, int build, int revision) + { + Assert.Equal(major, version.Major); + Assert.Equal(minor, version.Minor); + Assert.Equal(build, version.Build); + Assert.Equal(revision, version.Revision); + Assert.Equal((short)(revision >> 16), version.MajorRevision); + Assert.Equal(unchecked((short)(revision & 0xFFFF)), version.MinorRevision); + + Version clone = Assert.IsType(version.Clone()); + Assert.NotSame(version, clone); + Assert.Equal(version.Major, clone.Major); + Assert.Equal(version.Minor, clone.Minor); + Assert.Equal(version.Build, clone.Build); + Assert.Equal(version.Revision, clone.Revision); + } + [Theory] [MemberData(nameof(ToString_TestData))] public static void TryFormat_Invoke_WritesExpected(Version version, string[] expected) diff --git a/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.il b/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.il index 31e53b3de87c8..b2d99fc7a8c99 100644 --- a/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.il +++ b/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.il @@ -27,6 +27,22 @@ ret } +.method public static void TestMethodFoo() +{ + ret +} + +.method public static void TestMethodFoo(int32 myInt) +{ + ret +} + +.method private static int32 TestMethodBar(int32 myInt) +{ + ldarg.0 + ret +} + .namespace System.Reflection.TestModule { .class public Dummy extends [System.Runtime]System.Object diff --git a/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.ilproj b/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.ilproj index 34d98e119bee8..741c9700ba604 100644 --- a/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.ilproj +++ b/src/libraries/System.Runtime/tests/TestModule/System.Reflection.TestModule.ilproj @@ -1,11 +1,10 @@  1.0.0.0 - {3B7489C4-65DB-4E69-BE01-F6234133400C} netstandard2.0 Microsoft - \ No newline at end of file + diff --git a/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.ilproj b/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.ilproj index e6ecbbf098681..09afbeb33af07 100644 --- a/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.ilproj +++ b/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.ilproj @@ -1,11 +1,10 @@  1.0.0.0 - {6B002B34-089C-4BC4-91DD-57D350DEA91C} netstandard2.0 Microsoft - \ No newline at end of file + diff --git a/src/libraries/System.Security.AccessControl/ref/System.Security.AccessControl.cs b/src/libraries/System.Security.AccessControl/ref/System.Security.AccessControl.cs index b47ecfdc10165..f974e50b07bf7 100644 --- a/src/libraries/System.Security.AccessControl/ref/System.Security.AccessControl.cs +++ b/src/libraries/System.Security.AccessControl/ref/System.Security.AccessControl.cs @@ -530,3 +530,44 @@ public void SetAudit(System.Security.AccessControl.AuditFlags auditFlags, System public void SetAudit(System.Security.Principal.SecurityIdentifier sid, System.Security.AccessControl.ObjectAuditRule rule) { } } } + +namespace System.Security.Policy +{ + public sealed partial class Evidence : System.Collections.ICollection, System.Collections.IEnumerable + { + public Evidence() { } + [Obsolete("This constructor is obsolete. Please use the constructor which takes arrays of EvidenceBase instead.")] + public Evidence(object[] hostEvidence, object[] assemblyEvidence) { } + public Evidence(System.Security.Policy.Evidence evidence) { } + public Evidence(System.Security.Policy.EvidenceBase[] hostEvidence, System.Security.Policy.EvidenceBase[] assemblyEvidence) { } + [Obsolete("Evidence should not be treated as an ICollection. Please use GetHostEnumerator and GetAssemblyEnumerator to iterate over the evidence to collect a count.")] + public int Count { get { throw null; } } + public bool IsReadOnly { get { throw null; } } + public bool IsSynchronized { get { throw null; } } + public bool Locked { get { throw null; } set { } } + public object SyncRoot { get { throw null; } } + [Obsolete("This method is obsolete. Please use AddAssemblyEvidence instead.")] + public void AddAssembly(object id) { } + public void AddAssemblyEvidence(T evidence) where T : System.Security.Policy.EvidenceBase { } + [Obsolete("This method is obsolete. Please use AddHostEvidence instead.")] + public void AddHost(object id) { } + public void AddHostEvidence(T evidence) where T : System.Security.Policy.EvidenceBase { } + public void Clear() { } + public System.Security.Policy.Evidence? Clone() { throw null; } + [Obsolete("Evidence should not be treated as an ICollection. Please use the GetHostEnumerator and GetAssemblyEnumerator methods rather than using CopyTo.")] + public void CopyTo(System.Array array, int index) { } + public System.Collections.IEnumerator GetAssemblyEnumerator() { throw null; } + public T? GetAssemblyEvidence() where T : System.Security.Policy.EvidenceBase { throw null; } + [Obsolete("GetEnumerator is obsolete. Please use GetAssemblyEnumerator and GetHostEnumerator instead.")] + public System.Collections.IEnumerator GetEnumerator() { throw null; } + public System.Collections.IEnumerator GetHostEnumerator() { throw null; } + public T? GetHostEvidence() where T : System.Security.Policy.EvidenceBase { throw null; } + public void Merge(System.Security.Policy.Evidence evidence) { } + public void RemoveType(System.Type t) { } + } + public abstract partial class EvidenceBase + { + protected EvidenceBase() { } + public virtual System.Security.Policy.EvidenceBase? Clone() { throw null; } + } +} \ No newline at end of file diff --git a/src/libraries/System.Security.AccessControl/src/System.Security.AccessControl.csproj b/src/libraries/System.Security.AccessControl/src/System.Security.AccessControl.csproj index 524801d2ed72c..4f1fd6cd42513 100644 --- a/src/libraries/System.Security.AccessControl/src/System.Security.AccessControl.csproj +++ b/src/libraries/System.Security.AccessControl/src/System.Security.AccessControl.csproj @@ -24,6 +24,8 @@ + + diff --git a/src/libraries/System.Security.Permissions/src/System/Security/Policy/Evidence.cs b/src/libraries/System.Security.AccessControl/src/System/Security/Policy/Evidence.cs similarity index 90% rename from src/libraries/System.Security.Permissions/src/System/Security/Policy/Evidence.cs rename to src/libraries/System.Security.AccessControl/src/System/Security/Policy/Evidence.cs index 266df3cbadded..dfeaff878b84a 100644 --- a/src/libraries/System.Security.Permissions/src/System/Security/Policy/Evidence.cs +++ b/src/libraries/System.Security.AccessControl/src/System/Security/Policy/Evidence.cs @@ -22,12 +22,12 @@ public Evidence(EvidenceBase[] hostEvidence, EvidenceBase[] assemblyEvidence) { public void AddAssembly(object id) { } public void AddAssemblyEvidence(T evidence) where T : EvidenceBase { } public void AddHostEvidence(T evidence) where T : EvidenceBase { } - public T GetAssemblyEvidence() where T : EvidenceBase { return default(T); } - public T GetHostEvidence() where T : EvidenceBase { return default(T); } + public T? GetAssemblyEvidence() where T : EvidenceBase { return default(T); } + public T? GetHostEvidence() where T : EvidenceBase { return default(T); } [Obsolete("This method is obsolete. Please use AddHostEvidence instead.")] public void AddHost(object id) { } public void Clear() { } - public Evidence Clone() { return default(Evidence); } + public Evidence? Clone() { return default(Evidence); } [Obsolete("Evidence should not be treated as an ICollection. Please use the GetHostEnumerator and GetAssemblyEnumerator methods rather than using CopyTo.")] public void CopyTo(Array array, int index) { } public IEnumerator GetAssemblyEnumerator() { return Array.Empty().GetEnumerator(); } diff --git a/src/libraries/System.Security.Permissions/src/System/Security/Policy/EvidenceBase.cs b/src/libraries/System.Security.AccessControl/src/System/Security/Policy/EvidenceBase.cs similarity index 77% rename from src/libraries/System.Security.Permissions/src/System/Security/Policy/EvidenceBase.cs rename to src/libraries/System.Security.AccessControl/src/System/Security/Policy/EvidenceBase.cs index 243bd36afee0d..b2790af0cd1f5 100644 --- a/src/libraries/System.Security.Permissions/src/System/Security/Policy/EvidenceBase.cs +++ b/src/libraries/System.Security.AccessControl/src/System/Security/Policy/EvidenceBase.cs @@ -6,6 +6,6 @@ namespace System.Security.Policy public abstract partial class EvidenceBase { protected EvidenceBase() { } - public virtual EvidenceBase Clone() { return default(EvidenceBase); } + public virtual EvidenceBase? Clone() { return default(EvidenceBase); } } } diff --git a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_AddAccess.cs b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_AddAccess.cs index f4a5bee34744c..0689478e0c813 100644 --- a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_AddAccess.cs +++ b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_AddAccess.cs @@ -292,7 +292,7 @@ public static void AddAccess_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); @@ -326,7 +326,7 @@ public static void AddAccess_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[GenericAcl.MaxBinaryLength + 1 - 8 - 4 - 16]; gAce = new CustomAce(AceType.MaxDefinedAceType + 1, - AceFlags.InheritanceFlags, opaque); ; + AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); //After Mark changes design to make ACL with any CustomAce, CompoundAce uncanonical and diff --git a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccess.cs b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccess.cs index 6f0fbc6d6adbc..d09c3098e8d55 100644 --- a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccess.cs +++ b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccess.cs @@ -190,7 +190,7 @@ public static void RemoveAccess_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); diff --git a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccessSpecific.cs b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccessSpecific.cs index 37941bc3c3d41..690e78f0fb0f9 100644 --- a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccessSpecific.cs +++ b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_RemoveAccessSpecific.cs @@ -177,7 +177,7 @@ public static void RemoveAccessSpecific_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); diff --git a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_SetAccess.cs b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_SetAccess.cs index eb697bed70ccc..a04a4b9c1f915 100644 --- a/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_SetAccess.cs +++ b/src/libraries/System.Security.AccessControl/tests/DiscretionaryAcl/DiscretionaryAcl_SetAccess.cs @@ -182,7 +182,7 @@ public static void SetAccess_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); @@ -221,7 +221,7 @@ public static void SetAccess_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[GenericAcl.MaxBinaryLength + 1 - 8 - 4 - 16]; gAce = new CustomAce(AceType.MaxDefinedAceType + 1, - AceFlags.InheritanceFlags, opaque); ; + AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); @@ -256,7 +256,7 @@ public static void SetAccess_AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[GenericAcl.MaxBinaryLength + 1 - 8 - 4 - 28]; gAce = new CustomAce(AceType.MaxDefinedAceType + 1, - AceFlags.InheritanceFlags, opaque); ; + AceFlags.InheritanceFlags, opaque); rawAcl.InsertAce(0, gAce); discretionaryAcl = new DiscretionaryAcl(isContainer, isDS, rawAcl); diff --git a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_AddAudit.cs b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_AddAudit.cs index dc4532ebd1da2..60687205b7eae 100644 --- a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_AddAudit.cs +++ b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_AddAudit.cs @@ -221,7 +221,7 @@ public static void AdditionalTestCases() accessMask = 1; rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); rawAcl.InsertAce(0, gAce); systemAcl = new SystemAcl(isContainer, isDS, rawAcl); gAce = new CommonAce(AceFlags.ContainerInherit | AceFlags.InheritOnly | AceFlags.AuditFlags, @@ -251,7 +251,7 @@ public static void AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[GenericAcl.MaxBinaryLength + 1 - 8 - 4 - 16]; gAce = new CustomAce(AceType.MaxDefinedAceType + 1, - AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); ; + AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); rawAcl.InsertAce(0, gAce); systemAcl = new SystemAcl(isContainer, isDS, rawAcl); //After Mark changes design to make ACL with any CustomAce, CompoundAce uncanonical and diff --git a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAudit.cs b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAudit.cs index 98bdf7ef34052..40a9243caa5f6 100644 --- a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAudit.cs +++ b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAudit.cs @@ -187,7 +187,7 @@ public static void AdditionalTestCases() accessMask = 1; rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); rawAcl.InsertAce(0, gAce); systemAcl = new SystemAcl(isContainer, isDS, rawAcl); //After Mark changes design to make ACL with any CustomAce, CompoundAce uncanonical and diff --git a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAuditSpecific.cs b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAuditSpecific.cs index c0c489fcae540..c29b29a00e985 100644 --- a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAuditSpecific.cs +++ b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_RemoveAuditSpecific.cs @@ -196,7 +196,7 @@ public static void AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[4]; gAce = new CustomAce(AceType.MaxDefinedAceType + 1, - AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); ; + AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); rawAcl.InsertAce(0, gAce); systemAcl = new SystemAcl(isContainer, isDS, rawAcl); //After Mark changes design to make ACL with any CustomAce, CompoundAce uncanonical and diff --git a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_SetAudit.cs b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_SetAudit.cs index 80fdf70afe450..f4af018416829 100644 --- a/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_SetAudit.cs +++ b/src/libraries/System.Security.AccessControl/tests/SystemAcl/SystemAcl_SetAudit.cs @@ -172,7 +172,7 @@ public static void AdditionalTestCases() accessMask = 1; rawAcl = new RawAcl(0, 1); opaque = new byte[4]; - gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); ; + gAce = new CustomAce(AceType.MaxDefinedAceType + 1, AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); rawAcl.InsertAce(0, gAce); systemAcl = new SystemAcl(isContainer, isDS, rawAcl); gAce = new CommonAce(AceFlags.ContainerInherit | AceFlags.InheritOnly | AceFlags.AuditFlags, @@ -202,7 +202,7 @@ public static void AdditionalTestCases() rawAcl = new RawAcl(0, 1); opaque = new byte[GenericAcl.MaxBinaryLength + 1 - 8 - 4 - 16]; gAce = new CustomAce(AceType.MaxDefinedAceType + 1, - AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); ; + AceFlags.InheritanceFlags | AceFlags.AuditFlags, opaque); rawAcl.InsertAce(0, gAce); systemAcl = new SystemAcl(isContainer, isDS, rawAcl); //After Mark changes design to make ACL with any CustomAce, CompoundAce uncanonical and diff --git a/src/libraries/System.Security.Claims/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.Security.Claims/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.Security.Claims/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.Security.Claims/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs index 042a5f6efc7a9..1c7fbb1ea71f9 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.OSX.cs @@ -5,7 +5,7 @@ namespace Internal.Cryptography { - internal partial class AesImplementation + internal sealed partial class AesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs index 24dce8379f15b..378b4cf0c1121 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Unix.cs @@ -6,7 +6,7 @@ namespace Internal.Cryptography { - internal partial class AesImplementation + internal sealed partial class AesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs index f08ecbb6780a1..953bcde1cc132 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/AesImplementation.Windows.cs @@ -6,7 +6,7 @@ namespace Internal.Cryptography { - internal partial class AesImplementation + internal sealed partial class AesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs index fc88b0a4431cf..72714b4cf3eb7 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.OSX.cs @@ -5,7 +5,7 @@ namespace Internal.Cryptography { - internal partial class DesImplementation + internal sealed partial class DesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs index 7bf051e669dfa..2baa3062ea5ef 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Unix.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { - internal partial class DesImplementation + internal sealed partial class DesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs index a6d53c6e8879f..dba5858591c3d 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/DesImplementation.Windows.cs @@ -6,7 +6,7 @@ namespace Internal.Cryptography { - internal partial class DesImplementation + internal sealed partial class DesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs index 21647eca04f53..8c4a851572c4b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Unix.cs @@ -11,38 +11,23 @@ namespace Internal.Cryptography { internal static partial class HashProviderDispenser { - private static volatile IntPtr s_evpMd5; - private static volatile IntPtr s_evpSha1; - private static volatile IntPtr s_evpSha256; - private static volatile IntPtr s_evpSha384; - private static volatile IntPtr s_evpSha512; - - public static HashProvider CreateHashProvider(string hashAlgorithmId) + internal static HashProvider CreateHashProvider(string hashAlgorithmId) { - IntPtr evpType = HashAlgorithmToEvp(hashAlgorithmId); + IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId); return new EvpHashProvider(evpType); } - public static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan key) + internal static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpan key) { - IntPtr evpType = HashAlgorithmToEvp(hashAlgorithmId); + IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId); return new HmacHashProvider(evpType, key); } - public static IntPtr HashAlgorithmToEvp(string hashAlgorithmId) => hashAlgorithmId switch { - HashAlgorithmNames.SHA1 => s_evpSha1 == IntPtr.Zero ? (s_evpSha1 = Interop.Crypto.EvpSha1()) : s_evpSha1, - HashAlgorithmNames.SHA256 => s_evpSha256 == IntPtr.Zero ? (s_evpSha256 = Interop.Crypto.EvpSha256()) : s_evpSha256, - HashAlgorithmNames.SHA384 => s_evpSha384 == IntPtr.Zero ? (s_evpSha384 = Interop.Crypto.EvpSha384()) : s_evpSha384, - HashAlgorithmNames.SHA512 => s_evpSha512 == IntPtr.Zero ? (s_evpSha512 = Interop.Crypto.EvpSha512()) : s_evpSha512, - HashAlgorithmNames.MD5 => s_evpMd5 == IntPtr.Zero ? (s_evpMd5 = Interop.Crypto.EvpMd5()) : s_evpMd5, - _ => throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmId)) - }; - internal static class OneShotHashProvider { public static unsafe int HashData(string hashAlgorithmId, ReadOnlySpan source, Span destination) { - IntPtr evpType = HashAlgorithmToEvp(hashAlgorithmId); + IntPtr evpType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmId); Debug.Assert(evpType != IntPtr.Zero); int hashSize = Interop.Crypto.EvpMdSize(evpType); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs index d7b710be50ccf..4fa960795c46e 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/HashProviderDispenser.Windows.cs @@ -30,39 +30,37 @@ public static HashProvider CreateMacProvider(string hashAlgorithmId, ReadOnlySpa public static class OneShotHashProvider { - private static volatile bool s_useCompatOneShot; - public static unsafe int HashData(string hashAlgorithmId, ReadOnlySpan source, Span destination) { int hashSize; // in bytes // Try using a pseudo-handle if available. - if (!s_useCompatOneShot) + if (Interop.BCrypt.PseudoHandlesSupported) + { + HashDataUsingPseudoHandle(hashAlgorithmId, source, destination, out hashSize); + return hashSize; + } + else { - if (HashDataUsingPseudoHandle(hashAlgorithmId, source, destination, out hashSize)) + // Pseudo-handle not available. Fall back to a shared handle with no using or dispose. + SafeBCryptAlgorithmHandle cachedAlgorithmHandle = BCryptAlgorithmCache.GetCachedBCryptAlgorithmHandle( + hashAlgorithmId, + BCryptOpenAlgorithmProviderFlags.None, + out hashSize); + + if (destination.Length < hashSize) { - return hashSize; + Debug.Fail("Caller should have checked length."); + throw new CryptographicException(); } - } - // Pseudo-handle not available or a precondition check failed. - // Fall back to a shared handle with no using or dispose. - SafeBCryptAlgorithmHandle cachedAlgorithmHandle = BCryptAlgorithmCache.GetCachedBCryptAlgorithmHandle( - hashAlgorithmId, - BCryptOpenAlgorithmProviderFlags.None, - out hashSize); + HashUpdateAndFinish(cachedAlgorithmHandle, hashSize, source, destination); - if (destination.Length < hashSize) - { - throw new CryptographicException(); + return hashSize; } - - HashUpdateAndFinish(cachedAlgorithmHandle, hashSize, source, destination); - - return hashSize; } - private static unsafe bool HashDataUsingPseudoHandle(string hashAlgorithmId, ReadOnlySpan source, Span destination, out int hashSize) + private static unsafe void HashDataUsingPseudoHandle(string hashAlgorithmId, ReadOnlySpan source, Span destination, out int hashSize) { hashSize = default; @@ -97,37 +95,27 @@ private static unsafe bool HashDataUsingPseudoHandle(string hashAlgorithmId, Rea else { Debug.Fail("Unknown hash algorithm."); - return false; + throw new CryptographicException(); } if (destination.Length < digestSizeInBytes) { Debug.Fail("Caller should have checked length."); - return false; + throw new CryptographicException(); } - NTSTATUS ntStatus; fixed (byte* pSrc = &MemoryMarshal.GetReference(source)) fixed (byte* pDest = &MemoryMarshal.GetReference(destination)) { - try - { - ntStatus = Interop.BCrypt.BCryptHash((uint)algHandle, pbSecret: null, cbSecret: 0, pSrc, source.Length, pDest, digestSizeInBytes); - } - catch (EntryPointNotFoundException) + NTSTATUS ntStatus = Interop.BCrypt.BCryptHash((uint)algHandle, pbSecret: null, cbSecret: 0, pSrc, source.Length, pDest, digestSizeInBytes); + + if (ntStatus != NTSTATUS.STATUS_SUCCESS) { - s_useCompatOneShot = true; // this OS doesn't support BCryptHash with pseudohandles - return false; + throw Interop.BCrypt.CreateCryptographicException(ntStatus); } } - if (ntStatus != NTSTATUS.STATUS_SUCCESS) - { - throw Interop.BCrypt.CreateCryptographicException(ntStatus); - } - hashSize = digestSizeInBytes; - return true; // success! } private static void HashUpdateAndFinish( @@ -147,6 +135,7 @@ private static void HashUpdateAndFinish( if (ntStatus != NTSTATUS.STATUS_SUCCESS) { + hHash.Dispose(); throw Interop.BCrypt.CreateCryptographicException(ntStatus); } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/OpenSslCipher.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/OpenSslCipher.cs index 6adbec36bb8f9..92eae5679d7b4 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/OpenSslCipher.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/OpenSslCipher.cs @@ -11,7 +11,7 @@ namespace Internal.Cryptography { - internal class OpenSslCipher : BasicSymmetricCipher + internal sealed class OpenSslCipher : BasicSymmetricCipher { private readonly bool _encrypting; private SafeEvpCipherCtxHandle _ctx; diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs index c0ab7eebc76fa..296c7bfdb3af6 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Android.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { - internal partial class Pbkdf2Implementation + internal static partial class Pbkdf2Implementation { public static unsafe void Fill( ReadOnlySpan password, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.OSX.cs index f588543899e28..4450166381e37 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.OSX.cs @@ -8,7 +8,7 @@ namespace Internal.Cryptography { - internal partial class Pbkdf2Implementation + internal static partial class Pbkdf2Implementation { public static unsafe void Fill( ReadOnlySpan password, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Unix.cs index a4a04b1a6887d..bace89794cafe 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Unix.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { - internal partial class Pbkdf2Implementation + internal static partial class Pbkdf2Implementation { public static unsafe void Fill( ReadOnlySpan password, @@ -18,7 +18,7 @@ public static unsafe void Fill( { Debug.Assert(!destination.IsEmpty); Debug.Assert(hashAlgorithmName.Name is not null); - IntPtr evpHashType = HashProviderDispenser.HashAlgorithmToEvp(hashAlgorithmName.Name); + IntPtr evpHashType = Interop.Crypto.HashAlgorithmToEvp(hashAlgorithmName.Name); int result = Interop.Crypto.Pbkdf2(password, salt, iterations, evpHashType, destination); const int Success = 1; diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Windows.cs index 93c0bd55ebb89..c72dcb4c93249 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/Pbkdf2Implementation.Windows.cs @@ -14,10 +14,8 @@ namespace Internal.Cryptography { - internal partial class Pbkdf2Implementation + internal static partial class Pbkdf2Implementation { - private static readonly bool s_usePseudoHandles = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 0); - // For Windows 7 we will use BCryptDeriveKeyPBKDF2. For Windows 8+ we will use BCryptKeyDerivation // since it has better performance. private static readonly bool s_useKeyDerivation = OperatingSystem.IsWindowsVersionAtLeast(8, 0, 0); @@ -115,7 +113,7 @@ private static unsafe void FillKeyDerivation( NTSTATUS generateKeyStatus; - if (s_usePseudoHandles) + if (Interop.BCrypt.PseudoHandlesSupported) { fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs index dad0dce87c8fc..1473dc7ca3bd1 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.OSX.cs @@ -5,7 +5,7 @@ namespace Internal.Cryptography { - internal partial class RC2Implementation + internal sealed partial class RC2Implementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs index 3e2a12794ceec..6e77674161c28 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Unix.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { - internal partial class RC2Implementation + internal sealed partial class RC2Implementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs index 0c6139a3f36de..5c038c325d503 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RC2Implementation.Windows.cs @@ -7,7 +7,7 @@ namespace Internal.Cryptography { - internal partial class RC2Implementation + internal sealed partial class RC2Implementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Browser.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Browser.cs index daedb5dcc503c..1c68004362dfb 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Browser.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Browser.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography { - internal partial class RandomNumberGeneratorImplementation + internal sealed partial class RandomNumberGeneratorImplementation { private static unsafe void GetBytes(byte* pbBuffer, int count) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.OSX.cs index f66da266b9223..3bb1cd89143e8 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.OSX.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography { - internal partial class RandomNumberGeneratorImplementation + internal sealed partial class RandomNumberGeneratorImplementation { private static unsafe void GetBytes(byte* pbBuffer, int count) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Unix.cs index 73aed5126ab8c..d8ef68a61648b 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Unix.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography { - internal partial class RandomNumberGeneratorImplementation + internal sealed partial class RandomNumberGeneratorImplementation { private static unsafe void GetBytes(byte* pbBuffer, int count) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Windows.cs index 87a7ca45c526c..876111a4ef45a 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RandomNumberGeneratorImplementation.Windows.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography { - internal partial class RandomNumberGeneratorImplementation + internal sealed partial class RandomNumberGeneratorImplementation { private static unsafe void GetBytes(byte* pbBuffer, int count) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs index c87500a56e5dd..dff66e5ab8b61 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/SHAHashProvider.Browser.cs @@ -85,7 +85,7 @@ private abstract class SHAManagedImplementationBase public abstract byte[] HashFinal(); } - private class SHA1ManagedImplementation : SHAManagedImplementationBase + private sealed class SHA1ManagedImplementation : SHAManagedImplementationBase { // It's ok to use a "non-secret purposes" hashing implementation here, as this is only // used in wasm scenarios, and as of the current release we don't make any security guarantees @@ -112,7 +112,7 @@ public override byte[] HashFinal() } // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha256managed.cs - private class SHA256ManagedImplementation : SHAManagedImplementationBase + private sealed class SHA256ManagedImplementation : SHAManagedImplementationBase { private byte[] _buffer; private long _count; // Number of bytes in the hashed message @@ -384,7 +384,7 @@ private static unsafe void SHA256Expand(uint* x) } // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha384managed.cs - private class SHA384ManagedImplementation : SHAManagedImplementationBase + private sealed class SHA384ManagedImplementation : SHAManagedImplementationBase { private byte[] _buffer; private ulong _count; // Number of bytes in the hashed message @@ -671,7 +671,7 @@ private static unsafe void SHA384Expand(ulong* x) } // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/sha512managed.cs - private class SHA512ManagedImplementation : SHAManagedImplementationBase + private sealed class SHA512ManagedImplementation : SHAManagedImplementationBase { private byte[] _buffer; private ulong _count; // Number of bytes in the hashed message @@ -959,7 +959,7 @@ private static unsafe void SHA512Expand(ulong* x) } // ported from https://github.com/microsoft/referencesource/blob/a48449cb48a9a693903668a71449ac719b76867c/mscorlib/system/security/cryptography/utils.cs - private class SHAUtils + private static class SHAUtils { // digits == number of DWORDs public static unsafe void DWORDFromBigEndian(uint* x, int digits, byte* block) diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs index 1284943c323c5..34d3dcf4978fb 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.OSX.cs @@ -5,7 +5,7 @@ namespace Internal.Cryptography { - internal partial class TripleDesImplementation + internal sealed partial class TripleDesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs index 2efa80141eb8a..2044b3f596351 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Unix.cs @@ -6,7 +6,7 @@ namespace Internal.Cryptography { - internal partial class TripleDesImplementation + internal sealed partial class TripleDesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs index 331162e7ea8f9..b6ea4fca90585 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.Windows.cs @@ -6,7 +6,7 @@ namespace Internal.Cryptography { - internal partial class TripleDesImplementation + internal sealed partial class TripleDesImplementation { private static ICryptoTransform CreateTransformCore( CipherMode cipherMode, diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 15af720d3ab45..2c54324bfb4d6 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -9,9 +9,7 @@ - - true + true true SR.SystemSecurityCryptographyAlgorithms_PlatformNotSupported ExcludeApiList.PNSE.Browser.txt @@ -454,6 +452,8 @@ Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Ecdh.cs" /> + + DSAFactory.SupportsFips186_3; [ConditionalTheory(nameof(SupportsKeyGeneration))] + [PlatformSpecific(~TestPlatforms.Android)] // Android only supports key sizes that are a multiple of 1024 [InlineData(512)] [InlineData(960)] + public static void CreateWithKeysize_SmallKeys(int keySizeInBits) + { + using (DSA dsa = DSA.Create(keySizeInBits)) + { + Assert.Equal(keySizeInBits, dsa.KeySize); + + DSAParameters parameters = dsa.ExportParameters(false); + Assert.Equal(keySizeInBits, parameters.Y.Length << 3); + Assert.Equal(keySizeInBits, dsa.KeySize); + } + } + + [ConditionalTheory(nameof(SupportsKeyGeneration))] [InlineData(1024)] public static void CreateWithKeysize(int keySizeInBits) { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs index cd74668de7c1e..b1bcea4272808 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs @@ -78,7 +78,7 @@ public void Array_SignData_VerifyData_UsesHashDataAndSignHashAndVerifyHash() AssertExtensions.Throws("hashAlgorithm", () => ecdsa.VerifyData(new byte[1], 0, 1, new byte[1], new HashAlgorithmName(""))); var input = new byte[1024]; - new Random().NextBytes(input); + Random.Shared.NextBytes(input); byte[] result = ecdsa.SignData(input, HashAlgorithmName.SHA256); Assert.NotNull(result); @@ -104,7 +104,7 @@ public void Stream_SignData_VerifyData_UsesHashDataAndSignHashAndVerifyHash() AssertExtensions.Throws("hashAlgorithm", () => ecdsa.VerifyData(new MemoryStream(new byte[1]), new byte[1], new HashAlgorithmName(""))); var input = new byte[1024]; - new Random().NextBytes(input); + Random.Shared.NextBytes(input); byte[] result = ecdsa.SignData(new MemoryStream(input), HashAlgorithmName.SHA256); Assert.NotNull(result); @@ -126,7 +126,7 @@ public void Span_TrySignData_VerifyData_UsesTryHashDataAndTrySignHashAndTryVerif AssertExtensions.Throws("hashAlgorithm", () => ecdsa.VerifyData((ReadOnlySpan)new byte[1], new byte[1], new HashAlgorithmName(null))); var input = new byte[1024]; - new Random().NextBytes(input); + Random.Shared.NextBytes(input); byte[] output = new byte[1]; int outputLength; diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs index 12f2fd03d5fff..b0882e7b23486 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898OneShotTests.cs @@ -11,6 +11,7 @@ namespace System.Security.Cryptography.DeriveBytesTests [SkipOnMono("Not supported on Browser", TestPlatforms.Browser)] public static class Rfc2898OneShotTests { + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Password for testing.")] private const string Password = "tired"; private static readonly byte[] s_passwordBytes = Encoding.UTF8.GetBytes(Password); diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs index c8fabd02d466a..497411d6981c4 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/Rfc2898Tests.cs @@ -551,47 +551,44 @@ private static IEnumerable GetKnownValuesTestCases() { CaseName = "SHA256 alternate", HashAlgorithmName = "SHA256", - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy credentials.")] - Password = "abcdefghij", + Password = "PLACEHOLDER", Salt = ascii.GetBytes("abcdefghij"), IterationCount = 1, AnswerHex = ( // T-Block 1 - "9545B9CCBF915299F09BC4E8922B34B042F32689C072539FAEA739FCA4E782" + + "9352784113E5E6DC21FC82ADA3A321D64962F760DF6EAA8E46CEEF4FAF6C6E" + // T-Block 2 - "27B792394D6C13DB121CD16683CD738CB1717C69B34EF2B29E32306D24FCDF"), + "EE6DB97E5852FC4C15FA7C52FACDEDE89B916BCC864028084A2CF0889F7F76"), }; yield return new KnownValuesTestCase { CaseName = "SHA384 alternate", HashAlgorithmName = "SHA384", - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy credentials.")] - Password = "abcdefghij", + Password = "PLACEHOLDER", Salt = ascii.GetBytes("abcdefghij"), IterationCount = 1, AnswerHex = ( // T-Block 1 - "BB8CCC844224775A66E038E59B74B232232AE27C4BF9625BBF3E50317EDD9217BE7B7E07AA5697AF7D2617" + + "B9A10C6C82F36482D76C0C38C982C05F8BB21211ACBE1D1104B4F647DDEAEE179B92ACB0E00A304B791FD0" + // T-Block 2 - "AC02F63AA2B0EC9697B1801E70BD10A6B58CE5DE83DD18F4FFD2E8D9289716510AA0A170EF1D145F4B3247"), + "3C6A08364D0A47CD1F15E0E314800FF3AC9CF2E93B3F81A5EB67FE9F2FE6E86B0430B59902CCB5FD190E67"), }; yield return new KnownValuesTestCase { CaseName = "SHA512 alternate", HashAlgorithmName = "SHA512", - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy credentials.")] - Password = "abcdefghij", + Password = "PLACEHOLDER", Salt = ascii.GetBytes("abcdefghij"), IterationCount = 1, AnswerHex = ( // T-Block 1 - "9D6E96B14A53207C759DBB456B2F038170AF03389096E6EEB2161B3868D3E5" + - "1265A25EF7D7433BF8718DB14F934B6054ACCEA283528AD11A669C7C85196F" + + "AD8CE08CFA8F932CF9FEDDCDB6E4BC6417D61F0465D408C0BFE9656E2C1C47" + + "1424537ADB2D9EBE4E4232F474EFEE2AF347F21A804F64CBC05474A6DCE0A5" + // T-Block 2 - "B5DFAA2185446D6218EBC2D4030A83A4353B302E698C8521B6B69F7D5612EF" + - "AF060798DF40183FE6B71F2D35C60FBE27DFE963EFEE52A5756323BA1A41F6"), + "078100F813C1F8388EC233C1397D5E18C6509B5483141EF836C15A34D6DC67" + + "A3C46A45798A2839CFD239749219E9F2EDAD3249EC8221AFB17C0028A4A0A5"), }; } diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj index 4995d6690f944..77ce3aa220dfc 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/System.Security.Cryptography.Algorithms.Tests.csproj @@ -1,13 +1,8 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-Browser - - true + true true - - true CreateTransformTestData() + { + yield return new object[] { typeof(AesCryptoServiceProvider), null }; + yield return new object[] { typeof(DESCryptoServiceProvider), 9 }; + yield return new object[] { typeof(DESCryptoServiceProvider), 13 }; + + if (RC2Factory.IsSupported) + { + yield return new object[] { typeof(RC2CryptoServiceProvider), 9 }; + yield return new object[] { typeof(RC2CryptoServiceProvider), 17 }; + } + + yield return new object[] { typeof(TripleDESCryptoServiceProvider), 9 }; + yield return new object[] { typeof(TripleDESCryptoServiceProvider), 24 }; + yield return new object[] { typeof(TripleDESCryptoServiceProvider), 31 }; + } + [Theory] - [InlineData(typeof(AesCryptoServiceProvider), null)] - [InlineData(typeof(DESCryptoServiceProvider), 9)] - [InlineData(typeof(DESCryptoServiceProvider), 13)] - [InlineData(typeof(RC2CryptoServiceProvider), 9)] - [InlineData(typeof(RC2CryptoServiceProvider), 17)] - [InlineData(typeof(TripleDESCryptoServiceProvider), 9)] - [InlineData(typeof(TripleDESCryptoServiceProvider), 24)] - [InlineData(typeof(TripleDESCryptoServiceProvider), 31)] + [MemberData(nameof(CreateTransformTestData))] public static void CreateTransform_IVTooBig(Type t, int? ivSizeBytes) { using (SymmetricAlgorithm alg = (SymmetricAlgorithm)Activator.CreateInstance(t)) diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.Managed.cs b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.Managed.cs index a84762d7a765f..0275ab5546bab 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.Managed.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.Managed.cs @@ -14,7 +14,7 @@ internal abstract partial class AsnFormatter private static readonly AsnFormatter s_instance = new ManagedAsnFormatter(); } - internal class ManagedAsnFormatter : AsnFormatter + internal sealed class ManagedAsnFormatter : AsnFormatter { protected override string? FormatNative(Oid? oid, byte[] rawData, bool multiLine) { diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.cs b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.cs index 7a72349a042c7..6f200148ca935 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.cs @@ -99,7 +99,7 @@ internal static partial class OidLookup } /// Expected size of . - private const int FriendlyNameToOidCount = 110; + private const int FriendlyNameToOidCount = 111; /// Expected size of . private const int OidToFriendlyNameCount = 103; @@ -238,7 +238,7 @@ static void AddEntry(string oid, string primaryFriendlyName, string[]? additiona AddEntry("1.2.840.113549.1.1.1", "RSA"); AddEntry("1.2.840.113549.1.1.7", "RSAES_OAEP"); AddEntry("1.2.840.113549.1.1.10", "RSASSA-PSS"); - AddEntry("2.5.4.8", "S"); + AddEntry("2.5.4.8", "S", new[] { "ST" }); AddEntry("1.3.132.0.9", "secP160k1"); AddEntry("1.3.132.0.8", "secP160r1"); AddEntry("1.3.132.0.30", "secP160r2"); diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj b/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj index 22f3d55350c20..ea5329594dcfb 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj @@ -8,9 +8,7 @@ SR.SystemSecurityCryptographyEncoding_PlatformNotSupported - - true + true true diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs index 02ec934c51feb..0dc17c595d8ae 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/Base64Transforms.cs @@ -372,7 +372,7 @@ protected virtual void Dispose(bool disposing) } } - internal class ThrowHelper + internal static class ThrowHelper { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ValidateTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount) diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/Directory.Build.props b/src/libraries/System.Security.Cryptography.OpenSsl/Directory.Build.props index 05c132a29c6b6..d3efdf2d11994 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/Directory.Build.props +++ b/src/libraries/System.Security.Cryptography.OpenSsl/Directory.Build.props @@ -3,10 +3,10 @@ Microsoft true - windows;browser + windows;browser;android;ios;tvos Provides cryptographic algorithm implementations and key management for non-Windows systems with OpenSSL. Commonly Used Types: System.Security.Cryptography.RSAOpenSsl - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj index 3c55e5864f978..1f7b3c1415cf3 100644 --- a/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj +++ b/src/libraries/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj @@ -49,6 +49,8 @@ Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EcKey.cs" /> + c encryptedContent.CopyTo(encryptedContentArray); using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm)) - using (ICryptoTransform decryptor = alg.CreateDecryptor(cek, alg.IV)) { - // If we extend this library to accept additional algorithm providers - // then a different array pool needs to be used. - Debug.Assert(alg.GetType().Assembly == typeof(Aes).Assembly); - - return decryptor.OneShot( - encryptedContentArray, - 0, - encryptedContentLength); + ICryptoTransform decryptor; + + try + { + decryptor = alg.CreateDecryptor(cek, alg.IV); + } + catch (ArgumentException ae) + { + // Decrypting or deriving the symmetric key with the wrong key may still succeed + // but produce a symmetric key that is not the correct length. + throw new CryptographicException(SR.Cryptography_Cms_InvalidSymmetricKey, ae); + } + + using (decryptor) + { + // If we extend this library to accept additional algorithm providers + // then a different array pool needs to be used. + Debug.Assert(alg.GetType().Assembly == typeof(Aes).Assembly); + + return decryptor.OneShot( + encryptedContentArray, + 0, + encryptedContentLength); + } } } catch (CryptographicException e) diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx index 6fae1dbed6331..8086b46b90c69 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/Resources/Strings.resx @@ -295,4 +295,7 @@ Certificate already present in the collection. + + The key in the enveloped message is not valid or could not be decoded. + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs index 40b0b6e410335..c2c51f6dc3ef8 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.DSA.cs @@ -20,7 +20,7 @@ static partial void PrepareRegistrationDsa(Dictionary look lookup.Add(Oids.Dsa, new DSACmsSignature(null, default)); } - private class DSACmsSignature : CmsSignature + private sealed class DSACmsSignature : CmsSignature { private readonly HashAlgorithmName _expectedDigest; private readonly string? _signatureAlgorithm; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs index 9a4a92a65a6b4..cac04b086150e 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.ECDsa.cs @@ -20,7 +20,7 @@ static partial void PrepareRegistrationECDsa(Dictionary lo lookup.Add(Oids.EcPublicKey, new ECDsaCmsSignature(null, default)); } - private partial class ECDsaCmsSignature : CmsSignature + private sealed partial class ECDsaCmsSignature : CmsSignature { private readonly HashAlgorithmName _expectedDigest; private readonly string? _signatureAlgorithm; diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs index cc3b8ef696349..7873de050dd81 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSignature.RSA.cs @@ -195,7 +195,7 @@ protected override bool Sign( } } - private class RSAPssCmsSignature : RSACmsSignature + private sealed class RSAPssCmsSignature : RSACmsSignature { public RSAPssCmsSignature() : base(null, null) { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs index d3d1cc650e85d..fbe270792bc8c 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs @@ -258,6 +258,7 @@ internal SignerInfoAsn Sign( X509Chain chain = new X509Chain(); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; + chain.ChainPolicy.VerificationTime = Certificate!.NotBefore; if (!chain.Build(Certificate!)) { @@ -265,7 +266,18 @@ internal SignerInfoAsn Sign( { if (status.Status == X509ChainStatusFlags.PartialChain) { - throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); + if (chain.ChainElements.Count == 0) + { + // On Android, we will fail with PartialChain to build a cert chain + // even if the failure is an untrusted root cert since the underlying platform + // does not provide a way to distinguish the failure. + // In that case, just use the provided cert. + certs.Add(Certificate!); + } + else + { + throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain); + } } } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Certificates.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Certificates.cs index 1c1ca3e58f3c8..028defaf5eafd 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Certificates.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Certificates.cs @@ -488,7 +488,7 @@ private static class RawData + "3188DB9364AAD52D4E28169CC898B621FF84").HexToByteArray(); // password = "1111" - public static byte[] s_RSAKeyTransferPfx_ExplicitSki = + private static byte[] s_RSAKeyTransferPfx_ExplicitSki_RC2ContentEncryption = ("308209810201033082094706092A864886F70D010701A08209380482093430820930308203E706092A864886F70D0107" + "06A08203D8308203D4020100308203CD06092A864886F70D010701301C060A2A864886F70D010C0106300E0408101C5A" + "3E2DBE2A9102020800808203A0F58476F5E4741F8834F50ED49D1A3A5B2FC8C345B54255C30556B1426C1BA1D9EE4440" @@ -541,6 +541,65 @@ private static class RawData + "092A864886F70D01091531160414080FB9AAB81BD67FD85C2186B359054CEB13D2D730313021300906052B0E03021A05" + "0004142C205F0B1E9B99B0ED14E83F13D84BC683F66D3B04080D22E45D6A657CC602020800").HexToByteArray(); + public static byte[] s_RSAKeyTransferPfx_ExplicitSki_TripleDESContentEncryption = + ( "308209930201033082094F06092A864886F70D010701A08209400482093C30820938308203EF06092A864886F70D0107" + + "06A08203E0308203DC020100308203D506092A864886F70D0107013024060A2A864886F70D010C0103301604103C01F0" + + "817F2D7818D335FD48397D829C020207D0808203A004485AD45419862A564E9C8161C9A6BCB94EC72C3D18CB01C499D4" + + "F157C055EF92E915B85A15AD0F175DF99D8B3B0516AEB95D774FABFBAA84EB8B2331FE920D139BA5AB4CE23CE7F04EB3" + + "493F390B977EBF502AE81703C28CF9EEDD7247A8B930AFCB866FEFD08683270F75905D6FD456B4C6F87544F89329A5C6" + + "16991E60CBA5EC8287FB69A2BC85564CC5739D9416D20BEAE1238FA9FDBF1DC552AADD74DE777AA6D1C096D61F3354CC" + + "B920825812A4F4E886C4CDAA86DC4C3E77797240D66ACB4F4705743CB05E56FEEA1911A896AD1AD4A9A451F217CF2634" + + "1950C165577FB6E0200DB77A84679266D957B60E9B77F5690B44988C0D3087C945FE1A2FC0A8E5E7C186E72F670E98F6" + + "911F76F6FBF076CFC1149562AF67D132FDEE8F207DF89BDCFF79C5D3EA0D2B710071C47C36F6FFED6874BB2013B78207" + + "1F331F411D894BD2E140087AD293514C272145F9E51D0E8B390920BF89984C5567D5A2BAB726CE60DDB2F1E73F2276DB" + + "3ED142612C76717C3A97DD63B160A6560AF4EC65D6F8FDBCCF9ED03CD006570354C19CBC70BBA9896A15A02B6FBCB215" + + "FE771DCDA5BB4CDC4E86732776527C3A1AA01EE86D0C2B7BC525D19BE430270DD1AE7E0ED02174FF27974AB9BF9EBA91" + + "3437B743B7B6E7BAF80CE9D8048136F564B03813ABDF69291DFC8D215E85C0123A6E0DE0CA6202038ABB0F650ED7FE48" + + "CA366408DEB344FD81A6E1ED1DB6D4AAC97498D957734632C6F317076703EB2EDBB94B1F9D1D3049ED95BFE4AEF89CB8" + + "C51D73B5833DA7E2A9CA365B5D3F8BD2F8014633BB1B112FF42D96D0B9F5C002BBAA499509086E37A54F0875BC32CC61" + + "EA61650E5A251214BE6E9249DD1DA4B64EAAD9B389D8D7B5A3D9887A2A30F147AA931D0DEDA93D7D5F19B84AD2259D00" + + "7692FDCB6963FBD8F5897844A5C154B81C410EEA5B2DE770AF4335966B5B33354AA5C4C7C124C002B134EFFAC308B92F" + + "8034618213D3DEB805E6297A6EEB1AE16A27C55B01AAA68AB25823484B41C75B607F9F056484E8FA1C2D5AADB0BB4BEF" + + "3732E447CD510FA4813395D1147BDBFFBE33A1B9AA3A466A825DE6A8D0D0DC1B869553766039EC6FE366711B2BFF407B" + + "9C72A0939CB6D7CDE61CE7AC50CCE24A2B1796517FA4F0B0CE43B226F12BEC1837A7F0F4C467393A057BC9A2AE530349" + + "54DC10CE645D7EBC6E747F5DBC6695C49AFDFDC3C41B7236A8017BA16027E46A1EF3BC93C232DC8C19E3A8BCD71754AF" + + "A56AF755C0CFE8BC50ABCECD3F7ADDDE9B741614DF86A4C4BE9F297663CAE52C1922D41C153082054106092A864886F7" + + "0D010701A08205320482052E3082052A30820526060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A8648" + + "86F70D010C0103300E0408482E476C7712FD7202020800048204C8AF5357D64578320D963917F44B2B7714334AAE6571" + + "554F03A5999132754BA03892316159385225C4EEA7C958091BC8A32A9433AECA3A07F791ACE431217F0DFBD53DC15D25" + + "7069B99DA04EF719892004FD307E151EBB359C1D69AE8FF78A1CC1654470B0862CDAC1AED521608C29AA8D30E81A4015" + + "D54F75421B9BDB295036D79E4535F28D4A2ABF4F203BC67065A709AEF6EAF4D6A3DC7387CB3459D82399F4797CE53FD2" + + "BD0A19B1A9566F74246D6B6C50BD2777B6F6DE1A8C469F69D7EBF230018D56DF4C1764862CD982E81F56A543DA4ADB63" + + "EF8612A1BB56147156035541B0B41F06BBE2CD47DC402A75023558205870060438CF99D8BFC7CAADDE0583311FE4B854" + + "051C83638420BC5E93F999E67EDBBC266F519609E2BE9FC1BC3C7FEE54DBAB04DAC8A94BB347F3DC28DDAB7D28DD3BBF" + + "FB44C84E6F23A8E01CAB36382723DB94CD8973A303D8D4C7A22B9F811C07ED9A78E06135E0426FC93BB408F1DC915DF4" + + "ADBF048D22C201D80FDC0EF942D1E2AC0F39F8A95BA849C07BB0DA165B3F0317478870F704B8A34B7D5816BC4F8CA0C6" + + "BDB5346F2211416C79D7117AD1B86E44E0BC0C3793F9895638E5B7A2A5B957E0E691819AC7FA1F05E8D05ED99E4E286C" + + "96E3E31DF99633E8CB73CA135109AE727CB047641726A1599492B6F3E8E62195A79239279B2A9FBF47B31FEFF3C20DEC" + + "2DFBDB0CE98B183DBA773561DEE404BA1A5BEF5AB9729DBE22FB1C17EFD4D3AC81F04F49F9855CEACECB202090A1290C" + + "10E9D676F0658F3DE4C43DCD5A17B88881893DA87060C5F56D5CC9A92E6B1A47A6D16FB32C09925606F6D5C7CAFBC7A8" + + "2D8E441A05DFBEE0BEC92D89264B62D5DECC342D29D9A7727BBDE4E63EEB7CAED7C76953F6AC8CB570619C7607B753FD" + + "46889C76D29C9AC66F56CB3848323FA9CD16578EA5C6D876AE63D95F65E2CDEF68A1CF3D2FC3DF91D0055B0CDBD1510E" + + "291C0E7AC6EAA0D2AB5E8FAD44108C94A592251447926DB7139BC2A433D61711C6DA5EF82A8E18CEBF99AF753E33FFF6" + + "5126B7D3D3D09FF0C50EFF7822FA8797BAC52383B94E8FE602E62577994ACA6A2150F60A31CA0E79FE6DF3405D6918EA" + + "DC2231558FB29045034EB9DA9FB87BD71996C6AB6EA71A70EBFBC99BC292338A363176516C14EC92E174C59C6BE82F5B" + + "C0296525109C9A7FC9D9E654955992A5C9EDFD39ED9889BEAF105B2EF62B041789F20A6AB26563FCFA1A1482EE2A20E8" + + "C1A2F0931ACBA7F8756EE4C9119D29817ACA7D2B81FE736FD7A33D20EC333AC5123D29345647B734DB24B5C56B4576AB" + + "BF9B02F782DDE0B4BA277080F28F3D86DEC35F0F19B2B5DB0BD7A59B7C4B2BAE08E8584449BD3685F371F6A24F5F91EA" + + "6843DC6ABA89976E589511EB23A733D23F6CE076C952E9E7190ED78D5A34387F93418A87CB02270941F19DD35E1DB583" + + "6E67296A7F28A5EB8F32DA73EA7A47D5CEB292E767468CDF938A44B3CEEE6276A34705606A8F7496D7310DA1A0468A60" + + "4B8A9E7AB50450A6DFE0C4540CEA058CD7919E823D8A32FB811C6BF8754C65D7A9723018ADE95AED5C30978A8DBA185C" + + "F0BA36346456CD3E15C511FAD71B445DDFA7C5455A3597FE536E3BB87518C0725D6BE673D05DC5E74B4FF442711D242A" + + "37D0CCB88E6D19BD6B7299207D7036EB87D5E86189768CB14AE4F8886BB5AB7864BDA9757D0C26BFFF3FAA4001258557" + + "D394313125302306092A864886F70D01091531160414080FB9AAB81BD67FD85C2186B359054CEB13D2D7303B301F3007" + + "06052B0E03021A04145418AC4FCE987C52004DA05A858BFEA94B21D4860414A26BFC44B061FE0F7E01ED25D963641D1C" + + "057847020207D0").HexToByteArray(); + + public static readonly byte[] s_RSAKeyTransferPfx_ExplicitSki = + PlatformSupport.IsRC2Supported + ? s_RSAKeyTransferPfx_ExplicitSki_RC2ContentEncryption + : s_RSAKeyTransferPfx_ExplicitSki_TripleDESContentEncryption; + public static byte[] s_Rsa2048SignatureOnlyCer = ( "3082032C30820214A003020102020900E0D8AB6819D7306E300D06092A864886" + "F70D01010B05003038313630340603550403132D54776F2074686F7573616E64" + @@ -569,7 +628,7 @@ private static class RawData "AE96ED1C56D220D1FD5DD3B1B4FB2AA0E04FC94F7E3C7D476F29896224556395" + "3AD7225EDCEAC8B8509E49292E62D8BF").HexToByteArray(); - public static byte[] s_Rsa2048SignatureOnlyPfx = ( + private static byte[] s_Rsa2048SignatureOnlyPfx_RC2ContentEncryption = ( "308209E3020103308209A306092A864886F70D010701A0820994048209903082" + "098C308205BD06092A864886F70D010701A08205AE048205AA308205A6308205" + "A2060B2A864886F70D010C0A0102A08204FE308204FA301C060A2A864886F70D" + @@ -651,7 +710,94 @@ private static class RawData "19C6933240012EFEB95F734C648CCD13460414FA1743400686D25BA1CB28D736" + "F2B1ED97699EA4").HexToByteArray(); - public static byte[] s_dsa1024Pfx = ( + private static readonly byte[] s_Rsa2048SignatureOnlyPfx_TripleDESContentEncryption = ( + "308209EF020103308209AB06092A864886F70D010701A082099C048209983082" + + "0994308205BD06092A864886F70D010701A08205AE048205AA308205A6308205" + + "A2060B2A864886F70D010C0A0102A08204FE308204FA301C060A2A864886F70D" + + "010C0103300E04083EF905F0EA26FBF7020207D0048204D82297B5546DA6CC49" + + "BD8C1444E3FE1845A2C9E9BDB8B83E78235DF4ADF7A97496A62D31D4EEEB76B0" + + "71C0B183ACC3663272F88CF4F31E2E00D76357C0A051B8D6E0BB0BCF4CCDD064" + + "CCBAF546EABA80DA56CD11FE952C61154792559D65F26B0476CF7A5FDB8CC794" + + "B89F6ACD50003459054FE82C48D8791B226A0EEEC01F048AC3CE716C9F3BB313" + + "D64BEBBF0037D83133DD9C15D04F15BB11652D793B613A68AFE580245724E5D1" + + "110040B332B5C39BE04086BA4DFC58E905BC2FE8B3C696181E2879AF197EE24D" + + "91D8AD67013F14C4864C8D0FB19C134B766CF3E48B8C9E363A11EB19F1E82E74" + + "D25EDD96517D64A94314B40C11651030D561E742E63856E8D1A3EE9FDFD6CF64" + + "7140CFC354AE7EA1D14C157C2985F82D54296F7D3DE456AF7513F5F30A0421E4" + + "3A9DAD6DF8A2BF69005B35CA8066F80755D848DA73EF03BC0CC129C5799911D9" + + "3A1ED43F8E76732AF56FD62DC6D0B0DBA6AAC6DCDE77D0E8AC9F6A5EB5A02B61" + + "BC477706D4F1873240AB45E0291EF21E6034D48F1AE8EB139DE7ACD8B8A821E6" + + "70B395C3EC4B0E75C34BF0067F052FCED835CAC1F17C3FBEA2FC9FD281FCDE21" + + "D5B27CF31A07E90164A979ACEF0E1C67DBA6C33082E9B189D4BDA2D2D504776F" + + "455843437BDF10D4AF48639EC03344BCC36EFEA7CDE08D7F94DDEAF98BCB5D65" + + "207AE0C349EECE3032DE19F3B337F29A3457AA0AAF4306FA8301619AB01B7235" + + "BE16CB93728E142DAA6C1CBBCC5BD82D913596994DA40FB916CF2DB5FBCC20CF" + + "E893DC62BBC5FC59E00BC0A704A9DB25BBF9291971F2377FC1A20F2C954869DB" + + "6FFCC90C625AABE97ED4CF7C0209D39AD780003C437152D636ACB3B484C46885" + + "DC1584733D2153A3F9B968F12CDD5937CDF9DD2581D72EE67C83AE2530197AA7" + + "C6110613BEFF0B75E586C28394EA8EBCF7F9DB133295B33DC86C8DBA92BF8BD1" + + "ADCAF8D2CD2E018B08D59FF1C30A13484AB11468F7DCEB1FE53A6DAF309B0510" + + "7772CB735314A5B2F053E60A653F0496BCB9CADF5E50339A4D2EF2382056B768" + + "558EB9230D996C636E6D29664F92F70A088DE3EE4EC4BBD8A9C4C98C7892D122" + + "28806622B87E581A321AD835B8F4B964A17B5BE6D9DA50133D494732A41884E2" + + "9E891FE2D40ACCFD585C8BF626C1E8A412D2EE7CDE060E2CCDA826BF79D80F1B" + + "F6B8400473BCE0C19D03ACF55D1FAA994C04A8CD11D49743B1F45F48DFFDD701" + + "18B5FA82ECDF67714F5DE5D3D3DDDCB76ED0EA6A6E151665A4AA351DB1A99F8C" + + "7502D3795C2C358CCA589C390C1F4810615130B91BA42A85E77FA37197E1B083" + + "FE1246B067C6444D49B369D45B130A6D7B463C3F0459EB41D68009CABD2F5C60" + + "49DB706FA742C9773FB5791AF123FBE485E05F87759ADD25281BE337B6095EC3" + + "4EFF9FC692798FB4217EF4B2B59902D930F28181933FAA278C041123CAE3CA63" + + "6DFD3AD4E04EB751A30D50C26288EA4D01C7B323E4FD6387F88E020BC433BF60" + + "C4406398C44EA5C7A6EB24134B0811E4F94DFAF5553172306FA5543C254E7E04" + + "DEEC84DBF9FAF7BFEA8D61E094CBB18DD45C5BAB9199DD719F9A305E205605CC" + + "671DCD566FEBA2C8F4C1A445625C4F42D1CFE32087F095591798D1D48DA46DE9" + + "230F5102B56A1EF879D48936D5331D6B3D9F1B564CF08FD3C641CFF3B02CB4FC" + + "8995E5EC5DD1D183704940C02DEA7430FD594E54800DCC74B7732731C63FBBA2" + + "A2F6DC031174390A74781D352B09FB4F318190301306092A864886F70D010915" + + "3106040401000000307906092B0601040182371101316C1E6A004D0069006300" + + "72006F0073006F0066007400200045006E00680061006E006300650064002000" + + "520053004100200061006E006400200041004500530020004300720079007000" + + "74006F0067007200610070006800690063002000500072006F00760069006400" + + "650072308203CF06092A864886F70D010706A08203C0308203BC020100308203" + + "B506092A864886F70D0107013024060A2A864886F70D010C010330160410FA37" + + "DF4A395E316613255C1FCC424EE7020207D080820380B6A6EF2B8047F4541047" + + "384B943C35C28E4097BF204745FF0A112E9B00DB2AF79938B994483B308412D9" + + "F69E82027A3B35AB71540597565C4C5D20D62891AD495B49B4CD4C9A13D5ADFB" + + "77870EB32893C7F48758F7C7580BD02C658949874B6EF41A18C419A9D31FFDAC" + + "2FADE69BB6D17EBADAFF84AE420DFCE67124D7DEA9576711D5C340D2B1C122F2" + + "0031F46D57F8D3A7A44438429BFCFCBBA36A135DB1FA42B40491EF3E63168F7E" + + "992F4EBA4AA687AC5ECDC6AC4E8C1B8976E1868394AA9D703EB6D4227BA603EE" + + "C7CAE369BBD26934AF9F31D15DB04EACA13EC71D103F44669A965AE74060B33C" + + "67CBEAF974BEA1B74EBB4123AB1BA961CCE5B744FE1C97897BDB0D724317C7A5" + + "A1C8C41BCF6E10DD65C118D1EB0504EA17D0A9937854CC3C27295A33DA035B7C" + + "BA5F92156BD62347F7BB33CA9631E869A3987F60E9BE4231E7D069FE0406DA70" + + "C54ED86199C67097F30D44E36DD5CD5C379368456F71BC45BC5E75DCADAC3164" + + "068F005ED7E6E83F9F13532F555A1F5131DA6BA1BCE375B3FBEF681C4B60A9F1" + + "C79FEFB1742103117F5ABADF4FF93FDB697E13C5DB93288FE9D7DF45BB7E7BC9" + + "7EC85931791A6E889A4DC34599C985F39572785C4C8C31EBB2A9A32B093D7BE7" + + "0AC1B1C1F04B099865CB54E01F0D6F990EF598333E9D19BBEB42F0AFAA650D1B" + + "9E00902819BB68203BC5768315757F3417E7C4E744498FBBD2376D07D6BF06F7" + + "9E1F2AC7013F609A68987EBCE1CA891B8D8E9A5C4F391F441FA2CED912CA2D2B" + + "A5478E885F1AD7056962F078BCAF1FEEAF663B06192A0F185541DA4EF7887E34" + + "5A847A857102F84C45845FEDB5288B06D9C4DDC78FE94C691A206EB65CDB357C" + + "A680EE919CED5A472A5BACB48D13C158A542B4B64376A13E44EA99629DC7B6CB" + + "5F4293BFA9FEB3101E28E11E56FD50FC380CB29F79E5753793B75B4E0B582148" + + "C0E1B7A31175DE703E4AE6AD1D7B35AAA075FA30CC1909EE443D1BA610AB7FC3" + + "8A0810AA5EDB9DFAC3972A0B0A4D07D2562DB4C48B9D04AE93A43AFF8D94C9BE" + + "FBD5FDE36E4A3F0C643EDEB2C0F3C2ADFAAEEC627718BB6022C99D9C6FAF7345" + + "CEA3E01D032125CA0A14E7B8101E4061692CCC7650F08DCEB9440EB0E4E9293D" + + "386843A4F4CD761C82835658E688C9CDFB46A71629E58CFF7639B3F537BFC588" + + "B38ABE5EE0124A74FE4FB166B9ABDF013181CCD696FF190440055A2EFE98E368" + + "14F8E01DBBC97DDC00D5A2C79DFD4ACC9FA206C035FA303B301F300706052B0E" + + "03021A0414EA08415D254A79B627FFF47D0D844454A24BBC2B041476D3D168D6" + + "31F22156DA4116AD098C466C4680D1020207D0").HexToByteArray(); + + public static readonly byte[] s_Rsa2048SignatureOnlyPfx = + PlatformSupport.IsRC2Supported + ? s_Rsa2048SignatureOnlyPfx_RC2ContentEncryption + : s_Rsa2048SignatureOnlyPfx_TripleDESContentEncryption; + + private static byte[] s_dsa1024Pfx_RC2ContentEncryption = ( "308206EE020103308206B406092A864886F70D010701A08206A5048206A13082" + "069D3082043706092A864886F70D010706A0820428308204240201003082041D" + "06092A864886F70D010701301C060A2A864886F70D010C0106300E04084AF212" + @@ -709,6 +855,70 @@ private static class RawData "313021300906052B0E03021A0500041466FD3518CEBBD69877BA663C9E8D7092" + "8E8A98F30408DFB5AE610308BCF802020800").HexToByteArray(); + private static byte[] s_dsa1024Pfx_TripleDESContentEncryption = ( + "30820700020103308206BC06092A864886F70D010701A08206AD048206A93082" + + "06A53082043F06092A864886F70D010706A08204303082042C02010030820425" + + "06092A864886F70D0107013024060A2A864886F70D010C010330160410B7C75A" + + "92EFD0F0CFA7B422AC87BBA3B2020207D0808203F079F1EACA1E60CBB110E12A" + + "88FFCC2A888F4DEA4737692DA84C1040DD10F3FAC5EAC6B626633C2A690DBBFA" + + "57BC01171653584EE6E9B47F575476C45BA73C2530E0104933E7FD1C3B6E9115" + + "E2749B3A8A167D0F61BFDF2D4D405AE86AAB9693707583BFCCAD6DA60768EA4F" + + "34B5220E5A78D01ACAA99351D1D5025BBAE623A0AEA002032FF701BE35830CB1" + + "362D68CF7E470CF281697582ADBA32FFBAD5DAD3671105C2754ACCCF2A9839C9" + + "08E6D6A1D3DF39510517E0C6C89C709E0F77DA1228BC4E9DF10535A62CC0B5DD" + + "93E17D56533C2B5CD8EBD04EDC0DC7EAB89F5B1AD0056AAE9F7D0DE5C0F4A613" + + "9BEB7C2CC2E5051BF6F0E68B8F347A1221E6D6C0DABE50164FE9608FB41C0EA1" + + "01BB441DF3C9421869F4F8218ED93FFABC88FDD2E67922A0DF04CEDA1BBC286A" + + "82DCA5C12A494384F0C8900E06CC6931397ED6BF4C8A763759B7C9D50F42835E" + + "B61C6C1E029B8CDB59CFF27CB95D4850C6F4C89F5A57E716BA8723FB9D4302A3" + + "F9CBA8C4E995971D70ED7F5598F1006695DCBC6F0E259845BFD0BD5C480FD9F9" + + "2144E8EDCE881CBBE553B568928D934BCB0EFFAC5619398CE924BB52E1AF3D83" + + "249E345BEE05AABCD66F7C28D64BF32AFE7A1F028AFE9B256BBAA3BF67BDC91F" + + "2C5DE56790B2949155FCC5A4BFEB670549D182F06E1A328B56BAF23C85CF0F16" + + "A23F6B596F86F5415B20E6E14B5A532585C78D9F23ED08343A7C9D21F495ADD9" + + "C82DF7F22B1D9081F552010B2D1AEB9F690723F38BF53031C89BA4D4691437F7" + + "D9B0CC002A551B69F37B086B4EEC3330B19563FE0832DF5A8751646D208C1ABA" + + "7102E8161D6B473EA0C8A8BE5119FF46F6B3F78CF35A9DFE325AC27169C1BFAE" + + "AD6D8E21442C0EA61EC559A28B3B113550E8D84F12E398CC343050C5A6B99A0E" + + "BB801BBD226EA132DBEC3EB5525A4D1555C367443A1DCF96FB4D01AFAC5F1C02" + + "DE7394A2C80630614E81AFEEB62051DB79AA1FFAA773C58800A90A46F5784E3D" + + "D85C77BD74606E2FC2229EB671E09A88879E69488C630D8D510405CA189D6180" + + "DA5B29EBE3B289546433C8A64192685C55510A78F02EBAB861406BC966B7107D" + + "4F13A8EEFBDAA050AC1887781663A7B59354D52E61AAD77EE314E2DA62B9319F" + + "6F02FFA32DF62A8AE63681B85F516AA82614DD8F8D6A378A2AB8505B85E96E51" + + "6408140A20F2E517A386BD335504A239DA86524E44B7F1C08FEAB8F254CBC038" + + "E8551ECA5491BD39058A992EF55A0E64F47305F3C8781A3EF5A1CDD7C6B30BFE" + + "011C98167FDE1810A3FDD862DE14207C6A0D282431D6EA7B3A5103A29E32233C" + + "B677FC62C68EF14EC0B57A35376B5F399183D350970848D09D5489F156AC2066" + + "102037FB92DE0A6FA84A970270AFCC00D29DF43F970A01D7A67CDBFE0C9DD742" + + "876450411A3082025E06092A864886F70D010701A082024F0482024B30820247" + + "30820243060B2A864886F70D010C0A0102A082017630820172301C060A2A8648" + + "86F70D010C0103300E0408ECB4D1550DA52C6302020800048201509322DC0193" + + "DD9E79ADAFD38827AD6DE9299327DDDF6E9DF4FB70D53A64951E4B814E90D2A1" + + "9B3F4B8E39A2F851A3E5E9B9EB947DD248A3E5F5EB458F3323D4656709E97C6B" + + "D59238C4D1F26AB67D73235FAE7780D98705957B6650AC0DE3E2D46E22455D0A" + + "105D138F16A8483914EDDF5C518B748558704ED3AE4A8C4914F667BBDE07978E" + + "4A4FC66194F6B86BAB9F558EDE890C25DFB97C59653906CC573B5DEB62165CFF" + + "8A5F4F8059A478EBF6FED75F1DACDC612C2E271E25A7083E15D33697270FD442" + + "D79FFCB25DB135F98E580DC9CE14F73C3B847931AF821C77718455F595CA15B8" + + "6386F3FCC59622625FC916DDB4A08479DCB49FF7444333FA99FBB22F1AEC1876" + + "CF1E099F7A4ECA85A325A8623E071EEA9359194EEE712F73076C5EB72AA243D0" + + "C0978B934BC8596F8353FD3CA859EEA457C6175E82AE5854CC7B6598A1E98033" + + "2F56AB1EE12082774A91A63181B9302306092A864886F70D01091531160414E6" + + "335FA7097AB6DE4A1CDB0C678D7A929883FB6430819106092B06010401823711" + + "013181831E8180004D006900630072006F0073006F0066007400200045006E00" + + "680061006E006300650064002000440053005300200061006E00640020004400" + + "690066006600690065002D00480065006C006C006D0061006E00200043007200" + + "7900700074006F0067007200610070006800690063002000500072006F007600" + + "69006400650072303B301F300706052B0E03021A04142C936C6E3D06E14A72F8" + + "C08B2FD50E40D2DDE70A0414D58EED14B5437EFB11F19215656B22E05D119346" + + "020207D0").HexToByteArray(); + + public static readonly byte[] s_dsa1024Pfx = + PlatformSupport.IsRC2Supported + ? s_dsa1024Pfx_RC2ContentEncryption + : s_dsa1024Pfx_TripleDESContentEncryption; + public static byte[] s_dsa1024Cert = ( "3082038D3082034AA003020102020900AB740A714AA83C92300B060960864801" + "650304030230818D310B3009060355040613025553311330110603550408130A" + @@ -741,7 +951,7 @@ private static class RawData "CCAECB18B7EF4C00F9C069FA3BC78014DE").HexToByteArray(); // Password: "Test" - internal static readonly byte[] ECDsaP256_DigitalSignature_Pfx_Windows = ( + private static readonly byte[] ECDsaP256_DigitalSignature_Pfx_Windows_RC2ContentEncryption = ( "308204470201033082040306092A864886F70D010701A08203F4048203F03082" + "03EC3082016D06092A864886F70D010701A082015E0482015A30820156308201" + "52060B2A864886F70D010C0A0102A081CC3081C9301C060A2A864886F70D010C" + @@ -778,6 +988,47 @@ private static class RawData "ED140F5CA3CB92BEFCA32C690804576ABF0414B59D4FECA9944D40EEFDE7FB96" + "196D167B0FA511020207D0").HexToByteArray(); + private static readonly byte[] ECDsaP256_DigitalSignature_Pfx_Windows_TripleDESContentEncryption = ( + "3082041D020103308203D906092A864886F70D010701A08203CA048203C63082" + + "03C2308201CB06092A864886F70D010701A08201BC048201B8308201B4308201" + + "B0060B2A864886F70D010C0A0102A081CC3081C9301C060A2A864886F70D010C" + + "0103300E040867EFC6F9352D2AA6020207D00481A8FB4F97F1C05439604C1210" + + "31F2FE34BE90B3FCC24554D931236C1139FA658210EB58AC8FB22FA9970A29B8" + + "F9B2825D23EC03024CB1EDAD3E47C5D285AAC5C33F0B4DFDC83B9B209F4AA859" + + "BCDD17A9A3A9BCC7A26F3C6FB224E5532B98FE8F14D0BC5217F72BF602D55309" + + "CC58CCFA98E68A882D7A6031A8C2F947E3DCDEC32318CDA62C04D7BD8D85A5F1" + + "B5BAFC2BD05243D8FA00906F4E3006D4E05E4BE5A8A7EE8F86726898AE3181D1" + + "301306092A864886F70D0109153106040401000000305B06092A864886F70D01" + + "0914314E1E4C007B00430038003100320043003900300043002D004200340030" + + "0041002D0034004500390039002D0041003000300043002D0035003300300038" + + "00440042004200390043003400430043007D305D06092B060104018237110131" + + "501E4E004D006900630072006F0073006F0066007400200053006F0066007400" + + "770061007200650020004B00650079002000530074006F007200610067006500" + + "2000500072006F00760069006400650072308201EF06092A864886F70D010706" + + "A08201E0308201DC020100308201D506092A864886F70D010701301C060A2A86" + + "4886F70D010C0103300E04083B4E94A29A55072F020207D0808201A8AB9F6DD1" + + "EB034A689913986D91865C4F4C17595039EBC2F145FEFB656381F17F7222B628" + + "B10EACECC6C0658184A4913DFE74870284BA3EE5C83F88A09E0CD72D54D3304F" + + "DE93F274E2FA8E77F2F8C4F4A5F1C996004B12063B83DADDBA8388F9C407C801" + + "3D24D4BE2AA8D1C5F9BB250D42F7D2A7A93C4706C7A878410AA2D0971EFB551A" + + "80179AB3AB6597EE19D2025E572EEB36EE98AEE989F44AD03A6F38C262E7C43F" + + "C40E89157925E9E03A8B7B80B99DCAA92E3F50DD63EA059ECA49B456A5136DDB" + + "930F32DC38BE6F041856461E73BD8B0E9FCDBF8930B497C4914858B054A15D57" + + "0732B932A0A123400E9DBE50E867A1F98E582E87B29E670F9A2F44566B4BCBF5" + + "385C90DCD458975126E5534E1C51E58FBA4E1C26C34457E64D7BEC7D4A92AF56" + + "A090B09516A185D281B1F36EC14A58472546A77D7DD6960B3CC491740B267734" + + "1A884181E05C00ACFA0BDD8E0A13FA154E0A39FBA8B4E70E2EBEF47416D6E869" + + "C5FB84EF125903EEAB905746AF8E60EA3126719683E609AD6BEAE01B1D0BF700" + + "C8C61F2BC4C44348A5FC32B1E8F4908C369BC90731EC30D71FFEE237C87F2308" + + "6593D7A9303B301F300706052B0E03021A041497892AC27A24A2CC4F8841A8B3" + + "D4C17ACCAD1DAC04149135233EFE8BFB1A6C494E5E4F2621BF97B50E1F020207" + + "D0").HexToByteArray(); + + internal static readonly byte[] ECDsaP256_DigitalSignature_Pfx_Windows = + PlatformSupport.IsRC2Supported + ? ECDsaP256_DigitalSignature_Pfx_Windows_RC2ContentEncryption + : ECDsaP256_DigitalSignature_Pfx_Windows_TripleDESContentEncryption; + internal static readonly byte[] ECDsaP256_DigitalSignature_Cert = ( "308201583081FFA003020102021035428F3B3C5107AD49E776D6E74C4DC8300A" + "06082A8648CE3D04030230153113301106035504030C0A454344534120546573" + @@ -897,7 +1148,7 @@ private static class RawData "3BBF2077EBFB373F6ED8D6C7CEA7BFDF0425494078F293949496B0BEAF63CAB5" + "62C297590A737174").HexToByteArray(); - internal static readonly byte[] ValidLookingTsaCert_Pfx = ( + private static readonly byte[] ValidLookingTsaCert_Pfx_RC2ContentEncryption = ( "30820AAE02010330820A6E06092A864886F70D010701A0820A5F04820A5B3082" + "0A573082059006092A864886F70D010701A08205810482057D30820579308205" + "75060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + @@ -985,6 +1236,102 @@ private static class RawData "052B0E03021A0414AACBB391FF9626295715807ED7DDEE57F716A5710414658C" + "344F4B20292DD9282953DAA4CB587AD48714").HexToByteArray(); + private static readonly byte[] ValidLookingTsaCert_Pfx_TripleDESContentEncryption = ( + "30820B1002010330820ACC06092A864886F70D010701A0820ABD04820AB93082" + + "0AB5308205EE06092A864886F70D010701A08205DF048205DB308205D7308205" + + "D3060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + + "010C0103300E0408ADF347A4330ADED1020207D0048204C81696BCCDA96E42F3" + + "38EF7182A7517E36157F11BAAFE0CC6F289215BA8F034FB30D498A0E5149BAF4" + + "A40F3BD8A72F5B96A06AA5A7297A71A231A762EF9AE12C9E7D1AB37CBBD7D205" + + "7234110A55F14FFB89CABC1BC04C0388F5A655AE50DD9AAA001291799C7902E0" + + "BC942064CC53EAAE4E6D5EE19611EEF1B935131EA9CF38A055BD2D7CE3EDECD0" + + "4B34B299A1A1429A02DBAC1A4C1DEB828A933D15110C2BF6AF5872CCC84C912B" + + "9E14F5CEFAA7F0EB496B9E277765C9530784E4FA487252E12316166E78D1C6B0" + + "C4B74A0AE19C068F9AFD2BF18A5123A65E93696DDC9ABA7DD1625F9B16D9F5EE" + + "EDA9ED0507DBED944C6695B0DDEEBD3E2EC88F386FDC1497478EABA600F64E98" + + "478685EFE70B07A54CE17FAE9C0D66D9ADE92BDF0ED4D1FB87362FDE3981492E" + + "3AA4D47B6943B0DCC36CB5DBD9CE231980E54CE8A71D0EA3FB813D4903B88FFC" + + "D7546A41003B688CD43EBBF59E1181FDD3DC9C37D641D1E9CA99167C746DD917" + + "06571ED2CC7F04EEA2FF5097D859812BDFA491678EBE8D77DAE016757B3E4713" + + "94D5B08ED1C803AF3621644C8D34AFAF13239B6B6DDCFC163BAE728BA002FE7B" + + "19C7157A53BE0317073977ECF7072EE39EC1FA13FA26E392383BE1B57C91595E" + + "C5B4DD4740B63E4BBCB09C00A590E211FFE5863E7C06859C1B43912BEF2BEABD" + + "2BD1ACB43AD3729BED2AE491C1769F7E36FA91E63A8DC14A1580428817EF7451" + + "F5C85DD349B0878652958E385897C063E72D30F2B2C96C26D9967F8DEB796F05" + + "761C2406DE70D8D7275A40A78ACC802A4AA5B071FAF6218DC70288AAB29780A3" + + "BBC84E919D6A1B1D3F9DE41C931F860A27B12C155BFEAAF56CC5AD6F0ECB592E" + + "FD0B36BA166A121C66655A8B0C5DB299DC36FB5B75D634565DDF7A2498407452" + + "B59B996C5C586BAD45B968A20481598C285E870C3E534B3E51D28CE409B1B5B9" + + "138CC40EACC44C6859F83E6D3B6C1AD88FCD4414EFAD2CAA2658F8659905D856" + + "B0E2257606C1C93A39A54E35CD75405C61C49D7A8CBF3CA614797065C3B12C18" + + "4BAE8C222ADFC2E167CD6E6DF2B26270614EEEB009818F8030B5D5B7A5829C9D" + + "39DCF8ECD5CD3A0E403B6F9E6E1C6E682A8EEA4EA5CE28D2A123729A3C0B9A32" + + "845CD29970AD092A77BA1599339315F233F92CD741BA40EB029D36608CFCD711" + + "03A4CA5A4F82C688D54DC83DAF5B5EBB50A99802D69AEAD3A145FD9598D71345" + + "67BCDE414FDE05B922154BC841015C26DB93236D3BC32F125D1A4DF123D18337" + + "0A55CA413A8427F49DFEE024B52EBC7F1B3FBC6C19FB816B7C85DFE309CDDA83" + + "F078112432FDFA76F238C8C1BEAC104D59575907BF834618B36A15CD5C2FC817" + + "004C7D52AF29064E19CEAC287E362DF2DB179CFBF373786A29EE2B65AC0BD6CC" + + "C09AA409ABF211A69C357EA8972C3C5A413EDB8651660A2E1FE1B300CB402521" + + "CB4990D6C7C4FC20CC9C9D929D9324C2EEEAA4E253F1E5503E456A6C5365E625" + + "CFF07328E49322014036F4C86D6188647766F5072FC1DF6B6F1F222EF2982818" + + "4C9B2B625C1644ACA5AAB8BBE783A7435A00EF44151EDE46DE7B3D438479745F" + + "C31D620DE785C299B33C74479094D98B2CB6BA962C3DBE1840308476A5917331" + + "9CE976048856835830FCAC610F5BDC5C9E80DE63B56910B388C0B7F10E3356B9" + + "9B604DC107FE9A30292C0109552158D2D1A5960580E013C577946CB2A0DF9D23" + + "3181D1301306092A864886F70D0109153106040401000000305B06092A864886" + + "F70D010914314E1E4C007B00460033003400310044003100320033002D003400" + + "3800460042002D0034004400330043002D0041004200440039002D0031004200" + + "44004600340030003500440032003300330039007D305D06092B060104018237" + + "110131501E4E004D006900630072006F0073006F0066007400200053006F0066" + + "007400770061007200650020004B00650079002000530074006F007200610067" + + "0065002000500072006F00760069006400650072308204BF06092A864886F70D" + + "010706A08204B0308204AC020100308204A506092A864886F70D010701301C06" + + "0A2A864886F70D010C0103300E040857A66BF90E9EA805020207D0808204789A" + + "7480EA324EA3963561F35DB45508292574D9617690F85A4AF727E03703C85283" + + "95C03B958F0FDF0F547A823BE0D4912EC54B26D6734989EC9DBF638F9D951E1B" + + "0979D9432A44D7BC02395DA1675A9D12F30B2B4C5981FBCAB9B1EA258F9F8DAA" + + "6A97AC923910D4AA0BB67CEF311861069CD4DD1141A1F34E01EE44DCB12C158A" + + "76EE43CBE6097446ADE2AE14DE57B36E22BEB8FAC309F9B92385A30D364572F9" + + "C499DCAE960C4DE6223303EB52CD12B02115C5A3CACC7133E04047649CE99F71" + + "5F014B29FAEA55132CE6BF5190BF01583A58EDBDAB41B21FE9DC18E57B2BFE65" + + "41E3796DAEF49AA2ECA8083D576A2E98258AD488D48F1832D6C64FC08CE56F63" + + "97075E358963FC47F3759F6CCAF3EF37A679D542EE05FEBC4BB9E292B141497A" + + "74C99219612C65D42AC450DE6EBD35BF700A1EF5B844C955D81D0D20C84EB999" + + "161AAC32931F46D9216A85004858B3136AC2B5C94FAB360ACD441C0BF7870A69" + + "1E3114816F43F6F5B5171C1EE85814EAB5A0640CEA23F7AAAB756DEEE703D4D0" + + "848C071849DFCE92048C4422F8B94C8FAA430CBCFD1A736B1FEF01BB9D8F64C3" + + "55D42A645D55F772A101A630803D964813C2A15C3891317599664ED1B6034049" + + "1B7369C336A6E6513DE128E258F0FE2C2C6E1188B8C33F072CD11F4A72F68482" + + "C5433DE391E9BBC58E1C886CE11F3EC733D29F7E940D695C7A56D5BBA47F3D13" + + "8BAE578120F7339AF9DEB9930C786B83F16A9F56BFB31A0918CC917811E12787" + + "10F67967E0EE429A9B8F20D683D9AAE0A860653CFA133F001F3442931396EB18" + + "CB1D27AEE356CA2FDB374191BD59CFD5A9E58341FB32757459BCB568CED7CA8E" + + "4EA4CA2DC98E9FD250C4308F65482D4E161E6D072D40CC9D03FFB72A069EC289" + + "9B7E119ECD0AA49074E259CCD63D02395063790ED310446A3B49B0FDC8F05665" + + "A07BC452474DCBE3B739ED79D52C946DB7BE2BE95602232D1DA5B9307DAA6147" + + "4633BD04CFCF1FF20637C83B0B6A3EFCF59445428EF8BE2E22159BE5F42899FA" + + "F70B50F24FA9131F8CEF2D01DCF3DD60A9780028F302FE813188FF9CB9156880" + + "8FA543478A68503A5B8CC93167F0B8032029073C55ADEA9676D08C86C4FA0D6B" + + "E9D2AD1983FD068997D86980663778AE94686BB19E77362B0A9289080B1A0865" + + "4621F8ECEDB1D08224E44D8F0785DF2EB6A5B1BC7A97182926C30C78B345F995" + + "1E5DD67F318ED5A7F7922BCD71993426E26067477C0ED06887C8790B73A5661D" + + "B753E85433080616FBB9EFF7D40A7DD3072F9E89B51516C94186C72F3804C7A3" + + "1DF5F507675B21B295A0D14B633BD515C382AC4093B92D32800C7F63E8C9DFC2" + + "30B1692DADE7F5E6E71FAB8B40D3516F6F10AC3BF3722AE4F85FC6414FB4FD45" + + "5B93D87171DDD04962949AE685448B20C27561A58F90E05503DAE995711C10ED" + + "2881F2F15BF58EE206625937D620C715E397BA6D74C21BF68E1CE35915A9A917" + + "E935308E724F336151CCA43498371F92A9C3E543559056FC4DF42857357FBDFF" + + "D26A215B4A4F9880888683956931C11B020D8F69E392B4668A2E05D1DDCD8CB7" + + "37C0EB82E91110CDED11FA41D5EBDB44499BE2FCB5E021303B301F300706052B" + + "0E03021A041413439C2B57F81655034A005B25CAA60BF393100B0414A6161E42" + + "F758104D24EBAE60A38A72EAF7F7519B020207D0").HexToByteArray(); + + internal static readonly byte[] ValidLookingTsaCert_Pfx = + PlatformSupport.IsRC2Supported + ? ValidLookingTsaCert_Pfx_RC2ContentEncryption + : ValidLookingTsaCert_Pfx_TripleDESContentEncryption; + internal static readonly byte[] TwoEkuTsaCert = ( "3082044030820228A003020102020401020304300D06092A864886F70D01010B" + "05003029312730250603550403131E4578706572696D656E74616C2049737375" + @@ -1022,7 +1369,7 @@ private static class RawData "7257229376EE8C0179396C355DFEEEC03F8773BA1DD5B0807E44EA1E11257751" + "67020DF9").HexToByteArray(); - internal static readonly byte[] TwoEkuTsaPfx = ( + private static readonly byte[] TwoEkuTsaPfx_RC2ContentEncryption = ( "30820AC602010330820A8606092A864886F70D010701A0820A7704820A733082" + "0A6F3082059006092A864886F70D010701A08205810482057D30820579308205" + "75060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + @@ -1111,6 +1458,103 @@ private static class RawData "A519F103F114AFAD5EB7F368DB4D0748559CDD190414584DD2F41EC2DBDAEA69" + "FB2FF401BD9FC3B57572").HexToByteArray(); + private static readonly byte[] TwoEkuTsaPfx_TripleDESContentEncryption = ( + "30820B2802010330820AE406092A864886F70D010701A0820AD504820AD13082" + + "0ACD308205EE06092A864886F70D010701A08205DF048205DB308205D7308205" + + "D3060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + + "010C0103300E04087D3CDF9B432B99CD020207D0048204C8ACFB62895927191C" + + "320EA183A55D7F3557941E6B7C5ED28F91EB4C17EE01B6683EC313B12CF1DBB7" + + "1A5CA686214B9916292A9EC0DA42B7195772368CC3F05DCD449B010F55F4EA6C" + + "C6061C8F70356A4765D6FA5DB4DBF6AF14BCAF41020FF275DE691229BD5B61E0" + + "C06FA2239387F3A09DF650919723E0DF02326980B46B7425D5330CB6CF77B0FF" + + "CE374DE81A1A60BDA1DD3EA7DBB60AA36FF462C9806893FBB4774C3F7C1F0C9E" + + "910C6D0771779C7C911949B48BB636F44A2D907AB7DEA864A4297CA8DC3E58BA" + + "3654E41767A823F7BE90932099A07CB26AAC49EFB9C9046948B5BEBEB794661F" + + "8E7C8E0EB73521DCC6D6DE25DAB6AA12194BB7FC3949174569270A781792A6B0" + + "ADBB00B64944BD0BECE863D9DF9A177BDD11E929C76355EAA1814E65281E3672" + + "F566031B423E5B9E514AE03BADB6B4F894CE7AA1BF1433D4CC2B2E9FE7B07823" + + "6CB9B6766514026FF0F432FBC007951A51569403762956B5DDFC0B2EE7B69297" + + "0A4E25362B6DF26454AE56EECBF8CD9FE7850CE9E85BFC939DCDD94990872461" + + "CB32B2840DC87F200DE00CB13DA06A845ECC67AAFB77D53F06B216E2D1192BA9" + + "150690CA3CAD285BAEF34F3AC982F0F9578915652A681A79F46B808603264A2B" + + "EACD854CEE0D9D9B284F72F863E2BAAB8D40C102C9563BD806C7FE6EC5F3ABBE" + + "0F3995F65CDA6AA3CF26F086499DDCE7A28F3AD4A15CF244B4A8519CCB181CA5" + + "F7A1967A9A079B0E22629720544BA1CD16B20C34DAF0B0A192A838A7F41D18C7" + + "AF8E3CD67D5CE688A8A5A7BC19B1852DED772A9A648B59E5421711EF59A765E8" + + "550832DD8CFBD8BC19B8E88E8EFD14D384F747DDA2FDE6AE9189A2B8FCA2B9CC" + + "901FBC6621C40EE0CD77F3A4E8B81451C6AF6B18FF53B49FA97697A54628F8F7" + + "F87EB621CBBA472AFD1F1442F3C39051FDD746C75F7256B3624FECAB800D441E" + + "AF0FCD857DE979E79B192F1FB9E330C85629F0E6727003F1D58B03629144F14A" + + "F0761A09EC9B4AD459E798C2C20E32F1B67BB13DB2A04E2445DF74BBD536AE48" + + "7329EFB6D5297F37F36A098AF4DF3078044BD2F44F64F8598D3CAF7732586AA1" + + "EA5BAD85CCB8348B2CE595102DF4DA0E791B3C21BC860135B2F3A417FA2C14F6" + + "F70E75DC0C2F11C0C4B7154BEEE2EF251062E1A6592E7BC7E45740750C0F2B70" + + "0EFDBEF16A6F11D07EDFB6502419026F74929FFDB78DD2379BB83B02DAC29838" + + "DCF3A71B19E3D1C8B87ABD7970A1E658949E9EC53147E54FDDAABA0A21A04229" + + "0200B5EA2A9420941102B6218C16CC45EDB08627504623B7D3B8BB78300260E3" + + "FA1C20CABDEF8E8A1F5D1E875A321F94BDA20C798CE784484E7722FE844D0377" + + "D5B6FE150BA29D428AC0C42538BD41908DCEB1DB32E08939750D2454151CA85E" + + "64F42D0C4677FD7E6E88B881F4B1959050CE4D22BFAE7D0340EBF893E6E9961F" + + "9C72F1DD16466351A12A842672C0B79D7E05A272B393582B7B02F21BE35FC165" + + "31E0B7D8A317A45590D32BB2F61DCEC5AC7C15EA86341397EB3ADB80F8FF5A81" + + "17B2AEB98CCFDF6AA6E1CABA3547822EBD697EA2A82AEE5C9336C6E97C476D5C" + + "4EE17336CB905EC26D13CCABAB82F553A39695DC3D102CA17805B2F3259B73E9" + + "945617FD77BAB0E5E55B430164554DAD84C27454BF05D52F6B130AB69520D01C" + + "CB72A1BEFF18FE1D24DF3E5DB85DB36342B75FCDC9D1DB924A6280969DE29DE4" + + "3181D1301306092A864886F70D0109153106040401000000305B06092A864886" + + "F70D010914314E1E4C007B00380033004300420034003200440043002D004200" + + "3800300038002D0034003000370030002D0039004400440032002D0045004500" + + "38003100460034003300310038004600410046007D305D06092B060104018237" + + "110131501E4E004D006900630072006F0073006F0066007400200053006F0066" + + "007400770061007200650020004B00650079002000530074006F007200610067" + + "0065002000500072006F00760069006400650072308204D706092A864886F70D" + + "010706A08204C8308204C4020100308204BD06092A864886F70D010701301C06" + + "0A2A864886F70D010C0103300E04080242E511671DACBF020207D080820490C0" + + "257A60FD19BB77BD42308DC6F9042BEE22410F9EEAC205F23C907B838043C8D1" + + "EA8B9DD376DFB810AEC783603B99B15D44F14674F83B6D7A11DDE52D9D1FFCDA" + + "28559EDE9C4313DE021D2DA3D2D528D3A5B92ABCB82F25EC2581CCA0742BFE92" + + "EA499A8C4A9367151B7F6F6BCA7E07105A38C3ECBBDEC4B5C42063B12FC587C8" + + "C44371A6E9B171E9C9D2AEE4463FC8C2FD9842C675A09B8B6EBD2BD3DC207FB2" + + "C0F1875272B96A980EAB1B1735C0FA89E02FF690014370ED6FA544DA209F3981" + + "D94946B6C2AC501B502AA3ADBA38C78ED5F433F70210F6F9B6CC4E47A4CF5EC5" + + "DAC225C8F4C848452A2D8B9FA4CFA0ED53728FA970F48BF8C88341894A2E54AF" + + "7D9CFEEBE56C146237B901233DD7B296D8C9A06F7B962AAB67D1721972B8C3EA" + + "F34EC994238C500D077A876E560067844E3C9086C809DA7D9D04480858DCD311" + + "56057816354497D9D41C1F34456FCF479B1FCC85427888D04CDE4E8252BCFECC" + + "1A61A605847D0C7DB7F11225718F7CC889F9C9969524E8808B17925FAE2CDA0B" + + "75F825F4F5F35A482DB0A0E3F305803C9EC9EAF9849A45DC3F6D09A018B4213A" + + "E5DB24B82B4571A6242E41ABC581941A5C9927171BE89782989DDF72E6EE857D" + + "2FE7112FB20D43071866B751D30A85DCC2DF0C66E169B7857D6430373E119B9C" + + "0DD8DE260E12688D74DDE8FE2327D5D9D24680F95892F045E36710E2D25AA58A" + + "9858F94202378242D77BB56D94A9EBF08BDB7EF9E893BE26BCA49B52D4881B6E" + + "8AB0EA44C9D58D32905E63C6F1F1AFA2C5D1AC8C93D958626C9A47EE6CB6D7EE" + + "1D28F890D56DBB1F41070529C4A029C7BBCF0CE44748D8318EB4C2756B0979E7" + + "0136AB0B011CDD3D90EC48F416E63DECB183BA68507B3EA2A981F19F5D9A5EE3" + + "F83E821C4CD0B8DAD888626AB3778D788F4DA9A46CD3CB039545A711144D366F" + + "5D63699567F2015913D8C87FDB60C5E4DF2B07E120E9E22FEC81333429718B37" + + "A878D6104FFEB0CA9AF281DB0C64441A24A3020A562E53C9F4558A0AE7C4DDFD" + + "A165AA29AB73EF31C491F9DD7B3991F45786E9DDF3B44202EC847F8E99D829E2" + + "6A1D8918D3EC7B84936CBF0E7CDEF220DCB3E246169EB1F5EFECB54B90D18CAB" + + "CF0CE8628C8C36D5066735CFBE31C8062EE644FDCED0E2136EABB4D67A1ECBC0" + + "530A7FB5CBE87233F76CF585DD4A6D15DE345A8664D4B44B5CAF753C4F5410E4" + + "98DA51F31FCA417EB474B95725016ECFF71197BAB7541AAC3F679A493A5B6292" + + "895967DDE578E5F0CF2017CB210DF9C36B23715BFBC64D40B5005F09171654B3" + + "BE3424D9F749AEE51F5CDD350B11AF766218191A564A58BF8791F4B69D3627DB" + + "6BFD40FDDD9BBEAC97B73CBF156514691CA7993978C416DFDFD9499C439BE755" + + "95279EB20B063EA6F9B6A6892B4C95960639C5A76E8E7DB33B55D9DDD67F1225" + + "88F4331C3742F4446A0412C1992CB7C5D71F7F3F4F631CB383D58E0971FD54C9" + + "F9EB711D9CD97D4082B337B7E97859D70DC1326DFCBE7EEE678D544F7266424B" + + "8CA931B46C750A2A156C55955F368AD9B3FA6A600126D3E99F5D605587D37FF8" + + "E9E228426C65653412C92A380B5ECABEAF61BCBEC82195B17D036329C8454C88" + + "DBD9AB3B4D5FC3AB6978088D09E690303B301F300706052B0E03021A0414E6C1" + + "EB5F19E56891D2AD2D86C30FA62CF75AFEF10414CEB6CBF0022C852539BCB5CD" + + "5CF4123B4A8FF97F020207D0").HexToByteArray(); + + internal static readonly byte[] TwoEkuTsaPfx = + PlatformSupport.IsRC2Supported + ? TwoEkuTsaPfx_RC2ContentEncryption + : TwoEkuTsaPfx_TripleDESContentEncryption; + internal static readonly byte[] NonCriticalTsaEkuCert = ( "308204243082020CA003020102020401020304300D06092A864886F70D01010B" + "05003029312730250603550403131E4578706572696D656E74616C2049737375" + @@ -1147,7 +1591,7 @@ private static class RawData "F227222B86010D9521324BDDE04F47BF5190778C6B812ED78AC7DD8C82FBD0C4" + "0DA1EE040184500D").HexToByteArray(); - internal static readonly byte[] NonCriticalTsaEkuPfx = ( + private static readonly byte[] NonCriticalTsaEkuPfx_RC2ContentEncryption = ( "30820AAE02010330820A6E06092A864886F70D010701A0820A5F04820A5B3082" + "0A573082059006092A864886F70D010701A08205810482057D30820579308205" + "75060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + @@ -1235,6 +1679,102 @@ private static class RawData "052B0E03021A0414A0E907FF695A237FAB54BBB94CBCE689EE0B4552041426E2" + "132250203B3235FD5023D999B747478D8873").HexToByteArray(); + private static readonly byte[] NonCriticalTsaEkuPfx_TripleDESContentEncryption = ( + "30820B1002010330820ACC06092A864886F70D010701A0820ABD04820AB93082" + + "0AB5308205EE06092A864886F70D010701A08205DF048205DB308205D7308205" + + "D3060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + + "010C0103300E040885A220CF9DC89867020207D0048204C8493E01DA35E65667" + + "AA4C7764114555B54D086913F0892CF30E2AF6C669E4D2A174448E2455FC889F" + + "E1AFA684549D8C0A316A3FF42BEB3D223F76C65C78E834F083EE6D84C896938E" + + "F66306F6A146C3E1D065B8994E234D50D0617B29531BB95B37F16BDB2C7B2AE3" + + "834702AD58FB29BA6342C089C91699C2EAED188CD02DDE492DCC46E0D5C347A0" + + "22EADC1986140686F8AF7F6AB77B324E80890A8CCF01C954A48E518B02835398" + + "42FC74BF41AEDFD5F650537168434DD53E6B430BBC9781CA10D4A479AAC8A94F" + + "25B2A594B786E8CBD781C7CC137C53A98AFF2E11790671CA41175A708DDB74B8" + + "7F88548587BE23808A696B5CC165A4FA4777D9BE9F2F5BD0CE0BEB6C36353FED" + + "472790064A42FEA5456A38530AC10ED7862FC44833D0CC2C8544A516AD4D9118" + + "4A94A4D1356308FDCCA27599728E0B089CE08FD4B00D823162DEFAFE9E703310" + + "00D2D363EC936E6DD8D100ED8145306E485C3ABE2C1383FCDED4D29AB2A6534B" + + "AA939587492DA733F1488C308671A7EA2D6F62088B4AB497AE0267CEA5E80C26" + + "14FD5513B0989B2B24C218B0ED98605CA56A4DEF167221E904CFCBF8BDD897FA" + + "E3265672B3035C308E82222B6FC8F43F670B4B26CC26B644019EE959386FCD2D" + + "F5DF9A8E48B1C4CD523C04D8CF49CBC06E7445B8046EBFAFF74AB822B38174FB" + + "513FDE0930CBCF93F6CF8D069969270E3B486B3FDC34D31FC35864C5DE52425A" + + "8C581F6CA430C3841DEB9D1ECC7FCF353C8023C254010AB993651DA372CE3F10" + + "A0B5CE853FD563D9022B435D99D31603610600578DFDB5EFD127D1006DCD1942" + + "7DE7347E3539DDEA35A6CD10FD414D854D3D293828914A11A160A08FE1EFCE0D" + + "792DFE648F0948C232DB46222B212B6C4DB1DF051E5D399F34F695DA4587BA89" + + "522D98E930E5FB602F90939115A64D10C854A4032033BA09F8EB230D285FA8CD" + + "CA103B1C699B78271AA397B6ABCAF0D12DEEA3AF63EBA477D563D291537079BD" + + "96BDCB960C2A8A2ADFB687B969069956D300E295355836926495B80BFEB2B0BA" + + "848A7F05E3DACE177D1BF127F0A50572D9009F1E10CD8D92EB034DC2B729E3B8" + + "34862A8C6BD39E35B83E6AADFA75AF222D52E64F882F5A0BFFB6BF5C20AC976F" + + "32D42B53E7E857F25C4059E6E226E7E29CBF98E617A8D6B5EF9378E68BF705D6" + + "8D48A1E687BF13347B782B0CBF11B9985A84B0C3B5C224E7519DEF2C40718C27" + + "D6FC5F0868B0E6CF74A1858EFCF63A3B350C4BE8C0D561678B06ADB9EDA678A7" + + "1E6F1ADBE74231C690B10F8DA14055BE670719ED649669F75B71A01726696868" + + "D9A962815D7B2DD29E33DC47C0807614AFF891A3A687FAE688CC423766C7877E" + + "A87A46E7F7E7FDEB4B763C7DAE055F5EE5383ADF8F9220FF4613F8E90D8D03B6" + + "EBB1A5C8AB3AFE1A8FF0C4624800AEF4601E476C7E18CC3E9836D8E555EE7C4F" + + "1306065EA84DB84D8A3E4AF0C2B1D897AEAC2F8DB839EEC0E68F8E15B6B9B8E8" + + "FBB28608C246A449644AB8645830F11CB20594005EB6337A8159518F7F1C634D" + + "E571E1577E1E6FFA89E9457F773079A3DC059214FCE5E09495D6B64F0E2D69C8" + + "38BD14B10AB2D1793E6D6AA0DF74220F141A6958D73228D5AAC194B3415174EA" + + "92572B367ACB5C5596A63C9E695E93EB9F6BE647FB0EFF6DAB3A29D6F3186380" + + "17EE35CED6C5B213EE896DE44DB79D6F70923B0009D2FD6F1541AEFE3E95149C" + + "3181D1301306092A864886F70D0109153106040401000000305B06092A864886" + + "F70D010914314E1E4C007B00340042003900330031003300370032002D003300" + + "4200440046002D0034003500300036002D0038003800340043002D0042003200" + + "43003500300035003200300042003300330038007D305D06092B060104018237" + + "110131501E4E004D006900630072006F0073006F0066007400200053006F0066" + + "007400770061007200650020004B00650079002000530074006F007200610067" + + "0065002000500072006F00760069006400650072308204BF06092A864886F70D" + + "010706A08204B0308204AC020100308204A506092A864886F70D010701301C06" + + "0A2A864886F70D010C0103300E0408692303616098813D020207D080820478A9" + + "DCD7CA2C409B30FC9771D293816CF36EE63D1DFE738DFCBBE0C43C04995DAE19" + + "24A9FA11922A0737E1458A760A6168686638DF4C6D3E1B621499C5CBACEF6953" + + "6AB12FC6656DDA61D7DF31EB7A4FA0DE62E931C2B22CB30ED58C4584AED90978" + + "858B24F416D6E1EE786F88CAD806D52522872B8D6C61EAA4533C12257E7FAD73" + + "A797ABB89903D50F42C63DD4529DDA07D8E6442E91C4D2F4387404AF4C79780E" + + "29668B63E1C0B926D0A115087D5060EFC657EC96D29D7565F031C5271FA77323" + + "568FC7714570A578C68A6FE92B68EEA7659340DDD9ED1FF51085341ED408B91E" + + "009A42D49732BA66A93E43CA5AB8658BFC2D496E4AC8FC0E119120C8FA108C02" + + "6A5E4F212EBFB91CE1CF879875918312D289159AC4AAF776A4BF3F06BCA6BBD7" + + "10792147EA54CB1BE54936D7987A0710D6CDF71F5DD5470A265050A8D7718E70" + + "37D333A4C932912333695927FF0AED530B00E119F5CB18A3518E472CE0EEDAF0" + + "0010AE887F9F7FA911DFB9F34D9122DA2CA220E543CD5691D1AD11BA926E0158" + + "9BEA260EAE698F62212F2FC2AE194A188109EE4EDFF7E1D90439BD31E32998EE" + + "845AEE83B4EB13F779CACC3B6C981A122A0EBE543052D4587D0C0E300156C24E" + + "168503D4BAABAE11764BF13D3D037F283A824DAC692B645F539140E2EC8E0146" + + "A090BB3BA2FF03D7F3235E73425D4F7161B02CA24B917AE5AC3A3BA540610946" + + "80F3DFB7FD21A9CDACBEC8FA90956CF8F59E786BE57B003EF58324DE1BC54004" + + "27BD07C841A50469551FD3A4F40788C2BE8D685AFE3D8F4110F14B9B1466247B" + + "4A272706D8D2793457FCEB9B12F39F3B1A225F52C13FB7181EA43C07262AEE55" + + "72EA80212643B208C9C708D2E813301953306A0F5DCD1C0F829D952ABAEF61BF" + + "68137DEEA7F9F01C66AA2589B9F1519B382B7948EAFAF80C7B0A7792A55B9847" + + "EE0A40C622B9D34AEB2EF5F7708D3BCC0D9947334EC8BCC89BC5EEB412DC98EA" + + "5BDFCD322D67CCBDB2ABFF57E508DDA226C89B24AAB4AC5433AB6227DF11AF99" + + "7FB8ED5C7964BD2755DEDA6A9EAF1FA3C3F13DCB878C2CCAACFD45D1F9233177" + + "363ADCB3F6B86EC0C84FC0BFFF009192ECD08378055658BED34AD6443C7229B3" + + "D8AA7D9B086A2952168F44951204DD67B18394E19E41166A60D52123EEFCA5EA" + + "2001135734E3B18F98B92C08EA0C3D57A697032EA89691DC7C57651E498847DC" + + "ABC31DD71CFA992EC7F22BFCBCF3AA8D6EF166032567B77D218EE831E00F4333" + + "00D776BC1AD652902DD387A9A4D90E9A56A9784982F8E4BEE9DE15FCD74101A5" + + "36BDB3AF9A85E7D6757F073C66E5853B9B65A153915CD453192155733D3EBE08" + + "6811580CB50BB73D519896FEE3EF7DD91C3E50F8AA6920DC6B0331425DE7BC4A" + + "B05B2810B1475CBDAD17674452F082AC43CDAF47374006131B73C6B55C8340F5" + + "AF89B24BEDF6D2C257F761432ED864D37B1F36DC717B7E84D655C775360397FD" + + "E0E7F5A3F9430565DF4D1550BB1AC8E4CA761BFB47C2AE5AB50927138A075A91" + + "7D57E5006A81ABDE9E7361AD4ACF811E82395F0660B42145B4D81A027D039F2A" + + "117F641226C01FE1A993CA8FD3BC1AC0F4B90396FB53ED303B301F300706052B" + + "0E03021A04148527AF9ACC926A87B49C07238F570D60B0588B4D041467593F11" + + "3A8077255DF0A79741DBB93B70E5E352020207D0").HexToByteArray(); + + internal static readonly byte[] NonCriticalTsaEkuPfx = + PlatformSupport.IsRC2Supported + ? NonCriticalTsaEkuPfx_RC2ContentEncryption + : NonCriticalTsaEkuPfx_TripleDESContentEncryption; + internal static readonly byte[] TlsClientServerEkuCert = ( "3082043130820219A003020102020401020304300D06092A864886F70D01010B" + "05003029312730250603550403131E4578706572696D656E74616C2049737375" + @@ -1271,7 +1811,7 @@ private static class RawData "A5C6040881E61510E90C20F21DEA0A73E0DA5C1A2D178A48D76CC8FAA2ADA660" + "2A98B50AC197B40348A012E83A98EFF2B8D64900DE").HexToByteArray(); - internal static readonly byte[] TlsClientServerEkuPfx = ( + private static readonly byte[] TlsClientServerEkuPfx_RC2ContentEncryption = ( "30820AB602010330820A7606092A864886F70D010701A0820A6704820A633082" + "0A5F3082059006092A864886F70D010701A08205810482057D30820579308205" + "75060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + @@ -1359,6 +1899,102 @@ private static class RawData "973037301F300706052B0E03021A0414F2009473DB4B8B63778E28E68613246F" + "D53995810414A911A22D051F1BF45E7FF974C4285F6B4D880B08").HexToByteArray(); + private static readonly byte[] TlsClientServerEkuPfx_TripleDESContentEncryption = ( + "30820B1802010330820AD406092A864886F70D010701A0820AC504820AC13082" + + "0ABD308205EE06092A864886F70D010701A08205DF048205DB308205D7308205" + + "D3060B2A864886F70D010C0A0102A08204EE308204EA301C060A2A864886F70D" + + "010C0103300E0408157C22B1DC09A4B1020207D0048204C89340FF05561FC850" + + "8D2AB6A7F4CF1B5C6D0AC638CC40E58A2518CE947393B550E4A2214B188C479B" + + "CF508130F4C130241B926348AB97EB8F9602FCCF05351CD1FCC938A717599744" + + "5EF72D77FAC8780ACB0B2E10E12C4D48110A0FBE516A5FA2345A5C28FFA2AA01" + + "3D7A7AC72D3A8C27BBEDAF9F4BF8FB44F54BAA23F2D45E07CE669F1F979BEBA1" + + "BC5475BC82E74D3BDC5374FAD9C3224C979AA10F4259755E8388C8803430AA35" + + "87716E7C0FC3B469A88A61F9328CD5055EB6309C58DAE2749DE02A110DF13249" + + "33BD8E7B94CB6DD31403D03B8223DD3B00E94465ACC1AC678502D8FB4599B310" + + "D3252BD7E19EB1804D87BFE3B0C000B7DB7EF955C9B0AB96F98BC6971CEFDC6D" + + "E9759D0291DD0C9A47DB5B23202906E1788EA1809E8B837EF67A9AFCCFF979EB" + + "D202F85EE213CC04044303A30911043F1BA340DD3D5D44F9F2D038C37E8BA095" + + "46BC781AF70AFAA07CFD2CE72DE02F31346DAF2ACE9C71E026C5178334F5E1B0" + + "2C2560DCC243A83D162F145C2CD6679D2889D3E4BDF428A03B6089E7D565BB0A" + + "1D4076AA304D39E2C293BB5C7132216B8830A97E646EEA849123B86C2234D0AF" + + "9AD7FB623DF5D1AE53AF1FAA84120F724DD29D09C6CBDC9AD69FA8BBFB255DE0" + + "2814A4D40EA3A2DF65DC6239B5C87EF0A13CF0A96662987B99CE5985A6703432" + + "C3AFA98A1B1EC2E9064E166F3B5694A04C1B5D8762DBF79F4AC8CE785A826AE2" + + "E79ED26EA800DF797BBEF20CF971730EA895456728797280EA53133000BD89A5" + + "5ED89348E0F435703D4A19C5030919249FBC551056EC27A5CA1366B59D210C5A" + + "AD65DC1604D9DB61ACDE4FC48859937532042872FC93A6279F2D9EBF1CBADF77" + + "9CF1F0CA3660A3B8363D316A79CB14AD76513565314839EE46DF8A79A588AA46" + + "2A41E1F63AD8AD5D34DE9FD2362784CD7615F35DB86575FD123686BD26A53CA3" + + "842427778F227F633C28B50E86490E337004ED11089CB5965C4877A6C4A205FC" + + "A0FEF43AC05E1C66F24BA51DB861617BB3C0A287B63BD03B0DB02F9D3CB213B4" + + "D785DC7A6D9CF11C89A94DF734AFD1685CA9803987947454A8B653528196E8E9" + + "7078294A7A9A19BA5F326EF15F5B1AB9A3180F774BD8C8DFAEA480B0187AA08E" + + "9354719170D4DF7AB9C9F5086135B4A4DB5555FA9A283B1052F4E7762B5D2FA3" + + "1E23E6B150B56F6551142D9EFDB857379A7795CAA25D01255AB55A656B325C24" + + "29094605536DCAEEE1E4A6237118DDCFF39028FA3997907621A2EF2EBC007127" + + "5539ADB45AEE3E44ACDB70B39C7613FF42E5084BD9DE9B38D8CB166D34B58E98" + + "E6FF9A14D5F667D94EEEC2185638BA467E026538B264B6905E09B8B279163B3B" + + "FEF0C0FD9D44EB3D100D79069FDD25A730DC380959C9043272F24EAC30AEAFA8" + + "2BB27F78141D11FB5BC36C6CEE545B4B3E541047496BFE02C539B5160BAC5A5E" + + "0D97FC1270501E77E7461F06D8C41F843F624C821DDE87E66D29D0D97B669F69" + + "6868260BF2F4E4797D65F0833D72DA997B81941E766DB263C212EDE8D79DFE19" + + "BF50C5D1A4664CB2FCE52A729B091215C69CFAE2DDA561CDDBD4C9A598B11E50" + + "B26162FF757357DB2127DE4FD5A313A64694BB0292F482F1711515975ED49A60" + + "78A856D7BDA9D3976DDB75FD68D5A3743ECACC9A2A715CF1B58F1B32D5A46E5D" + + "4CC7E4743FA7E15E70362C773B621B39D9C996C8867F8CDA9333B9898395D43B" + + "3181D1301306092A864886F70D0109153106040401000000305B06092A864886" + + "F70D010914314E1E4C007B00340045004200420039004200450038002D004300" + + "4200370033002D0034003500380030002D0041003800350033002D0046003800" + + "38003900300035004500310038004500370030007D305D06092B060104018237" + + "110131501E4E004D006900630072006F0073006F0066007400200053006F0066" + + "007400770061007200650020004B00650079002000530074006F007200610067" + + "0065002000500072006F00760069006400650072308204C706092A864886F70D" + + "010706A08204B8308204B4020100308204AD06092A864886F70D010701301C06" + + "0A2A864886F70D010C0103300E04083C4FC94CA167D9CA020207D0808204806A" + + "4AB218D2BB0B213172A7988955F8E22DC0D8FC9B5B009A7933B911F2C0B0F483" + + "12C8A3A6AAF9DF1B9729FBB82C6D0683BB1BCB57EFF0BD551A247F1F0C903043" + + "6EA13F2463B85F18D94B68ADCC18D5EC979BFB2062A5ACAE4D5CEF22A86BC591" + + "AA80BECE63688F8B3090888BB1E07F7D9545A71E26ECE75F5AF728BC5259FE89" + + "938DCB1C1B6149AE02A99FD463608955464EAA1061E6B16C377602E960FB73C0" + + "90B86381EE3BB2146FB439DA8CC14E2FD7B88D08DEB69250D4F274506FB634B2" + + "01C98297DBF121168534DE416366F1ED4D96FA67520F197F2AB219DECEEA8241" + + "C9503C8379A18176BCA4720AF05A820653CC63366C625347BAE20DDE0D07E617" + + "3EAD3637706820946CA6190393D242FF645BC009A06B812E10C4A83259DB1931" + + "A0D91A9B4835FFA93A5F86DF501AF3793DCABF3E1A6D8789968E449DFF77725A" + + "DA0CA2F87F7F545E151E62C8C84CDA7197187693E2458A480F26D531C68D74F9" + + "E9DD84B656DC62C73B9833583CDF8FED3777EAE99E06BD33BABB81214987D8AF" + + "28F56AC8D411646A8F1DD7C4847ED63937C7B10683DA118059ECB73A560A289C" + + "8B675E7EC35810912DA5201DF36BD8CA14428506D6181B3BC4BEA807D7C2CC1C" + + "8459F48F6DCF9D5842C85DD374B1D9B03E6F8340B197FA5BF8B9858FE7D56391" + + "81D486428A1474C8299959FC1CE0FDB8A3912D09BE3E4F05BA58AA2B6999F09A" + + "3A4ABE26EDA71C6C611C139F8C8A112106036BC55F4320344F4791A59CB02A31" + + "8DC0820625D531A0A79FD5052A492DA6E0637ADFB6F90F672CE9EB8016D0026C" + + "AF155553FE2256B4FE791DABEA84244F154EFA22CEAA47BDD151C1EFDA50B44F" + + "985EC26342E775D9ECBF1D6C03B29A57982A6513283ED5E39776BD96B8196B72" + + "3A48C02E058DEB7EF6145A4153867CA24357AB670F4FF5BE3E707B4ED9A6FF9E" + + "E24E13A874AB2985F4478AE3A5958D4A41249A021558EF56D531AAADCA69A6AE" + + "92284D764F229AB09EF4529BF329FB91895013F808006329D2C5DFE05EFE3712" + + "CB214F01B55834E70C5AE483522DD7D1B712E9EE93EE5647618623423DBC14D5" + + "78C2B4BE1B038D8CEF8C70DD6201692CAC995C51B0366EFD01177ABD3D4C153C" + + "5D04D62EDE624E978F1CC9A01BE84E5D52CF114614340454E27AEC783BCAF6EB" + + "FAA03C571A487581408B9DBC2F480F262E4E8DE5F0645264BD04D0FEB39ED15D" + + "7602C58E2A896791CB739BFC671E116B8CC253F3BC84A3F421F17E9C9FE77738" + + "4916F8F8C822A2576E7CC00E0AABB9425699EBEECD5BB2D3F9A536E42D953AF8" + + "DC4C7692E333E7D5134849D42AABA751BD3FD79F66B8C138D6EF0207BF3DB900" + + "E44AEA2F112E61547BB7187B99346F0B851CAEC1DFC20773ED26CA67DA4B9319" + + "5F38E277186EA536E8BA075A1A93DEDC63AAB5B21B40A83A8954905B637807BF" + + "494A059DCC779009DD41C3EB397ECDE6057B5104B0CE675F29590FB564B426C3" + + "9E5144392B57D56326BC42807712B1FD56FE0BD5E7BC0C5CF86E8F820E0CE6E4" + + "C971E816E68CDA9C17178FF0FC6C3DCC11841D8F1B8C69881501C42F657EA8AA" + + "06F9AC1BE74D48907F7825B97CDAB88C9AE8738319F063497B7EC2E626C8A430" + + "3B301F300706052B0E03021A0414F71F2A9273499EABD45CD57C42AC2D2FD7DA" + + "4D17041406C0432C5D5A1BEDD4771A8232801BB5933EC037020207D0").HexToByteArray(); + + internal static readonly byte[] TlsClientServerEkuPfx = + PlatformSupport.IsRC2Supported + ? TlsClientServerEkuPfx_RC2ContentEncryption + : TlsClientServerEkuPfx_TripleDESContentEncryption; + internal static readonly byte[] s_RSAKeyTransfer4_ExplicitSkiCer = Convert.FromBase64String( "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhakNDQWxLZ0F3SUJBZ0lKQUppdWpocnpi" + "Sk9XTUEwR0NTcUdTSWIzRFFFQkN3VUFNR014Q3pBSkJnTlYKQkFZVEFsVlRNUk13RVFZRFZRUUlE" + @@ -1383,7 +2019,7 @@ private static class RawData "Z0NhbC9DVTE0b2tSaG5zVUxsQWw0RzBFeWorQy8zZE90alZQMjhWWFdacEJ2WXB6SWkrUHNxK1RH" + "ClEveU9MN2psMGFnT1dTcngwWnM9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"); - internal static readonly byte[] s_RSAKeyTransfer4_ExplicitSkiPfx = Convert.FromBase64String( + private static readonly byte[] s_RSAKeyTransfer4_ExplicitSkiPfx_RC2ContentEncryption = Convert.FromBase64String( "MIIJqQIBAzCCCW8GCSqGSIb3DQEHAaCCCWAEgglcMIIJWDCCBA8GCSqGSIb3DQEHBqCCBAAwggP8" + "AgEAMIID9QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQI2rQEBhq4HAUCAggAgIIDyEn+zCF3" + "wteqkmcbOFO7Aa2HbI8d1Dbp8M2QESYZhIZr/nX69etCuh4UewErHwwxfJYVQjhKO9NhWO4wB3UP" + @@ -1429,6 +2065,57 @@ private static class RawData "hkiG9w0BCRUxFgQUsdOM787p6dBPlEljE4Lazjja1NswMTAhMAkGBSsOAwIaBQAEFHcLzfQtoDAa" + "sbfisMs0Ll8CgjH2BAhMwg4eZANcOgICCAA="); + private static readonly byte[] s_RSAKeyTransfer4_ExplicitSkiPfx_TripleDESContentEncryption = Convert.FromBase64String( + "MIIJuwIBAzCCCXcGCSqGSIb3DQEHAaCCCWgEgglkMIIJYDCCBBcGCSqGSIb3DQEHBqCCBAgwggQE" + + "AgEAMIID/QYJKoZIhvcNAQcBMCQGCiqGSIb3DQEMAQMwFgQQzMqKF6TLC64Jd+DF7mgQmQICB9CA" + + "ggPIoCkOExBhWQ0rnLj6ZlwP9pkKhnC07NHHXBuRKQTLdZgQlchqjz6wMebmc+ZlwVQbaPvJmXMF" + + "QR46wbjJdbxVeIX34ng9tUyAo0mEUaL7PEl76ajS2lrfsGBSv3HmLL7+uGmY29/lxMNcTWLt8PbD" + + "S/Com9sQB2MWSwsz7NsT4FlYs8P/n89Kf5yqG6RRqcdB3GlqaFZuqNi0Ycb3kMM0urOlLAEN5U1K" + + "TCEy+YCg5+1fibgkTvLa4aJTjDsgX84bg2IuKne745RLb1kPAeLiQ3BscVyj3BRcMgujm31VqESs" + + "OeKdCwNk8bN5DE8iSx30XlhxrFANMMtl+3Ya45+W7zmJS1BvN0DSjDGP8M9ibq9/b7dTtX88xvAn" + + "OpK6Ecpq01rV5QLIuXnk5knQV0mWXEc97l0dz3gTso9xqUs7H422Eg9OOIuJQqJRpsbHB8sCWjyP" + + "1Td6fLsnhmEn/Ri3+w/QhHF1oAKaNZk39vzruJVYxZL39Ny8dVmrKx/Myn9qErLQqgcx+v+DZVEP" + + "ODFytAgJ0pfB9SaMSLqi4I6pFl7yevKt5kFif6JmiI7cvgrjaz+LCkdDY4y9WZLcQNmzKgl8+eJg" + + "BQitWgZCnk73Jwlj2KoZFATdTWr7RYx/3F3Ra8UI/eUAE0TxJXAVUgJ2VLYsEKKtlgLkKJulQ2jr" + + "49q3+q/oQDNOLLMc7hjr+B2kknmAnvmtayywK8w08Mw/0o5dPBrPG46aeLqw6McNArZASzUGyUSP" + + "8cZDDGMBilBYyGRFaw9vxANDo0y9mHJSx8Ip4Ow+VU7tng6qrlJjt8LEVWM947UGygHc2EbvwWa/" + + "gmX4OtVGuwqlANyR8Rm3cufUNGclcxt8IKRLkTRq+DQ3KTCRqZSsRfxq/pl3QDICWb2f7v6iP29I" + + "gcCDf6ujQ7sQn5gP9KdkkAA0ZJPH7PSioJRUc4zsq3EKJJYA+YfMTJVZSdrQMBgNFhHJyEfiyxbg" + + "o8Ge3gjcUx3ZnAN0saIC4/LxPfdu1OCrHGFkJDiTQeqROqbeYF5YtKaCwnlL3dn89eUicHVvl/W8" + + "WRfJS60F6KFWrAOmKjx620V5/vbMyYxA+LgAIcwdmrF9k0OtnpJqcZ9qB4ie1FhtW6UsIsPQVnN4" + + "g5JjxMLoGdMwTY0LH9YjEDdyVzjcGOMX6SVv1KyeGYuiBEfNjgik4PUAZuVqt04KO3Yp6huj9ljn" + + "WSdJVVA4NBB4FiG/PjmmVw3MMfw2lx/M/rlI1BN+hPAW207Vuesx806cqxwdV4RI0eEe5Z5vjJx7" + + "KiAwggVBBgkqhkiG9w0BBwGgggUyBIIFLjCCBSowggUmBgsqhkiG9w0BDAoBAqCCBO4wggTqMBwG" + + "CiqGSIb3DQEMAQMwDgQIXCvLnetBP6sCAggABIIEyAQ5BEIYz+6RAAQDiqsFq21jOULe2IcPSCoS" + + "HpIDG89ptoMhIwsugbRpnTKOj7AM3tqk5v2iFEjGhXJp0n1303j+c8ifVOfMdzvJANNrVyS0xISz" + + "MmArydayj4B9B6WOqoyaVOSvIyJR2n56RG7hOIPOr7N7NCnbQvDmx9NWAa4xh9NQBiQHos3ONtTD" + + "hLv+YQRyk3tsoUp9x/fGLX3JHAg7ROEsD2cXvw+rVBo5FGlS4OLbPFJpwfE3TC8zkcQhUSovr5Ts" + + "GBUBkXyvnXHX/7R2jg7VdmkNwNALX/KNbf11IXNZs0w4KbdqPztNznC6sgVbUmo+YdzTWT8qzzd2" + + "pKqxwQG17+p7PRcq0UaLNRrGJ6jRlBEeQtYTXBLu+YcjOGZhtGlKvf9Zk7y7lDl1rdzUa3kmjowC" + + "TeqneBujCCkj288Vbg0iGr5cjH2ufwF3AoilHBcYHm8rhpaH0YrgoEyTeYVXFZhrQgJ/3rZH1UzN" + + "K9ItJIgnK5bdSlvNfFBSM5Wz32grJ+4MvItA945WAEBWXmitITaFwoA3YRmkc4XjlNmxj1n4clpL" + + "ClpQMQ9y9P/0kPi4uEBwfudDkdf9Syn2c7w3nML6jMFPUenyjT6DmlvwUHGHk0XQa6PdOVDpUNcV" + + "fjWUCt0nLTitl9aQZT1AThVsysG97+/ICKp0wgnidQIbSCEyvtWMUs8PgbPv70RlLa1+3e2m7hWK" + + "Ez9PFIaCxaMWLLLlhlFTvYotn7O1m90upg5znb9fDFyDN1vajAySmvcsAk42bzJcj48WQ4BUX6+Q" + + "F1/jM0jOaNpJyy+ciErtPC+V8AcQkG7xDymQ7qynvX3RYDrA5aFvtGi8MwUwIkVofsBUbxfuK7R8" + + "PE38SpfAof9dPP/4VEYMhGaZL9W1q9ARaRSx9IorF8pP6CVn+wfKjlPoKaXcdpSRn9pbyxDuSUhb" + + "X+4N6lgTiZq0DzOegM9Pt9LlEz1wV++fdSHcqp8sHxIIECIWQNCyV/++xW8UfHEgKUDj77ShbAna" + + "sGJtbdF1A8lpyB3wEe5PLrlMxmBuS2FUEVrinc17ZztlvGFrPDQ+3kWrD/6Q6n6j1yEzEmy7vlUy" + + "PO9WkyBGi22+fdzSM+PyiWyI+fqYRahiF1BC6lNzeqaaFIdEgsdzkUFj1QqhVF2Dd9Z4Hy9/c6dB" + + "GorWS+Zz/LPf4JRf0qX33yViWn9isWmoi/0DUuTIXyXjeFdTnrK5BOBqhiIxVMERt1Dr8u7CD5kC" + + "Et+XjUbwwJJifjqZCCSj0Fiv24gWlHvzLNZsueObjS9D2Y/YET7kvEKD5ATQWvDu2ksdiuzXMgCE" + + "XD2eRlMN5OXpW85KZB6BvDm4/dVX9KSRfZ87wPNT0zmcMOYr8IKSVobLafMUAqsciGweEwvgVaOI" + + "vEDQ8aHpoET/an6f1bjmss2oI5N6OxsTktXP+VRwNdtoJIpTbwB05RJKa/p/aFhXuJ0yYI5G+mAp" + + "ma2OuPBV8alGmHtLTnvgWtLvjimwsgtv5pIZqI0E84WIFFFxSE/JU0v5jhw6P80ymBboSxSlalMk" + + "Cv/V2eMUxvOXPVvgHwpmR5CXeyZclonP/uhTWMn3pBFicZHLHo1hc6+76/dvnqDcujwy8EXn124M" + + "1zElMCMGCSqGSIb3DQEJFTEWBBSx04zvzunp0E+USWMTgtrOONrU2zA7MB8wBwYFKw4DAhoEFDV/" + + "r3z/utYI1K4Y+CvDRILGEGbbBBQMvVmizsCIdljG6t477/4bInm7XQICB9A="); + + internal static readonly byte[] s_RSAKeyTransfer4_ExplicitSkiPfx = + PlatformSupport.IsRC2Supported + ? s_RSAKeyTransfer4_ExplicitSkiPfx_RC2ContentEncryption + : s_RSAKeyTransfer4_ExplicitSkiPfx_TripleDESContentEncryption; + internal static readonly byte[] s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Cer = Convert.FromBase64String( "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURhakNDQWxLZ0F3SUJBZ0lKQUs0cjJQai96" + "ZnF2TUEwR0NTcUdTSWIzRFFFQkN3VUFNR014Q3pBSkJnTlYKQkFZVEFsVlRNUk13RVFZRFZRUUlE" + @@ -1453,7 +2140,7 @@ private static class RawData "R2o2cUpuRWhTaDBXVkdwMlRmMFhRaXBTTDk2REVCNlF2NlF4T3hpb0xNNG9zY2tKUEdhTGl5cmNj" + "CkZDUFYveUJyVm5Md0ZHR3NheFk9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"); - internal static readonly byte[] s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Pfx = Convert.FromBase64String( + private static readonly byte[] s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Pfx_RC2ContentEncryption = Convert.FromBase64String( "MIIJqQIBAzCCCW8GCSqGSIb3DQEHAaCCCWAEgglcMIIJWDCCBA8GCSqGSIb3DQEHBqCCBAAwggP8" + "AgEAMIID9QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQITLms5nr095wCAggAgIIDyPyxib5c" + "oCEPC3OpfmCzzieARKf56sQZU31qlDJNDet7v1R68T7X9vu236kHOphKvqedPVNXKEPpaQXIxkSY" + @@ -1499,6 +2186,57 @@ private static class RawData "hkiG9w0BCRUxFgQU++MZ+0nVwxEM/9SdcPKsQcAk4ycwMTAhMAkGBSsOAwIaBQAEFFNlHKZQXjbm" + "f0PE6idyhcH963CyBAiMLpuBSuzCdgICCAA="); + private static readonly byte[] s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Pfx_TripleDESContentEncryption = Convert.FromBase64String( + "MIIJuwIBAzCCCXcGCSqGSIb3DQEHAaCCCWgEgglkMIIJYDCCBBcGCSqGSIb3DQEHBqCCBAgwggQE" + + "AgEAMIID/QYJKoZIhvcNAQcBMCQGCiqGSIb3DQEMAQMwFgQQcikkI3mNYPzp9KgATYd+sgICB9CA" + + "ggPI8jMlpeNA98igwqedfn4BlSDksVxqex+KPgGDn/UYx/S5yfQMUxq2vocr3mXGBGm5B7/4v7mV" + + "X0CbLu32ngd+lWovtp/n+DyNdsLiVpAasjY07F1ozdtJQ9ANBgkz5PH5h/s2tzvcUDteIyclZ6X9" + + "UWuwY9/LnnwQKfZAnvdMuYe9Cvygt2hEvp9xJSnotH5b74JcKGDm+VXhyDL9Ls2hvyjg8/AonfeX" + + "t8MimMDX4PL4DruRbm8bFTU1SP0io9OLenZ8P9QJ1oEzIk5n30taxfMonQcImaBHLnugE9HeREfT" + + "pPUlAjkLQR7muqnsabCMq9hDYNwdts+lF9cgKWO78VBDOgcfwJ+X3HdtVp+dfah9MDftC5PyuQE2" + + "VY85CoPqMzsvk9afgxOdhIR9n/LmPZM+cc1uyXhdU9+mDUP7jJFnO+36uFn8cUDy1C6d1TqHZbFo" + + "Lh/R2+Jf/txawMfGvanN8K6z4DPv5y6ARMBzsiIAsdfIQVWS397yJT5kKPcfDQb7icbpFdta/efV" + + "sIbwz/2Yvku9+ipccGgqPjhAvBj1m549L/UM0+Oumm3iltDO4GYdWDHWR72rFEDN3wurrFgPADZk" + + "3qrwH0T/GIUGYZeF1Kp7XEEgXcB2MCQiwwkVWy3HBVXL5RQFhFfPGSixXu1JPeugLGEsIlm3Jv/C" + + "7D6v4aPfa2SpEAebYhSvfN3t/0J1cFzUeN4XVt0g44t2XnFDGbJVEG+/7odsArL/jYsp6jmR02Hv" + + "0vhzA+4qyDEtZPSOWtiYRIE6g6rKfsrGZ8lVf/M+0jgXOSW4spoZEBtHoIeycQSvvPN3kLH9alcu" + + "fJYf1PtQGFKR7EdGsJ88Ho5e8lzOn3Ka7X/M+vegIxdnmPclv6yD3FFCRS2r2DD31a1dJfWVO67x" + + "ydXuhWEE20/rbEmblVHpoG9pSXrn7A4NV9Wyetwd/UE8XvGocuLFwXyLikHYs+KDTa7OW5BFebox" + + "72QuNhQteU7ay+1i/rfhNvvqq+B0MALQAamAVVz/ynsdsRuszikcALK9Zofq0ap51ZpVcqKXI82V" + + "9Gg0LdmH8AicB+RWj18MeEEqVKJAzbU7QJGmG4kNgZxSPbfAWek6axEB1SoKFlq+C+HcPl2O/r8n" + + "WEeNd4lDp1v9cFbJ0XEnoAf/0yd7jDBr9DtMNyamW4wBvoCWgmAeF7ojuztitsy9Cu2Lh8r/idR4" + + "dA9gZCI6kXw0pQ86ud2X1p41goxHcHa5ic0oHj3jZAYh1X6PPUo1IGg541jdnhQCk2hVmkIwCh+g" + + "X78wggVBBgkqhkiG9w0BBwGgggUyBIIFLjCCBSowggUmBgsqhkiG9w0BDAoBAqCCBO4wggTqMBwG" + + "CiqGSIb3DQEMAQMwDgQIhPMgDcNUCjACAggABIIEyFQXuZYiQ56H/jN32fvWI20eDJkib3RwXfVh" + + "EdLezIZywAgHgsfTCce2EbW7kZoSFOMzMFOv3qEWQ4XCYJb7wpuDbmghzLXZqt6fZGfYs+SgeaZX" + + "9oDmi3ttuwra9OBtp7gZszBdvSgEwotTfDMpxjKW6FUZWR63b6zNQdSwwpnGEVROGS9mqc0LUFgo" + + "OJ2II/SekRtuBfgBEEQH75svTVgW9Btf/vYlI8pO29OzdqhhOV+iSXBSvFLDrT/Bif7fLwpXIEiR" + + "1koNUpLQBsW40RBlibJ/gCJK/uriHpYp/Aj2iOXvC/2TT+H9r7H7ysAhMd1f9lpI1yahZr/MIpY0" + + "75ie2+tHEtnEYTi3dL/iHfZtuj50MPmIu5y5QZUwW3pv0IGmHz5Bghu17fONA+M9ZjVh7FEjguqI" + + "rn/dkUJE8koSrhtiaG6vfhGZD29nLs18A2Bb6UlWK3W984weNCPray+RtpsyNWtZWGekAnuOEGg1" + + "HNuCBj8SGGobkFdjp8SPA9YWK8czSCaYFflWUJOaElyTawInispKlz1kHgnq5w8ffAcTI7UcgOAH" + + "zIjju/gJl4FfJ8iujOK8+fs4KYi0Pi1R0fvDj8EnE04xn4amjPm0Zah4xw9bNvcmhQEmClntieoV" + + "sbwjWVOUbo8RuE27Je0HzdHrkIABK14fDDs8x9kOZRA78onA2+ZkU82jHgihxMot/tw4xe3o1HmE" + + "9Clz+15wN25DNAbWcQhOrQw/wTHdhCC7fuB+lg2JECSLvIqvDNinBWtRLBqnRE8ifj5THCgg1eeD" + + "BlOY2SFcSv6x9by6IHw/lmuGoX710IYEcoADLfk0wQZdccrv1g6uc/ao1/wht+einCdk1hKeIoUX" + + "te8+aOJUA1oBFDzBYhRuGzB5kjP13PQYfuZ2TzvujSTXGuqldqAB5oXvDADP8csVsgbj5dcPl8WO" + + "SpoWm5NiM60WCd433BcPCC9BbKo9uPH+QkEPmT5QBbWvXP4sPAP9V08wnlt19ZrlAUIjFbDXftWy" + + "J4Zc1m8HntiF0jYPcsqT7QOEYmMZPHLqD9lPC8ZSU897bT4inb7NKgqETZhUEBuzLfr5yvvtu44+" + + "Dwvlo2pSDmWshh7l4sAMuDxSdq/LnjJIjLalnSBfYbZ4ZB5vAAhASoNgEhFTKISFuMYqmtNNFCPo" + + "ejcz+aYc4ox8dl5ZP4EY4BrT54LPjfpBLtixeXYA7AUGoMjpk41hMFG7ZYu+4redvBv6HVG/r+l3" + + "s0YjoBckEonX6IEsaXkcGg4hiakb7IaBqNcZcNWqREYRJSeslAaVdvCrTQwW+0psYj36JAGDDilu" + + "tTNznEh3Q5nXKdJNisrNoZ+LUqoyPZ1BtD5vRKyEP8FWsG1hhUSdj0to+iQedrRxhxQZfGfJx6yT" + + "YC/SRDOISYe+W1VkOPUvIwEnMX9q4dj49vHg0g3fd850pcHJBITi6VmqCx+2fvsHbnLHCM7ZKSz5" + + "6yzv6kYFs7WKQ6qbrOiX3aXBGJuPxttu9rzpJ3fJ3zjpjFzhBFxKV1M8w5CwGt+zxl626xEKI41d" + + "v6AoOd79n8273iaTEyQm8tEkc96y82TGJdiELSZIWX1ENKQiCtEna5FLG/RXk51wCIKLd33w6Wsh" + + "iTElMCMGCSqGSIb3DQEJFTEWBBT74xn7SdXDEQz/1J1w8qxBwCTjJzA7MB8wBwYFKw4DAhoEFCyj" + + "rTpupcG3BPfLBuT5GlHPVA7IBBRe4QUbn/DUvN/IRNbZin9j7WlOtgICB9A="); + + internal static readonly byte[] s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Pfx = + PlatformSupport.IsRC2Supported + ? s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Pfx_RC2ContentEncryption + : s_RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4Pfx_TripleDESContentEncryption; + internal static readonly byte[] NegativeSerialNumberCert = ( "308202C2308201AAA0030201020210FD319CB1514B06AF49E00522277E43C830" + "0D06092A864886F70D01010B05003014311230100603550403130953656C6620" + @@ -1524,7 +2262,7 @@ private static class RawData "9240E6FC0F2C8933F1844AD5F54E504DDAA3E87AE843C298DED1761035D2DFBF" + "61E1B8F20930").HexToByteArray(); - internal static readonly byte[] NegativeSerialNumberPfx = ( + private static readonly byte[] NegativeSerialNumberPfx_RC2ContentEncryption = ( "308209C40201033082098406092A864886F70D010701A0820975048209713082" + "096D3082060E06092A864886F70D010701A08205FF048205FB308205F7308205" + "F3060B2A864886F70D010C0A0102A08204FE308204FA301C060A2A864886F70D" + @@ -1605,6 +2343,92 @@ private static class RawData "B6B4257EBA3E399CA9EDD87BDC1F82FBF7D70414B796F49E612D1A2B5A0DEC11" + "608A153B5BCD4FE9").HexToByteArray(); + private static readonly byte[] NegativeSerialNumberPfx_TripleDESContentEncryption = ( + "308209C80201033082098406092A864886F70D010701A0820975048209713082" + + "096D3082060E06092A864886F70D010701A08205FF048205FB308205F7308205" + + "F3060B2A864886F70D010C0A0102A08204FE308204FA301C060A2A864886F70D" + + "010C0103300E0408EADBD31BF1DB9D12020207D0048204D860781235FA36052B" + + "1142E6E63AB86FAAB05C8522758F20D4104714E6C756173419C61A7A05CF57B2" + + "8B716B01FA2509DD8F57226706BF8678D11AC4C76369CACF2A54A9E5A53BC658" + + "675F326CC565CC7127BC50DA682DE3F1C57921FEA921F797370C07B194936A5E" + + "23F0583B4E911DBED2D0ACE32190CB0513AEFF385B046D46EDE7BDDD1377A807" + + "1B1D1AFA5400DF1E3E4DC2177617DB23D81F15B51CA1697625D8208FBA1F1548" + + "B3CEA06D62A7F947997923A790608D6C11F4EA294BF5C394276C4988A1B95C51" + + "BDE8317CCB2EF77FC20EDB1993260F7D581E89E03E0EA93569CF39BFFB531FF0" + + "D51C5773661B5F8FB37E34FA847D67A1D8B3A17F4F67D123C81CA84C01974E18" + + "07B6BA9AAFB816565E20F91E4D2D70D31EA8C523423AE4621ECFE6026882B6CE" + + "78D858D518B1C52D49AFE4442E2FCD81E961153F5ED85E4B17ADB7C25AB71101" + + "42B97C09297002058A0180E88AEDFF37910A2DCB275380E428B6CDE0707DE988" + + "37732DE2C7817AEDCC872C0E0E8862DC553DEE8224A196840B1AAF82EFBBD988" + + "4E37DA2BCC8EA859B2671947F6863034360F0F58E736BD2B5995C34C44E098DC" + + "9B340E08F2A1CF40DDC0F6407212630120203DB17F27EF11042BEBAFECAEC5BA" + + "5176468C5BEFEEC1233F8AD9F691F0D09BBD9C9D7E48E036FE14BFBA460885C2" + + "3C417CBB0C58B962EEB960AE9FCFDD8BA51CF2056B26C506B0A2B46E159D19E0" + + "EF9678B696A25DFCE06F6CB71263B7D7CEA23997A32EE148BB03B2BB5563FB76" + + "AF0E10E428281EAA83AE16F9915C9D4D835277556193A3C1A3E3737A7B596F19" + + "4AE3C2A5CE05A36F0531F8A173217DCEC91E328AE643ED721ADD3585DF2AB832" + + "6D27066F701C329C6F3D43AAD1BE51E7DCA157EBDCF082C889D26CB91A54EC45" + + "C84D67F291BD7B16DF7C590CD1FE2172A93C8693954F15EF913DE032CA677E30" + + "03A00606CA194B1C02CCEA873730FA1EACE02CDAC838D9C6EFA2467EBD4FFF64" + + "E1404F636068151DC4C840055816EC1C91BB6B7F5166AC2758BFE7B02A8402E3" + + "6857DDEF537CBBDDEDD7B0461602147BC453FA5637D826273C9AA47DA96E94C7" + + "A0ECFB5316A551DCD7FCEE25288CB5EC26F919915A1B724774ADFBEC3A9B44F9" + + "5C838366BE34A8FDE892CF8286B90B45005E0980114A19D351DDD0EF5D45005F" + + "0570C019E94622A79E0122B72B15EC6F011C0C5E4CCB12CF99164D5B4ACEF3D2" + + "8C4F33773FD3C71A3F7CED5013887A4FB3804D487ED9FE8969D80F3705FBFF61" + + "1D9BC5CF0101AC321609EBD735A8BE23F1CFA01087212714AA7A035D7BA1028F" + + "30EE685E66E943105218FBF9F7ACD5969851D48405B8533754AD8CA5836F7B18" + + "7EAD45C7CE0603798FFBF6F58DDE2392BDDE986AB050D00953684CB7FCAC2151" + + "EF293100E894C5AC5CF44C2795C41BBDFB9C9252BE289B9ECC6822B97883E765" + + "D958EA9AD84ACB9601930B4FB9CCE8F69C771D5D02C4CF9FC5EC9394B01C6CED" + + "60500F317E4D5AEBCE5744EC8DDD5A03BB28072023CA7F7310BF04167692B03D" + + "763169E142644DC2CEB369DA9C03411DE4C4E4FAD2532DE81EE3E7151DAA94F1" + + "BCD89071F94B0DA91139064912FD697AB520F0AF8F8C938B50EF1CDD9A54C974" + + "E6665851C42485E82A5E98E1E278309F45F3AA198D22DBC16321DE04E26E7109" + + "1148E687B5D8869AB1EF942083DC2AFF863C251B0936FB73E4849D1ECB553497" + + "7D334F3648F9C2A21A40C355FC11D7673181E1301306092A864886F70D010915" + + "3106040401000000304F06092A864886F70D01091431421E4000380038003100" + + "3800350063003300300061006100660038003400370036003500610037003700" + + "32006600610036003400350035003900630062003400320064307906092B0601" + + "040182371101316C1E6A004D006900630072006F0073006F0066007400200045" + + "006E00680061006E006300650064002000520053004100200061006E00640020" + + "004100450053002000430072007900700074006F006700720061007000680069" + + "0063002000500072006F007600690064006500723082035706092A864886F70D" + + "010706A0820348308203440201003082033D06092A864886F70D010701301C06" + + "0A2A864886F70D010C0103300E0408F2C9235120339743020207D08082031087" + + "A84399911830599C956CB78AC54D2AD6F3D3D90A180C9F71079DD90287703FF2" + + "82E74E6FFE0F4C4335E2A3547EC8B4AFE5B618C5DB15548879B7CC4A2E56D514" + + "FA7231F6EDB429385C6C8E44EDD9303089236371C2ED982D5F7EE32CA63B3A6D" + + "F1D43DD9BFF4133AE4DFEDE56FC96CA3603A861EA52B9B6D21EAFA961ED97E53" + + "5ED290A462912AAA621185F9E1339305AD7D2039120FD213A4063FDFF4DBFE14" + + "FBF580ECA3A6DFE956B96A737962A2018F3525A472A90E133F022E5FCEEE25AA" + + "CE9635182659049471BA03C5573E2F68E3F4484796669A9BE81398C232C0247D" + + "AA8615505FD394104F11E7749227239628F666B2C717CC1E4FEDDB9F91F218B3" + + "6726086B66C27CFE0395BFD150C5B868AF3D472EFF6FA8A0980EB7494AD7B059" + + "6253F65C94585D2F86FD3E70A0E49C330419D2BDE70000FEB7DFFBFCBF33E1DF" + + "12E08B3A8A6D82D9BC0AE0FC20CBACAB9C62D6C1FDE9505BA27CE77DB7C94475" + + "29EB5C3FAF3F2E806D88A1A46BF230DBF7B8E4CEF6C12DEAC6ED805F4028AB5C" + + "7CA99C23CF4E2419D8E6EEDD05BA6FDF061C3251E8E909E2DB35893AE96F3118" + + "61AE4260EADABEDB22F545A85CBCF234324C95FB171AAD72DE503F2EA1409ECC" + + "BA245A2C78DA485373E85F643C64F9E3DC4D20D971B7A960A6C76B1819BFC02A" + + "7FFCA7768D4DC36EA7FCF19C80F3B082D9BF30545C17885875C571D050EBF2D6" + + "952595043945EADE050CEA8C99433514D3B03D1F0082750BFC0965DB05444C11" + + "311EE037DBA4579CB0D9411B54613DF8FFF8407110F0E8BEB5DBB4D4E286455F" + + "EA748C5BCFD72B7BBFC1B960DF08C9601D5C49B5BF747B029DF87FD3255BA9A4" + + "0224F2B97E8770BA23F1C8A04FD099E63AA05A172D883FA4CD6298479046B4B4" + + "DB0A4CC8C72ED0BDAF6E846F795705C6D330E4A50363003CD23C55655AC332B1" + + "30F6BF625896957877DC5C4B8D6D8357764DFF6FDDAE19D2B002062FC54A75C1" + + "45E0C85678210CAA04B4E6A8B5C88F596DD769EF484927096415BAD1355DF038" + + "DC4C150F67E6B2A3FDADCBD6DB1081ED29868CDDCB4EECC83B2ADBABB14AD09D" + + "E8D0E90ECE88FCD440959F706B4028303B301F300706052B0E03021A0414E431" + + "D7F51D316EB967B5AC6CE0632A882B4F61F0041404CA58CF8EBA5473BFB0AFEF" + + "96D11C7E403BB3F3020207D0").HexToByteArray(); + + internal static readonly byte[] NegativeSerialNumberPfx = + PlatformSupport.IsRC2Supported + ? NegativeSerialNumberPfx_RC2ContentEncryption + : NegativeSerialNumberPfx_TripleDESContentEncryption; + internal static byte[] RsaOaep2048_NullParametersCert = ( "3082032830820210A003020102021000B2DB376E004B56F8089A1034C37D7330" + "0D06092A864886F70D01010B050030173115301306035504030C0C436F6E746F" + diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/ContentEncryptionAlgorithmTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/ContentEncryptionAlgorithmTests.cs index 0d0da026045be..7842c0f7f4030 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/ContentEncryptionAlgorithmTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/ContentEncryptionAlgorithmTests.cs @@ -11,10 +11,11 @@ namespace System.Security.Cryptography.Pkcs.EnvelopedCmsTests.Tests { public static partial class ContentEncryptionAlgorithmTests { + public static bool SupportsRc2 => PlatformSupport.IsRC2Supported; public static bool SupportsRc4 => PlatformDetection.IsWindows; public static bool DoesNotSupportRc4 => !SupportsRc4; - [Fact] + [ConditionalFact(nameof(SupportsRc2))] public static void EncryptionAlgorithmRc2_InvalidKeyLength() { // For .NET Framework compat, variable key length ciphers throw an error if the key length provided @@ -29,7 +30,7 @@ public static void EncryptionAlgorithmRc2_InvalidKeyLength() } } - [Fact] + [ConditionalFact(nameof(SupportsRc2))] public static void DecodeAlgorithmRc2_128_RoundTrip() { AlgorithmIdentifier algorithm = new AlgorithmIdentifier(new Oid(Oids.Rc2)); @@ -88,7 +89,7 @@ public static void DecodeAlgorithmRc2_40_FixedValue() Assert.Equal(40, algorithm.KeyLength); } - [Fact] + [ConditionalFact(nameof(SupportsRc2))] [OuterLoop(/* Leaks key on disk if interrupted */)] public static void DecodeAlgorithmRc2_40_RoundTrip() { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTestsUsingCertWithPrivateKey.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTestsUsingCertWithPrivateKey.cs index 425ead3c2bc57..3c84d6e9d354d 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTestsUsingCertWithPrivateKey.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/EnvelopedCms/DecryptTestsUsingCertWithPrivateKey.cs @@ -67,6 +67,60 @@ public static void DecryptMultipleRecipients() Assert.Equal(content, contentInfo.Content); } + [Fact] + public static void DecryptSuccesfullyWithWrongKeyProducesInvalidSymmetricKey() + { + using (X509Certificate2 wrongRecipient = Certificates.RSAKeyTransfer5_ExplicitSkiOfRSAKeyTransfer4.TryGetCertificateWithPrivateKey()) + { + // This is an enveloped CMS that is encrypted with one RSA key recipient + // but does not fail when decrypting the content encryption key (CEK). + // Though it did not fail, the CEK is wrong and cannot decrypt the data + // for the recipient. It might decrypt the CEK to a key that is an invalid + // for the symmetric algorithm, like a 120-bit key for AES. For that case, + // the symmetric decryption would throw an ArgumentException, not a + // CryptographicException. This tests that circumstance where we need to + // wrap the ArgumentException with a CryptographicException. + // + // This content can be re-created with trial-and-error with the managed PAL. + // + // Two certificates with the same SKI are required. + // using X509Certificate2 cert1 = ... + // using X509Certificate2 cert2 = ... + // while (true) { + // EnvelopedCms ecms = new EnvelopedCms(..); + // CmsRecipient recipient = new CmsRecipient(SubjectIdentifierType.SubjectKeyIdentifier, cert1); + // ecms.Encrypt(recipient); + // byte[] encoded = ecms.Encode(); + // ecms = new EnvelopedCms(); + // ecms.Decode(encoded); + // try { + // ecms.Decrypt(new X509Certificate2Collection(cert2)); + // } + // catch (CryptographicException e) when e.Message == SR.Cryptography_Cms_InvalidSymmetricKey; + // // If we get here, we've produced an EnvelopedCms with the needed criteria. + // break; + // } + // } + string encryptedContent = +"3082018806092A864886F70D010703A082017930820175020102318201303082" + +"012C0201028014B46B61938FF9864BD8494B3937DA19C9F06FA8D3300D06092A" + +"864886F70D0101010500048201008198CBAFF1C67EE634C7A32C356729F996BC" + +"E4125EE353B220A792CCFD37855B50E05916CC8EC6E25EF62B35B29620C8BF76" + +"144032854D14E3E19B613C15A26E376A2014AD3AD492F80A92F0D61910B6C416" + +"867985279CF4E26CDED351AFB84CE9E1BC105899280DB6B782688CE6B04B7003" + +"E4C53B580DD2F21A71B973C2AB70E61F1AFBDD2616FE0101BB02BCDA14881CEC" + +"037032C91FE803C76D91FA5E0A802ECB2FB2BBE71C8567F58B5B74638CD9765E" + +"F658172AAD423963784C5BE49AC01682751796F0AFE4943373981FC074F24640" + +"901201AD6884415788FC18721ECB201A60A7FE5859FF61DA8BB21D0D23593D28" + +"86896886D3507906DB58FB056953303C06092A864886F70D010701301D060960" + +"864801650304012A0410C85C553A73E9B55F98752E1133ACA645801099B22DFF" + +"6A9D8984F8B3F63079CE9265"; + EnvelopedCms ecms = new EnvelopedCms(); + ecms.Decode(encryptedContent.HexToByteArray()); + Assert.ThrowsAny(() => ecms.Decrypt(new X509Certificate2Collection(wrongRecipient))); + } + } + [Fact] public static void DecryptUsingCertificateWithSameSubjectKeyIdentifierButDifferentKeyPair() { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12Documents.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12Documents.cs index 51c80e09c768b..aafc3dec4c3ab 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12Documents.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs12/Pkcs12Documents.cs @@ -222,7 +222,7 @@ internal static class Pkcs12Documents "2b0e03021a05000414c429b968eeca558cc2ec486f89b78c024bdecf2804" + "087cbeafa8089685a102030927c1").HexToByteArray(); - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy credentials.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy credentials.")] internal const string OracleWalletPassword = "123Wallet"; } } diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/Rfc3161/TimestampTokenTests.cs b/src/libraries/System.Security.Cryptography.Pkcs/tests/Rfc3161/TimestampTokenTests.cs index 5532bbaa79e02..49b6b8f0c584a 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/Rfc3161/TimestampTokenTests.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/Rfc3161/TimestampTokenTests.cs @@ -828,7 +828,10 @@ private static byte[] BuildCustomToken( long accuracyMicroSeconds = (long)(TimeSpan.FromMinutes(1).TotalMilliseconds * 1000); byte[] serialNumber = BitConverter.GetBytes(DateTimeOffset.UtcNow.Ticks); - Array.Reverse(serialNumber); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(serialNumber); + } Rfc3161TimestampTokenInfo info = new Rfc3161TimestampTokenInfo( new Oid("0.0", "0.0"), diff --git a/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj b/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj index 611a17a08d91d..76ebc9ee64b08 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.Pkcs/tests/System.Security.Cryptography.Pkcs.Tests.csproj @@ -6,6 +6,8 @@ + @@ -73,4 +75,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoConfigForwarder.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoConfigForwarder.cs index e5d86ac972c34..bd8211229908c 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoConfigForwarder.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoConfigForwarder.cs @@ -4,11 +4,13 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; +[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Target = "M:System.Security.Cryptography.CryptoConfigForwarder.#cctor", + Scope = "member", + Justification = "The cctor caches the RequiresUnreferencedCode call in a delegate, and usage of that delegate is marked with RequiresUnreferencedCode.")] + namespace System.Security.Cryptography { - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Target = "M:System.Security.Cryptography.CryptoConfigForwarder.#cctor", - Justification = "The cctor caches the RequiresUnreferencedCode call in a delegate, and usage of that delegate is marked with RequiresUnreferencedCode.")] internal static class CryptoConfigForwarder { internal const string CreateFromNameUnreferencedCodeMessage = "The default algorithm implementations might be removed, use strong type references like 'RSA.Create()' instead."; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/FindPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/FindPal.cs index 96b743817f8d6..0cdbbe6555ebe 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/FindPal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/FindPal.cs @@ -10,7 +10,7 @@ namespace Internal.Cryptography.Pal { - internal partial class FindPal + internal sealed partial class FindPal { private const int NamedKeyUsageFlagsCount = 9; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs index 740b878ffcfb8..658c2da9e1a76 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/AndroidCertificatePal.cs @@ -121,12 +121,12 @@ private static ICertificatePal ReadPkcs12(ReadOnlySpan rawData, SafePasswo } } - private AndroidCertificatePal(SafeX509Handle handle) + internal AndroidCertificatePal(SafeX509Handle handle) { _cert = handle; } - private AndroidCertificatePal(SafeX509Handle handle, SafeKeyHandle privateKey) + internal AndroidCertificatePal(SafeX509Handle handle, SafeKeyHandle privateKey) { _cert = handle; _privateKey = privateKey; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/ChainPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/ChainPal.cs index 00bfde848bd47..1af847e0defe9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/ChainPal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/ChainPal.cs @@ -4,9 +4,12 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + namespace Internal.Cryptography.Pal { internal sealed partial class ChainPal @@ -35,7 +38,363 @@ public static IChainPal BuildChain( TimeSpan timeout, bool disableAia) { - throw new NotImplementedException(nameof(BuildChain)); + var chainPal = new AndroidCertPath(); + try + { + chainPal.Initialize(cert, extraStore, customTrustStore, trustMode); + chainPal.Evaluate(verificationTime, applicationPolicy, certificatePolicy, revocationMode, revocationFlag); + } + catch + { + chainPal.Dispose(); + throw; + } + + return chainPal; + } + + private sealed class AndroidCertPath : IChainPal + { + public X509ChainElement[]? ChainElements { get; private set; } + public X509ChainStatus[]? ChainStatus { get; private set; } + + public SafeX509ChainHandle? SafeHandle => null; + + private SafeX509ChainContextHandle? _chainContext; + private bool _isValid; + + public void Dispose() + { + if (_chainContext != null) + { + _chainContext.Dispose(); + } + } + + public bool? Verify(X509VerificationFlags flags, out Exception? exception) + { + Debug.Assert(_chainContext != null); + exception = null; + + if (!_isValid) + { + // There is no way to bypass certain validation - time, trusted root, name, + // policy constraint - on Android. It will not build any chain without these + // all being valid. This will be an empty chain with PartialChain status. + Debug.Assert(ChainElements!.Length == 0); + Debug.Assert(ChainStatus!.Length > 0 && (ChainStatus[0].Status & X509ChainStatusFlags.PartialChain) == X509ChainStatusFlags.PartialChain); + return false; + } + + return ChainVerifier.Verify(ChainElements!, flags); + } + + internal void Initialize( + ICertificatePal cert, + X509Certificate2Collection? extraStore, + X509Certificate2Collection customTrustStore, + X509ChainTrustMode trustMode) + { + List extraCertHandles = new List() { ((AndroidCertificatePal)cert).SafeHandle }; + if (extraStore != null) + { + foreach (X509Certificate2 extraCert in extraStore) + { + extraCertHandles.Add(((AndroidCertificatePal)extraCert.Pal).SafeHandle); + } + } + + Debug.Assert( + trustMode == X509ChainTrustMode.System || trustMode == X509ChainTrustMode.CustomRootTrust, + "Unsupported trust mode. Only System and CustomRootTrust are currently handled"); + + List customTrustCertHandles = new List(); + bool useCustomRootTrust = trustMode == X509ChainTrustMode.CustomRootTrust; + if (useCustomRootTrust && customTrustStore != null) + { + foreach (X509Certificate2 custom in customTrustStore) + { + SafeHandle certHandle = ((AndroidCertificatePal)custom.Pal).SafeHandle; + if (custom.SubjectName.RawData.ContentsEqual(custom.IssuerName.RawData)) + { + // Add self-issued certs to custom root trust cert + customTrustCertHandles.Add(certHandle); + } + else + { + // Add non-self-issued certs to extra certs + extraCertHandles.Add(certHandle); + } + } + } + + int extraIdx = 0; + int customIdx = 0; + try + { + IntPtr[] extraCerts = new IntPtr[extraCertHandles.Count]; + for (extraIdx = 0; extraIdx < extraCertHandles.Count; extraIdx++) + { + SafeHandle handle = extraCertHandles[extraIdx]; + bool addedRef = false; + handle.DangerousAddRef(ref addedRef); + extraCerts[extraIdx] = handle.DangerousGetHandle(); + } + + _chainContext = Interop.AndroidCrypto.X509ChainCreateContext( + ((AndroidCertificatePal)cert).SafeHandle, + extraCerts, + extraCerts.Length); + + if (useCustomRootTrust) + { + // Android does not support an empty set of trust anchors + if (customTrustCertHandles.Count == 0) + { + throw new PlatformNotSupportedException(SR.Chain_EmptyCustomTrustNotSupported); + } + + IntPtr[] customTrustCerts = new IntPtr[customTrustCertHandles.Count]; + for (customIdx = 0; customIdx < customTrustCertHandles.Count; customIdx++) + { + SafeHandle handle = customTrustCertHandles[customIdx]; + bool addedRef = false; + handle.DangerousAddRef(ref addedRef); + customTrustCerts[customIdx] = handle.DangerousGetHandle(); + } + + int res = Interop.AndroidCrypto.X509ChainSetCustomTrustStore(_chainContext, customTrustCerts, customTrustCerts.Length); + if (res != 1) + { + throw new CryptographicException(); + } + } + } + finally + { + for (extraIdx -= 1; extraIdx >= 0; extraIdx--) + { + extraCertHandles[extraIdx].DangerousRelease(); + } + + for (customIdx -= 1; customIdx >= 0; customIdx--) + { + customTrustCertHandles[customIdx].DangerousRelease(); + } + } + } + + internal void Evaluate( + DateTime verificationTime, + OidCollection applicationPolicy, + OidCollection certificatePolicy, + X509RevocationMode revocationMode, + X509RevocationFlag revocationFlag) + { + Debug.Assert(_chainContext != null); + + long timeInMsFromUnixEpoch = new DateTimeOffset(verificationTime).ToUnixTimeMilliseconds(); + _isValid = Interop.AndroidCrypto.X509ChainBuild(_chainContext, timeInMsFromUnixEpoch); + if (!_isValid) + { + // Android always validates name, time, signature, and trusted root. + // There is no way bypass that validation and build a path. + ChainElements = Array.Empty(); + + Interop.AndroidCrypto.ValidationError[] errors = Interop.AndroidCrypto.X509ChainGetErrors(_chainContext); + var chainStatus = new X509ChainStatus[errors.Length]; + for (int i = 0; i < errors.Length; i++) + { + Interop.AndroidCrypto.ValidationError error = errors[i]; + chainStatus[i] = ValidationErrorToChainStatus(error); + Marshal.FreeHGlobal(error.Message); + } + + ChainStatus = chainStatus; + return; + } + + byte checkedRevocation; + int res = Interop.AndroidCrypto.X509ChainValidate(_chainContext, revocationMode, revocationFlag, out checkedRevocation); + if (res != 1) + throw new CryptographicException(); + + X509Certificate2[] certs = Interop.AndroidCrypto.X509ChainGetCertificates(_chainContext); + List overallStatus = new List(); + List[] statuses = new List[certs.Length]; + + // Android will stop checking after the first error it hits, so we track the first + // instances of revocation and non-revocation errors to fix-up the status of elements + // beyond the first error + int firstNonRevocationErrorIndex = -1; + int firstRevocationErrorIndex = -1; + Dictionary> errorsByIndex = GetStatusByIndex(_chainContext); + foreach (int index in errorsByIndex.Keys) + { + List errors = errorsByIndex[index]; + for (int i = 0; i < errors.Count; i++) + { + X509ChainStatus status = errors[i]; + AddUniqueStatus(overallStatus, ref status); + } + + // -1 indicates that error is not tied to a specific index + if (index != -1) + { + statuses[index] = errorsByIndex[index]; + if (errorsByIndex[index].Exists(s => s.Status == X509ChainStatusFlags.Revoked || s.Status == X509ChainStatusFlags.RevocationStatusUnknown)) + { + firstRevocationErrorIndex = Math.Max(index, firstRevocationErrorIndex); + } + else + { + firstNonRevocationErrorIndex = Math.Max(index, firstNonRevocationErrorIndex); + } + } + } + + if (firstNonRevocationErrorIndex > 0) + { + // Assign PartialChain to everything from the first non-revocation error to the end certificate + X509ChainStatus partialChainStatus = new X509ChainStatus + { + Status = X509ChainStatusFlags.PartialChain, + StatusInformation = SR.Chain_PartialChain, + }; + AddStatusFromIndexToEndCertificate(firstNonRevocationErrorIndex - 1, ref partialChainStatus, statuses, overallStatus); + } + + if (firstRevocationErrorIndex > 0) + { + // Assign RevocationStatusUnknown to everything from the first revocation error to the end certificate + X509ChainStatus revocationUnknownStatus = new X509ChainStatus + { + Status = X509ChainStatusFlags.RevocationStatusUnknown, + StatusInformation = SR.Chain_RevocationStatusUnknown, + }; + AddStatusFromIndexToEndCertificate(firstRevocationErrorIndex - 1, ref revocationUnknownStatus, statuses, overallStatus); + } + + if (revocationMode != X509RevocationMode.NoCheck && checkedRevocation == 0) + { + // Revocation checking was requested, but not performed (due to basic validation failing) + // Assign RevocationStatusUnknown to everything + X509ChainStatus revocationUnknownStatus = new X509ChainStatus + { + Status = X509ChainStatusFlags.RevocationStatusUnknown, + StatusInformation = SR.Chain_RevocationStatusUnknown, + }; + AddStatusFromIndexToEndCertificate(statuses.Length - 1, ref revocationUnknownStatus, statuses, overallStatus); + } + + if (!IsPolicyMatch(certs, applicationPolicy, certificatePolicy)) + { + // Assign NotValidForUsage to everything + X509ChainStatus policyFailStatus = new X509ChainStatus + { + Status = X509ChainStatusFlags.NotValidForUsage, + StatusInformation = SR.Chain_NoPolicyMatch, + }; + AddStatusFromIndexToEndCertificate(statuses.Length - 1, ref policyFailStatus, statuses, overallStatus); + } + + X509ChainElement[] elements = new X509ChainElement[certs.Length]; + for (int i = 0; i < certs.Length; i++) + { + X509ChainStatus[] elementStatus = statuses[i] == null ? Array.Empty() : statuses[i].ToArray(); + elements[i] = new X509ChainElement(certs[i], elementStatus, string.Empty); + } + + ChainElements = elements; + ChainStatus = overallStatus.ToArray(); + } + + private static void AddStatusFromIndexToEndCertificate( + int index, + ref X509ChainStatus statusToSet, + List[] statuses, + List overallStatus) + { + AddUniqueStatus(overallStatus, ref statusToSet); + for (int i = index; i >= 0; i--) + { + if (statuses[i] == null) + { + statuses[i] = new List(); + } + + AddUniqueStatus(statuses[i], ref statusToSet); + } + } + + private static void AddUniqueStatus(List list, ref X509ChainStatus status) + { + X509ChainStatusFlags statusFlags = status.Status; + string statusInfo = status.StatusInformation; + if (!list.Exists(s => s.Status == statusFlags && s.StatusInformation == statusInfo)) + { + list.Add(status); + } + } + + private static Dictionary> GetStatusByIndex(SafeX509ChainContextHandle ctx) + { + var statusByIndex = new Dictionary>(); + Interop.AndroidCrypto.ValidationError[] errors = Interop.AndroidCrypto.X509ChainGetErrors(ctx); + for (int i = 0; i < errors.Length; i++) + { + Interop.AndroidCrypto.ValidationError error = errors[i]; + X509ChainStatus chainStatus = ValidationErrorToChainStatus(error); + Marshal.FreeHGlobal(error.Message); + + if (!statusByIndex.ContainsKey(error.Index)) + { + statusByIndex.Add(error.Index, new List()); + } + + statusByIndex[error.Index].Add(chainStatus); + } + + return statusByIndex; + } + + private static X509ChainStatus ValidationErrorToChainStatus(Interop.AndroidCrypto.ValidationError error) + { + X509ChainStatusFlags statusFlags = (X509ChainStatusFlags)error.Status; + Debug.Assert(statusFlags != X509ChainStatusFlags.NoError); + + return new X509ChainStatus + { + Status = statusFlags, + StatusInformation = Marshal.PtrToStringUni(error.Message) + }; + } + + private static bool IsPolicyMatch( + X509Certificate2[] certs, + OidCollection? applicationPolicy, + OidCollection? certificatePolicy) + { + bool hasApplicationPolicy = applicationPolicy != null && applicationPolicy.Count > 0; + bool hasCertificatePolicy = certificatePolicy != null && certificatePolicy.Count > 0; + + if (!hasApplicationPolicy && !hasCertificatePolicy) + return true; + + List certsToRead = new List(certs); + CertificatePolicyChain policyChain = new CertificatePolicyChain(certsToRead); + if (hasCertificatePolicy && !policyChain.MatchesCertificatePolicies(certificatePolicy!)) + { + return false; + } + + if (hasApplicationPolicy && !policyChain.MatchesApplicationPolicies(applicationPolicy!)) + { + return false; + } + + return true; + } } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.AndroidKeyStore.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.AndroidKeyStore.cs new file mode 100644 index 0000000000000..898d79c292da3 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.AndroidKeyStore.cs @@ -0,0 +1,159 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + private sealed class AndroidKeyStore : IStorePal + { + private readonly bool _readOnly; + private readonly SafeX509StoreHandle _keyStoreHandle; + + public static AndroidKeyStore OpenDefault(OpenFlags openFlags) + { + SafeX509StoreHandle store = Interop.AndroidCrypto.X509StoreOpenDefault(); + if (store.IsInvalid) + { + store.Dispose(); + throw new CryptographicException(); + } + + return new AndroidKeyStore(store, openFlags); + } + + private AndroidKeyStore(SafeX509StoreHandle keyStoreHandle, OpenFlags openFlags) + { + _keyStoreHandle = keyStoreHandle; + _readOnly = (openFlags & (OpenFlags.ReadWrite | OpenFlags.MaxAllowed)) == 0; + } + + public SafeHandle SafeHandle => _keyStoreHandle; + + public void Dispose() + { + _keyStoreHandle.Dispose(); + } + + public void Add(ICertificatePal cert) + { + if (_readOnly) + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + + AndroidCertificatePal certPal = (AndroidCertificatePal)cert; + string hashString = GetCertificateHashString(cert); + + bool success; + if (certPal.HasPrivateKey) + { + Interop.AndroidCrypto.PAL_KeyAlgorithm algorithm = certPal.PrivateKeyHandle switch + { + // The AndroidKeyStore doesn't support adding DSA private key entries in newer versions (API 23+) + // Our minimum supported version (API 21) does support it, but for simplicity, we simply block adding + // certificates with DSA private keys on all versions instead of trying to support it on two versions. + SafeDsaHandle _ => throw new PlatformNotSupportedException(SR.Cryptography_X509_StoreDSAPrivateKeyNotSupported), + SafeEcKeyHandle _ => Interop.AndroidCrypto.PAL_KeyAlgorithm.EC, + SafeRsaHandle _ => Interop.AndroidCrypto.PAL_KeyAlgorithm.RSA, + _ => throw new NotSupportedException(SR.NotSupported_KeyAlgorithm) + }; + + success = Interop.AndroidCrypto.X509StoreAddCertificateWithPrivateKey(_keyStoreHandle, certPal.SafeHandle, certPal.PrivateKeyHandle, algorithm, hashString); + } + else + { + success = Interop.AndroidCrypto.X509StoreAddCertificate(_keyStoreHandle, certPal.SafeHandle, hashString); + } + + if (!success) + throw new CryptographicException(SR.Cryptography_X509_StoreAddFailure); + } + + public void Remove(ICertificatePal cert) + { + string hashString = GetCertificateHashString(cert); + AndroidCertificatePal certPal = (AndroidCertificatePal)cert; + if (_readOnly) + { + bool containsCert = Interop.AndroidCrypto.X509StoreContainsCertificate(_keyStoreHandle, certPal.SafeHandle, hashString); + if (containsCert) + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + + // Removing a non-existent certificate is not an error + return; + } + + bool success = Interop.AndroidCrypto.X509StoreRemoveCertificate(_keyStoreHandle, certPal.SafeHandle, hashString); + if (!success) + throw new CryptographicException(SR.Cryptography_X509_StoreRemoveFailure); + } + + public void CloneTo(X509Certificate2Collection collection) + { + EnumCertificatesContext context = default; + context.Results = new HashSet(); + unsafe + { + bool success = Interop.AndroidCrypto.X509StoreEnumerateCertificates( + _keyStoreHandle, + &EnumCertificatesCallback, + Unsafe.AsPointer(ref context)); + if (!success) + { + throw new CryptographicException(SR.Cryptography_X509_StoreEnumerateFailure); + } + } + + foreach (X509Certificate2 cert in context.Results) + { + collection.Add(cert); + } + } + + private static string GetCertificateHashString(ICertificatePal certPal) + { + return X509Certificate.GetCertHashString(HashAlgorithmName.SHA256, certPal); + } + + private struct EnumCertificatesContext + { + public HashSet Results; + } + + [UnmanagedCallersOnly] + private static unsafe void EnumCertificatesCallback(void* certPtr, void* privateKeyPtr, Interop.AndroidCrypto.PAL_KeyAlgorithm privateKeyAlgorithm, void* context) + { + ref EnumCertificatesContext callbackContext = ref Unsafe.As(ref *(byte*)context); + + AndroidCertificatePal certPal; + var handle = new SafeX509Handle((IntPtr)certPtr); + if (privateKeyPtr != null) + { + SafeKeyHandle privateKey = privateKeyAlgorithm switch + { + Interop.AndroidCrypto.PAL_KeyAlgorithm.DSA => new SafeDsaHandle((IntPtr)privateKeyPtr), + Interop.AndroidCrypto.PAL_KeyAlgorithm.EC => new SafeEcKeyHandle((IntPtr)privateKeyPtr), + Interop.AndroidCrypto.PAL_KeyAlgorithm.RSA => new SafeRsaHandle((IntPtr)privateKeyPtr), + _ => throw new NotSupportedException(SR.NotSupported_KeyAlgorithm) + }; + certPal = new AndroidCertificatePal(handle, privateKey); + } + else + { + certPal = new AndroidCertificatePal(handle); + } + + var cert = new X509Certificate2(certPal); + if (!callbackContext.Results.Add(cert)) + cert.Dispose(); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.TrustedStore.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.TrustedStore.cs new file mode 100644 index 0000000000000..6998610bd1c39 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.TrustedStore.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Win32.SafeHandles; + +namespace Internal.Cryptography.Pal +{ + internal sealed partial class StorePal + { + private sealed class TrustedStore : IStorePal + { + private readonly StoreLocation _location; + + internal TrustedStore(StoreLocation location) + { + _location = location; + } + + public SafeHandle? SafeHandle => null; + + public void Dispose() + { + } + + public void Add(ICertificatePal cert) + { + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + } + + public void Remove(ICertificatePal cert) + { + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + } + + public void CloneTo(X509Certificate2Collection collection) + { + EnumCertificatesContext context = default; + context.Results = new HashSet(); + + bool systemOnly = _location == StoreLocation.LocalMachine; + unsafe + { + bool success = Interop.AndroidCrypto.X509StoreEnumerateTrustedCertificates( + (byte)(systemOnly ? 1 : 0), + &EnumCertificatesCallback, + Unsafe.AsPointer(ref context)); + if (!success) + { + throw new CryptographicException(SR.Cryptography_X509_StoreEnumerateFailure); + } + } + + foreach (X509Certificate2 cert in context.Results) + { + collection.Add(cert); + } + } + + private struct EnumCertificatesContext + { + public HashSet Results; + } + + [UnmanagedCallersOnly] + private static unsafe void EnumCertificatesCallback(void* certPtr, void* context) + { + ref EnumCertificatesContext callbackContext = ref Unsafe.As(ref *(byte*)context); + var handle = new SafeX509Handle((IntPtr)certPtr); + var cert = new X509Certificate2(new AndroidCertificatePal(handle)); + if (!callbackContext.Results.Add(cert)) + cert.Dispose(); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs index 3fe90285a8c1a..09ec3ce538955 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/StorePal.cs @@ -53,7 +53,59 @@ public static IExportPal LinkFromCertificateCollection(X509Certificate2Collectio public static IStorePal FromSystemStore(string storeName, StoreLocation storeLocation, OpenFlags openFlags) { - throw new NotImplementedException(nameof(FromSystemStore)); + bool isReadWrite = (openFlags & OpenFlags.ReadWrite) == OpenFlags.ReadWrite; + if (isReadWrite && storeLocation == StoreLocation.LocalMachine) + { + // All LocalMachine stores are read-only from an Android application's perspective + throw new CryptographicException( + SR.Cryptography_Unix_X509_MachineStoresReadOnly, + new PlatformNotSupportedException(SR.Cryptography_Unix_X509_MachineStoresReadOnly)); + } + + StringComparer ordinalIgnoreCase = StringComparer.OrdinalIgnoreCase; + switch (storeLocation) + { + case StoreLocation.CurrentUser: + { + // Matches Unix behaviour of getting a disallowed store that is always empty. + if (ordinalIgnoreCase.Equals(X509Store.DisallowedStoreName, storeName)) + { + return new UnsupportedDisallowedStore(openFlags); + } + + if (ordinalIgnoreCase.Equals(X509Store.MyStoreName, storeName)) + { + return AndroidKeyStore.OpenDefault(openFlags); + } + + if (ordinalIgnoreCase.Equals(X509Store.RootStoreName, storeName)) + { + // Android only allows updating the trusted store through the built-in settings application + if (isReadWrite) + { + throw new CryptographicException(SR.Security_AccessDenied); + } + + return new TrustedStore(storeLocation); + } + break; + } + case StoreLocation.LocalMachine: + { + if (ordinalIgnoreCase.Equals(X509Store.RootStoreName, storeName)) + { + return new TrustedStore(storeLocation); + } + + break; + } + } + + if ((openFlags & OpenFlags.OpenExistingOnly) == OpenFlags.OpenExistingOnly) + throw new CryptographicException(SR.Cryptography_X509_StoreNotFound); + + string message = SR.Format(SR.Cryptography_X509_StoreCannotCreate, storeName, storeLocation); + throw new CryptographicException(message, new PlatformNotSupportedException(message)); } private static ICertificatePal[] ReadPkcs12Collection(ReadOnlySpan rawData, SafePasswordHandle password) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/X509Pal.cs index 53956f55c4960..370127735235b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Android/X509Pal.cs @@ -19,7 +19,7 @@ private X509Pal() { } - private partial class AndroidX509Pal : ManagedX509ExtensionProcessor, IX509Pal + private sealed partial class AndroidX509Pal : ManagedX509ExtensionProcessor, IX509Pal { public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs index cd2db47497703..c730b7ff5b6f7 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs @@ -21,7 +21,7 @@ private X509Pal() { } - private partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal + private sealed partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal { public ECDsa DecodeECDsaPublicKey(ICertificatePal? certificatePal) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CachedSystemStoreProvider.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CachedSystemStoreProvider.cs index d78e648a00050..4cd99fad815ce 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CachedSystemStoreProvider.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/CachedSystemStoreProvider.cs @@ -20,20 +20,17 @@ internal sealed class CachedSystemStoreProvider : IStorePal // followed by a reboot for a kernel update, etc). // Customers requested something more often than "never" and 5 minutes seems like a reasonable // balance. - // - // Note that on Ubuntu the LastWrite test always fails, because the system default for SSL_CERT_DIR - // is a symlink, so the LastWrite value is always just when the symlink was created (and Ubuntu does - // not provide the single-file version at SSL_CERT_FILE, so the file update does not trigger) -- - // meaning the "assume invalid" interval is Ubuntu's only refresh. private static readonly TimeSpan s_lastWriteRecheckInterval = TimeSpan.FromSeconds(5); private static readonly TimeSpan s_assumeInvalidInterval = TimeSpan.FromMinutes(5); private static readonly Stopwatch s_recheckStopwatch = new Stopwatch(); private static readonly DirectoryInfo? s_rootStoreDirectoryInfo = SafeOpenRootDirectoryInfo(); + private static readonly DirectoryInfo? s_rootLinkedStoreDirectoryInfo = SafeOpenLinkedRootDirectoryInfo(); private static readonly FileInfo? s_rootStoreFileInfo = SafeOpenRootFileInfo(); // Use non-Value-Tuple so that it's an atomic update. private static Tuple? s_nativeCollections; private static DateTime s_directoryCertsLastWrite; + private static DateTime s_linkCertsLastWrite; private static DateTime s_fileCertsLastWrite; private readonly bool _isRoot; @@ -101,16 +98,19 @@ private static Tuple GetCollections() { FileInfo? fileInfo = s_rootStoreFileInfo; DirectoryInfo? dirInfo = s_rootStoreDirectoryInfo; + DirectoryInfo? linkInfo = s_rootLinkedStoreDirectoryInfo; fileInfo?.Refresh(); dirInfo?.Refresh(); + linkInfo?.Refresh(); if (ret == null || elapsed > s_assumeInvalidInterval || (fileInfo != null && fileInfo.Exists && fileInfo.LastWriteTimeUtc != s_fileCertsLastWrite) || - (dirInfo != null && dirInfo.Exists && dirInfo.LastWriteTimeUtc != s_directoryCertsLastWrite)) + (dirInfo != null && dirInfo.Exists && dirInfo.LastWriteTimeUtc != s_directoryCertsLastWrite) || + (linkInfo != null && linkInfo.Exists && linkInfo.LastWriteTimeUtc != s_linkCertsLastWrite)) { - ret = LoadMachineStores(dirInfo, fileInfo); + ret = LoadMachineStores(dirInfo, fileInfo, linkInfo); } } } @@ -121,7 +121,8 @@ private static Tuple GetCollections() private static Tuple LoadMachineStores( DirectoryInfo? rootStorePath, - FileInfo? rootStoreFile) + FileInfo? rootStoreFile, + DirectoryInfo? linkedRootPath) { Debug.Assert( Monitor.IsEntered(s_recheckStopwatch), @@ -134,6 +135,7 @@ private static Tuple LoadMachineStores DateTime newFileTime = default; DateTime newDirTime = default; + DateTime newLinkTime = default; var uniqueRootCerts = new HashSet(); var uniqueIntermediateCerts = new HashSet(); @@ -153,6 +155,11 @@ private static Tuple LoadMachineStores } } + if (linkedRootPath != null && linkedRootPath.Exists) + { + newLinkTime = linkedRootPath.LastWriteTimeUtc; + } + void ProcessFile(FileInfo file) { using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb")) @@ -248,6 +255,7 @@ void ProcessFile(FileInfo file) Volatile.Write(ref s_nativeCollections, newCollections); s_directoryCertsLastWrite = newDirTime; s_fileCertsLastWrite = newFileTime; + s_linkCertsLastWrite = newLinkTime; s_recheckStopwatch.Restart(); return newCollections; } @@ -291,5 +299,43 @@ void ProcessFile(FileInfo file) return null; } + + private static DirectoryInfo? SafeOpenLinkedRootDirectoryInfo() + { + string? rootDirectory = Interop.Crypto.GetX509RootStorePath(); + + if (!string.IsNullOrEmpty(rootDirectory)) + { + string? linkedDirectory = Interop.Sys.ReadLink(rootDirectory); + if (linkedDirectory == null) + { + return null; + } + + if (linkedDirectory[0] == '/') + { + rootDirectory = linkedDirectory; + } + else + { + // relative link + var root = new DirectoryInfo(rootDirectory); + root = new DirectoryInfo(Path.Join(root.Parent?.FullName, linkedDirectory)); + rootDirectory = root.FullName; + } + + try + { + return new DirectoryInfo(rootDirectory); + } + catch (ArgumentException) + { + // If SSL_CERT_DIR is set to the empty string, or anything else which gives + // "The path is not of a legal form", then the GetX509RootStoreFile value is ignored. + } + } + + return null; + } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/DirectoryBasedStoreProvider.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/DirectoryBasedStoreProvider.cs index 0cf1ccac56aec..0928ced4c2784 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/DirectoryBasedStoreProvider.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/DirectoryBasedStoreProvider.cs @@ -423,70 +423,27 @@ private static void EnsureFilePermissions(FileStream stream, uint userId) } } - internal sealed class UnsupportedDisallowedStore : IStorePal + internal static IStorePal OpenDisallowedStore(OpenFlags openFlags) { - private readonly bool _readOnly; - - internal UnsupportedDisallowedStore(OpenFlags openFlags) + string storePath = GetStorePath(X509Store.DisallowedStoreName); + try { - // ReadOnly is 0x00, so it is implicit unless either ReadWrite or MaxAllowed - // was requested. - OpenFlags writeFlags = openFlags & (OpenFlags.ReadWrite | OpenFlags.MaxAllowed); - - if (writeFlags == OpenFlags.ReadOnly) + if (Directory.Exists(storePath)) { - _readOnly = true; - } - - string storePath = GetStorePath(X509Store.DisallowedStoreName); - - try - { - if (Directory.Exists(storePath)) + // If it has no files, leave it alone. + foreach (string filePath in Directory.EnumerateFiles(storePath)) { - // If it has no files, leave it alone. - foreach (string filePath in Directory.EnumerateFiles(storePath)) - { - string msg = SR.Format(SR.Cryptography_Unix_X509_DisallowedStoreNotEmpty, storePath); - throw new CryptographicException(msg, new PlatformNotSupportedException(msg)); - } + string msg = SR.Format(SR.Cryptography_Unix_X509_DisallowedStoreNotEmpty, storePath); + throw new CryptographicException(msg, new PlatformNotSupportedException(msg)); } } - catch (IOException) - { - // Suppress the exception, treat the store as empty. - } - } - - public void Dispose() - { - // Nothing to do. - } - - public void CloneTo(X509Certificate2Collection collection) - { - // Never show any data. - } - - public void Add(ICertificatePal cert) - { - if (_readOnly) - { - throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); - } - - throw new CryptographicException( - SR.Cryptography_Unix_X509_NoDisallowedStore, - new PlatformNotSupportedException(SR.Cryptography_Unix_X509_NoDisallowedStore)); } - - public void Remove(ICertificatePal cert) + catch (IOException) { - // Remove never throws if it does no measurable work. - // Since CloneTo always says the store is empty, no measurable work is ever done. + // Suppress the exception, treat the store as empty. } - SafeHandle? IStorePal.SafeHandle { get; } + return new UnsupportedDisallowedStore(openFlags); } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs index ee401a4a0569b..e174a9fd59366 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs @@ -41,13 +41,14 @@ public string NormalizeOid(string maybeOid, OidGroup expectedGroup) public void FindByThumbprint(byte[] thumbprint) { - FindCore(cert => cert.GetCertHash().ContentsEqual(thumbprint)); + FindCore(thumbprint, static (thumbprint, cert) => cert.GetCertHash().ContentsEqual(thumbprint)); } public void FindBySubjectName(string subjectName) { FindCore( - cert => + subjectName, + static (subjectName, cert) => { string formedSubject = X500NameEncoder.X500DistinguishedNameDecode(cert.SubjectName.RawData, false, X500DistinguishedNameFlags.None); @@ -57,13 +58,14 @@ public void FindBySubjectName(string subjectName) public void FindBySubjectDistinguishedName(string subjectDistinguishedName) { - FindCore(cert => StringComparer.OrdinalIgnoreCase.Equals(subjectDistinguishedName, cert.Subject)); + FindCore(subjectDistinguishedName, static (subjectDistinguishedName, cert) => StringComparer.OrdinalIgnoreCase.Equals(subjectDistinguishedName, cert.Subject)); } public void FindByIssuerName(string issuerName) { FindCore( - cert => + issuerName, + static (issuerName, cert) => { string formedIssuer = X500NameEncoder.X500DistinguishedNameDecode(cert.IssuerName.RawData, false, X500DistinguishedNameFlags.None); @@ -73,17 +75,18 @@ public void FindByIssuerName(string issuerName) public void FindByIssuerDistinguishedName(string issuerDistinguishedName) { - FindCore(cert => StringComparer.OrdinalIgnoreCase.Equals(issuerDistinguishedName, cert.Issuer)); + FindCore(issuerDistinguishedName, static (issuerDistinguishedName, cert) => StringComparer.OrdinalIgnoreCase.Equals(issuerDistinguishedName, cert.Issuer)); } public void FindBySerialNumber(BigInteger hexValue, BigInteger decimalValue) { FindCore( - cert => + (hexValue, decimalValue), + static (state, cert) => { byte[] serialBytes = cert.GetSerialNumber(); BigInteger serialNumber = FindPal.PositiveBigIntegerFromByteArray(serialBytes); - bool match = hexValue.Equals(serialNumber) || decimalValue.Equals(serialNumber); + bool match = state.hexValue.Equals(serialNumber) || state.decimalValue.Equals(serialNumber); return match; }); @@ -107,27 +110,28 @@ public void FindByTimeValid(DateTime dateTime) { DateTime normalized = NormalizeDateTime(dateTime); - FindCore(cert => cert.NotBefore <= normalized && normalized <= cert.NotAfter); + FindCore(normalized, static (normalized, cert) => cert.NotBefore <= normalized && normalized <= cert.NotAfter); } public void FindByTimeNotYetValid(DateTime dateTime) { DateTime normalized = NormalizeDateTime(dateTime); - FindCore(cert => cert.NotBefore > normalized); + FindCore(normalized, static (normalized, cert) => cert.NotBefore > normalized); } public void FindByTimeExpired(DateTime dateTime) { DateTime normalized = NormalizeDateTime(dateTime); - FindCore(cert => cert.NotAfter < normalized); + FindCore(normalized, static (normalized, cert) => cert.NotAfter < normalized); } public void FindByTemplateName(string templateName) { FindCore( - cert => + templateName, + static (templateName, cert) => { X509Extension? ext = FindExtension(cert, Oids.EnrollCertTypeExtension); @@ -172,7 +176,8 @@ public void FindByTemplateName(string templateName) public void FindByApplicationPolicy(string oidValue) { FindCore( - cert => + oidValue, + static (oidValue, cert) => { X509Extension? ext = FindExtension(cert, Oids.EnhancedKeyUsage); @@ -201,7 +206,8 @@ public void FindByApplicationPolicy(string oidValue) public void FindByCertificatePolicy(string oidValue) { FindCore( - cert => + oidValue, + static (oidValue, cert) => { X509Extension? ext = FindExtension(cert, Oids.CertPolicies); @@ -218,13 +224,14 @@ public void FindByCertificatePolicy(string oidValue) public void FindByExtension(string oidValue) { - FindCore(cert => FindExtension(cert, oidValue) != null); + FindCore(oidValue, static (oidValue, cert) => FindExtension(cert, oidValue) != null); } public void FindByKeyUsage(X509KeyUsageFlags keyUsage) { FindCore( - cert => + keyUsage, + static (keyUsage, cert) => { X509Extension? ext = FindExtension(cert, Oids.KeyUsage); @@ -246,7 +253,8 @@ public void FindByKeyUsage(X509KeyUsageFlags keyUsage) public void FindBySubjectKeyIdentifier(byte[] keyIdentifier) { FindCore( - cert => + keyIdentifier, + (keyIdentifier, cert) => { X509Extension? ext = FindExtension(cert, Oids.SubjectKeyIdentifier); byte[] certKeyId; @@ -305,11 +313,14 @@ protected virtual void Dispose(bool disposing) protected abstract X509Certificate2 CloneCertificate(X509Certificate2 cert); - private void FindCore(Predicate predicate) + private void FindCore(TState state, Func predicate) { - foreach (X509Certificate2 cert in _findFrom) + X509Certificate2Collection findFrom = _findFrom; + int count = findFrom.Count; + for (int i = 0; i < count; i++) { - if (predicate(cert)) + X509Certificate2 cert = findFrom[i]; + if (predicate(state, cert)) { if (!_validOnly || IsCertValid(cert)) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs index 345579ea104c0..6148b022a42cc 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs @@ -15,10 +15,14 @@ using System.Text; using Microsoft.Win32.SafeHandles; +using X509VerifyStatusCodeUniversal = Interop.Crypto.X509VerifyStatusCodeUniversal; + namespace Internal.Cryptography.Pal { internal sealed class OpenSslX509ChainProcessor : IChainPal { + private delegate X509ChainStatusFlags MapVersionSpecificCode(Interop.Crypto.X509VerifyStatusCode code); + // The average chain is 3 (End-Entity, Intermediate, Root) // 10 is plenty big. private const int DefaultChainCapacity = 10; @@ -35,8 +39,10 @@ internal sealed class OpenSslX509ChainProcessor : IChainPal // Save the results of GetX509VerifyCertErrorString as we look them up. // On Windows we preload the entire string table, but on Linux we'll delay-load memoize // to avoid needing to know the upper bound of error codes for the particular build of OpenSSL. - private static readonly ConcurrentDictionary s_errorStrings = - new ConcurrentDictionary(); + private static readonly ConcurrentDictionary s_errorStrings = + new ConcurrentDictionary(); + + private static readonly MapVersionSpecificCode s_mapVersionSpecificCode = GetVersionLookup(); private SafeX509Handle _leafHandle; private SafeX509StoreHandle _store; @@ -191,10 +197,10 @@ internal Interop.Crypto.X509VerifyStatusCode FindFirstChain(X509Certificate2Coll internal static bool IsCompleteChain(Interop.Crypto.X509VerifyStatusCode statusCode) { - switch (statusCode) + switch (statusCode.UniversalCode) { - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: return false; default: return true; @@ -208,7 +214,7 @@ internal Interop.Crypto.X509VerifyStatusCode FindChainViaAia( SafeX509StoreCtxHandle storeCtx = _storeCtx; Interop.Crypto.X509VerifyStatusCode statusCode = - Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; while (!IsCompleteChain(statusCode)) { @@ -657,14 +663,14 @@ private Interop.Crypto.X509VerifyStatusCode CheckOcsp( Interop.Crypto.X509VerifyStatusCode status = Interop.Crypto.X509ChainGetCachedOcspStatus(_storeCtx, ocspCache, chainDepth); - if (status != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL) + if (status != X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL) { return status; } if (revocationMode != X509RevocationMode.Online) { - return Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL; + return X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL; } string? baseUri = GetOcspEndpoint(certHandle); @@ -703,7 +709,7 @@ private Interop.Crypto.X509VerifyStatusCode CheckOcsp( { if (resp == null || resp.IsInvalid) { - return Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL; + return X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL; } try @@ -941,7 +947,7 @@ private static void AddElementStatus( Status = X509ChainStatusFlags.OfflineRevocation, StatusInformation = GetErrorString( - Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL), + X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL), }; elementStatus.Add(chainStatus); @@ -1000,89 +1006,123 @@ private static void AddUniqueStatus(List list, ref X509ChainSta private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.Crypto.X509VerifyStatusCode code) { - switch (code) + switch (code.UniversalCode) { - case Interop.Crypto.X509VerifyStatusCode.X509_V_OK: + case X509VerifyStatusCodeUniversal.X509_V_OK: return X509ChainStatusFlags.NoError; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_NOT_YET_VALID: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_HAS_EXPIRED: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_NOT_YET_VALID: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_HAS_EXPIRED: + case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: return X509ChainStatusFlags.NotTimeValid; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_REVOKED: return X509ChainStatusFlags.Revoked; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_SIGNATURE_FAILURE: return X509ChainStatusFlags.NotSignatureValid; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_UNTRUSTED: + case X509VerifyStatusCodeUniversal.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509VerifyStatusCodeUniversal.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: return X509ChainStatusFlags.UntrustedRoot; // When adding to the RevocationStatusUnknown block, ensure these codes are properly // tested/cleared in the ErrorCollection type. - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_HAS_EXPIRED: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_NOT_YET_VALID: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_SIGNATURE_FAILURE: + case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: return X509ChainStatusFlags.RevocationStatusUnknown; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_EXTENSION: + case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_EXTENSION: return X509ChainStatusFlags.InvalidExtension; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: return X509ChainStatusFlags.PartialChain; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_PURPOSE: + case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_PURPOSE: return X509ChainStatusFlags.NotValidForUsage; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_CA: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_NON_CA: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_PATH_LENGTH_EXCEEDED: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CERTSIGN: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: + case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_NON_CA: + case X509VerifyStatusCodeUniversal.X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_CERTSIGN: + case X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: return X509ChainStatusFlags.InvalidBasicConstraints; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_POLICY_EXTENSION: - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_NO_EXPLICIT_POLICY: + case X509VerifyStatusCodeUniversal.X509_V_ERR_INVALID_POLICY_EXTENSION: + case X509VerifyStatusCodeUniversal.X509_V_ERR_NO_EXPLICIT_POLICY: return X509ChainStatusFlags.InvalidPolicyConstraints; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REJECTED: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_REJECTED: return X509ChainStatusFlags.ExplicitDistrust; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: return X509ChainStatusFlags.HasNotSupportedCriticalExtension; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_PERMITTED_VIOLATION: + case X509VerifyStatusCodeUniversal.X509_V_ERR_PERMITTED_VIOLATION: return X509ChainStatusFlags.HasNotPermittedNameConstraint; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_EXCLUDED_VIOLATION: + case X509VerifyStatusCodeUniversal.X509_V_ERR_EXCLUDED_VIOLATION: return X509ChainStatusFlags.HasExcludedNameConstraint; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_SUBTREE_MINMAX: + case X509VerifyStatusCodeUniversal.X509_V_ERR_SUBTREE_MINMAX: return X509ChainStatusFlags.HasNotSupportedNameConstraint; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: + case X509VerifyStatusCodeUniversal.X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: return X509ChainStatusFlags.InvalidNameConstraints; - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_CHAIN_TOO_LONG: throw new CryptographicException(); - case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_OUT_OF_MEM: + case X509VerifyStatusCodeUniversal.X509_V_ERR_OUT_OF_MEM: throw new OutOfMemoryException(); + default: + return s_mapVersionSpecificCode(code); + } + } + + private static X509ChainStatusFlags MapOpenSsl30Code(Interop.Crypto.X509VerifyStatusCode code) + { + switch (code.Code30) + { + case Interop.Crypto.X509VerifyStatusCode30.X509_V_ERR_INVALID_CA: + return X509ChainStatusFlags.InvalidBasicConstraints; + default: + Debug.Fail("Unrecognized X509VerifyStatusCode:" + code); + throw new CryptographicException(); + } + } + + private static X509ChainStatusFlags MapOpenSsl102Code(Interop.Crypto.X509VerifyStatusCode code) + { + switch (code.Code102) + { + case Interop.Crypto.X509VerifyStatusCode102.X509_V_ERR_INVALID_CA: + return X509ChainStatusFlags.InvalidBasicConstraints; + default: + Debug.Fail("Unrecognized X509VerifyStatusCode:" + code); + throw new CryptographicException(); + } + } + + private static X509ChainStatusFlags MapOpenSsl111Code(Interop.Crypto.X509VerifyStatusCode code) + { + switch (code.Code111) + { + case Interop.Crypto.X509VerifyStatusCode111.X509_V_ERR_INVALID_CA: + return X509ChainStatusFlags.InvalidBasicConstraints; default: Debug.Fail("Unrecognized X509VerifyStatusCode:" + code); throw new CryptographicException(); @@ -1186,7 +1226,7 @@ private static void AddToStackAndUpRef(IntPtr cert, SafeX509StackHandle stack) private static string GetErrorString(Interop.Crypto.X509VerifyStatusCode code) { return s_errorStrings.GetOrAdd( - code, + code.Code, c => Interop.Crypto.GetX509VerifyCertErrorString(c)); } @@ -1242,7 +1282,7 @@ internal int VerifyCallback(int ok, IntPtr ctx) int errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx); if (AbortOnSignatureError && - errorCode == Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE) + errorCode == X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_SIGNATURE_FAILURE) { AbortedForSignatureError = true; return 0; @@ -1252,9 +1292,9 @@ internal int VerifyCallback(int ok, IntPtr ctx) // * For compatibility with Windows / .NET Framework, do not report X509_V_CRL_NOT_YET_VALID. // * X509_V_ERR_DIFFERENT_CRL_SCOPE will result in X509_V_ERR_UNABLE_TO_GET_CRL // which will trigger OCSP, so is ignorable. - if (errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_OK && - errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID && - errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_DIFFERENT_CRL_SCOPE) + if (errorCode != X509VerifyStatusCodeUniversal.X509_V_OK && + errorCode != X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_NOT_YET_VALID && + errorCode != X509VerifyStatusCodeUniversal.X509_V_ERR_DIFFERENT_CRL_SCOPE) { if (_errors == null) { @@ -1290,6 +1330,23 @@ internal int VerifyCallback(int ok, IntPtr ctx) } } + private static MapVersionSpecificCode GetVersionLookup() + { + // 3.0+ are M_NN_00_PP_p (Major, Minor, 0, Patch, Preview) + // 1.x.y are 1_XX_YY_PP_p + if (SafeEvpPKeyHandle.OpenSslVersion >= 0x3_00_00_00_0) + { + return MapOpenSsl30Code; + } + + if (SafeEvpPKeyHandle.OpenSslVersion >= 0x1_01_01_00_0) + { + return MapOpenSsl111Code; + } + + return MapOpenSsl102Code; + } + private unsafe struct ErrorCollection { // As of OpenSSL 1.1.1 there are 75 defined X509_V_ERR values, @@ -1323,18 +1380,18 @@ private bool HasError(Interop.Crypto.X509VerifyStatusCode statusCode) internal void ClearRevoked() { - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_REVOKED); } internal bool IsRevoked() { - return HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED); + return HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_REVOKED); } internal bool HasCorruptRevocation() { return - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE) && + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_SIGNATURE_FAILURE) && IsRevoked(); } @@ -1342,16 +1399,16 @@ internal void ClearRevocationUnknown() { // When adding codes here, make sure that HasRevocationUnknown and // MapVerifyErrorToChainStatus both agree that the code maps to RevocationStatusUnknown. - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER); - ClearError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_HAS_EXPIRED); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_NOT_YET_VALID); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_SIGNATURE_FAILURE); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER); + ClearError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); } internal bool HasRevocationUnknown() @@ -1366,25 +1423,25 @@ internal bool HasRevocationUnknown() // The most common reasons are UNABLE_TO_GET_CRL, then CRL_HAS_EXPIRED. return - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_HAS_EXPIRED) || // The rest are simply alphabetical. - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER) || - HasError(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_NOT_YET_VALID) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_CRL_SIGNATURE_FAILURE) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER) || + HasError(X509VerifyStatusCodeUniversal.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); } internal void AddRevocationUnknown() { // Only one of the codes has to be set. // So set the one we look for first. - Add(Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL); + Add(X509VerifyStatusCodeUniversal.X509_V_ERR_UNABLE_TO_GET_CRL); } public Enumerator GetEnumerator() @@ -1421,7 +1478,7 @@ public override string ToString() private static int FindBucket(Interop.Crypto.X509VerifyStatusCode statusCode, out int bitValue) { - int val = (int)statusCode; + int val = statusCode.Code; int bucket; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs index d0f583da64c1a..749beed10edc8 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/StorePal.cs @@ -139,7 +139,7 @@ public static IStorePal FromSystemStore(string storeName, StoreLocation storeLoc { if (X509Store.DisallowedStoreName.Equals(storeName, StringComparison.OrdinalIgnoreCase)) { - return new DirectoryBasedStoreProvider.UnsupportedDisallowedStore(openFlags); + return DirectoryBasedStoreProvider.OpenDisallowedStore(openFlags); } return new DirectoryBasedStoreProvider(storeName, openFlags); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnsupportedDisallowedStore.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnsupportedDisallowedStore.cs new file mode 100644 index 0000000000000..fbdcd08bceb75 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnsupportedDisallowedStore.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Internal.Cryptography.Pal +{ + internal sealed class UnsupportedDisallowedStore : IStorePal + { + private readonly bool _readOnly; + + internal UnsupportedDisallowedStore(OpenFlags openFlags) + { + // ReadOnly is 0x00, so it is implicit unless either ReadWrite or MaxAllowed + // was requested. + OpenFlags writeFlags = openFlags & (OpenFlags.ReadWrite | OpenFlags.MaxAllowed); + + if (writeFlags == OpenFlags.ReadOnly) + { + _readOnly = true; + } + } + + public void Dispose() + { + // Nothing to do. + } + + public void CloneTo(X509Certificate2Collection collection) + { + // Never show any data. + } + + public void Add(ICertificatePal cert) + { + if (_readOnly) + { + throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly); + } + + throw new CryptographicException( + SR.Cryptography_Unix_X509_NoDisallowedStore, + new PlatformNotSupportedException(SR.Cryptography_Unix_X509_NoDisallowedStore)); + } + + public void Remove(ICertificatePal cert) + { + // Remove never throws if it does no measurable work. + // Since CloneTo always says the store is empty, no measurable work is ever done. + } + + SafeHandle? IStorePal.SafeHandle { get; } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/FindPal.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/FindPal.cs index 8915e3469ae5f..a858d8ee11340 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/FindPal.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/FindPal.cs @@ -14,7 +14,7 @@ namespace Internal.Cryptography.Pal { - internal partial class FindPal : IFindPal + internal sealed partial class FindPal : IFindPal { private readonly StorePal _storePal; private readonly X509Certificate2Collection _copyTo; @@ -50,7 +50,7 @@ public unsafe void FindByThumbprint(byte[] thumbPrint) fixed (byte* pThumbPrint = thumbPrint) { CRYPTOAPI_BLOB blob = new CRYPTOAPI_BLOB(thumbPrint.Length, pThumbPrint); - FindCore(CertFindType.CERT_FIND_HASH, &blob); + FindCore(CertFindType.CERT_FIND_HASH, &blob); } } @@ -58,14 +58,15 @@ public unsafe void FindBySubjectName(string subjectName) { fixed (char* pSubjectName = subjectName) { - FindCore(CertFindType.CERT_FIND_SUBJECT_STR, pSubjectName); + FindCore(CertFindType.CERT_FIND_SUBJECT_STR, pSubjectName); } } public void FindBySubjectDistinguishedName(string subjectDistinguishedName) { FindCore( - delegate (SafeCertContextHandle pCertContext) + subjectDistinguishedName, + static (subjectDistinguishedName, pCertContext) => { string actual = GetCertNameInfo(pCertContext, CertNameType.CERT_NAME_RDN_TYPE, CertNameFlags.None); return subjectDistinguishedName.Equals(actual, StringComparison.OrdinalIgnoreCase); @@ -76,14 +77,15 @@ public unsafe void FindByIssuerName(string issuerName) { fixed (char* pIssuerName = issuerName) { - FindCore(CertFindType.CERT_FIND_ISSUER_STR, pIssuerName); + FindCore(CertFindType.CERT_FIND_ISSUER_STR, pIssuerName); } } public void FindByIssuerDistinguishedName(string issuerDistinguishedName) { FindCore( - delegate (SafeCertContextHandle pCertContext) + issuerDistinguishedName, + static (issuerDistinguishedName, pCertContext) => { string actual = GetCertNameInfo(pCertContext, CertNameType.CERT_NAME_RDN_TYPE, CertNameFlags.CERT_NAME_ISSUER_FLAG); return issuerDistinguishedName.Equals(actual, StringComparison.OrdinalIgnoreCase); @@ -93,7 +95,8 @@ public void FindByIssuerDistinguishedName(string issuerDistinguishedName) public unsafe void FindBySerialNumber(BigInteger hexValue, BigInteger decimalValue) { FindCore( - delegate (SafeCertContextHandle pCertContext) + (hexValue, decimalValue), + static (state, pCertContext) => { byte[] actual = pCertContext.CertContext->pCertInfo->SerialNumber.ToByteArray(); GC.KeepAlive(pCertContext); @@ -101,7 +104,7 @@ public unsafe void FindBySerialNumber(BigInteger hexValue, BigInteger decimalVal // Convert to BigInteger as the comparison must not fail due to spurious leading zeros BigInteger actualAsBigInteger = PositiveBigIntegerFromByteArray(actual); - return hexValue.Equals(actualAsBigInteger) || decimalValue.Equals(actualAsBigInteger); + return state.hexValue.Equals(actualAsBigInteger) || state.decimalValue.Equals(actualAsBigInteger); }); } @@ -125,19 +128,21 @@ private unsafe void FindByTime(DateTime dateTime, int compareResult) FILETIME fileTime = FILETIME.FromDateTime(dateTime); FindCore( - delegate (SafeCertContextHandle pCertContext) + (fileTime, compareResult), + static (state, pCertContext) => { - int comparison = Interop.crypt32.CertVerifyTimeValidity(ref fileTime, + int comparison = Interop.crypt32.CertVerifyTimeValidity(ref state.fileTime, pCertContext.CertContext->pCertInfo); GC.KeepAlive(pCertContext); - return comparison == compareResult; + return comparison == state.compareResult; }); } public unsafe void FindByTemplateName(string templateName) { FindCore( - delegate (SafeCertContextHandle pCertContext) + templateName, + static (templateName, pCertContext) => { // The template name can have 2 different formats: V1 format (<= Win2K) is just a string // V2 format (XP only) can be a friendly name or an OID. @@ -203,7 +208,8 @@ public unsafe void FindByTemplateName(string templateName) public unsafe void FindByApplicationPolicy(string oidValue) { FindCore( - delegate (SafeCertContextHandle pCertContext) + oidValue, + static (oidValue, pCertContext) => { int numOids; int cbData = 0; @@ -234,7 +240,8 @@ public unsafe void FindByApplicationPolicy(string oidValue) public unsafe void FindByCertificatePolicy(string oidValue) { FindCore( - delegate (SafeCertContextHandle pCertContext) + oidValue, + static (oidValue, pCertContext) => { CERT_INFO* pCertInfo = pCertContext.CertContext->pCertInfo; CERT_EXTENSION* pCertExtension = Interop.crypt32.CertFindExtension(Oids.CertPolicies, @@ -274,7 +281,8 @@ public unsafe void FindByCertificatePolicy(string oidValue) public unsafe void FindByExtension(string oidValue) { FindCore( - delegate (SafeCertContextHandle pCertContext) + oidValue, + static (oidValue, pCertContext) => { CERT_INFO* pCertInfo = pCertContext.CertContext->pCertInfo; CERT_EXTENSION* pCertExtension = Interop.crypt32.CertFindExtension(oidValue, pCertInfo->cExtension, pCertInfo->rgExtension); @@ -286,7 +294,8 @@ public unsafe void FindByExtension(string oidValue) public unsafe void FindByKeyUsage(X509KeyUsageFlags keyUsage) { FindCore( - delegate (SafeCertContextHandle pCertContext) + keyUsage, + static (keyUsage, pCertContext) => { CERT_INFO* pCertInfo = pCertContext.CertContext->pCertInfo; X509KeyUsageFlags actual; @@ -300,7 +309,8 @@ public unsafe void FindByKeyUsage(X509KeyUsageFlags keyUsage) public void FindBySubjectKeyIdentifier(byte[] keyIdentifier) { FindCore( - delegate (SafeCertContextHandle pCertContext) + keyIdentifier, + static (keyIdentifier, pCertContext) => { int cbData = 0; if (!Interop.crypt32.CertGetCertificateContextProperty(pCertContext, CertContextPropId.CERT_KEY_IDENTIFIER_PROP_ID, null, ref cbData)) @@ -319,12 +329,12 @@ public void Dispose() _storePal.Dispose(); } - private unsafe void FindCore(Func filter) + private unsafe void FindCore(TState state, Func filter) { - FindCore(CertFindType.CERT_FIND_ANY, null, filter); + FindCore(CertFindType.CERT_FIND_ANY, null, state, filter); } - private unsafe void FindCore(CertFindType dwFindType, void* pvFindPara, Func? filter = null) + private unsafe void FindCore(CertFindType dwFindType, void* pvFindPara, TState state = default!, Func? filter = null) { SafeCertStoreHandle findResults = Interop.crypt32.CertOpenStore( CertStoreProvider.CERT_STORE_PROV_MEMORY, @@ -338,7 +348,7 @@ private unsafe void FindCore(CertFindType dwFindType, void* pvFindPara, Func(void* pvDecodedObject, int cbDecodedObject); - public static void DecodeObject( + public static TResult DecodeObject( this byte[] encoded, CryptDecodeObjectStructType lpszStructType, - DecodedObjectReceiver receiver) + DecodedObjectReceiver receiver) { unsafe { @@ -109,14 +110,14 @@ public static void DecodeObject( throw Marshal.GetLastWin32Error().ToCryptographicException(); } - receiver(decoded, cb); + return receiver(decoded, cb); } } - public static void DecodeObject( + public static TResult DecodeObject( this byte[] encoded, string lpszStructType, - DecodedObjectReceiver receiver) + DecodedObjectReceiver receiver) { unsafe { @@ -148,7 +149,7 @@ public static void DecodeObject( throw Marshal.GetLastWin32Error().ToCryptographicException(); } - receiver(decoded, cb); + return receiver(decoded, cb); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs index 57883ba6d9091..f7f1c5a5ebb2a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.CustomExtensions.cs @@ -41,14 +41,12 @@ public void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags ke { unsafe { - uint keyUsagesAsUint = 0; - encoded.DecodeObject( + uint keyUsagesAsUint = encoded.DecodeObject( CryptDecodeObjectStructType.X509_KEY_USAGE, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CRYPT_BIT_BLOB)); CRYPT_BIT_BLOB* pBlob = (CRYPT_BIT_BLOB*)pvDecoded; - keyUsagesAsUint = 0; byte* pbData = pBlob->pbData; if (pbData != null) @@ -58,13 +56,13 @@ public void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags ke switch (pBlob->cbData) { case 1: - keyUsagesAsUint = *pbData; - break; + return *pbData; case 2: - keyUsagesAsUint = *(ushort*)(pbData); - break; + return *(ushort*)(pbData); } } + + return 0u; } ); keyUsages = (X509KeyUsageFlags)keyUsagesAsUint; @@ -95,25 +93,16 @@ public void DecodeX509BasicConstraintsExtension(byte[] encoded, out bool certifi { unsafe { - bool localCertificateAuthority = false; - bool localHasPathLengthConstraint = false; - int localPathLengthConstraint = 0; - - encoded.DecodeObject( + (certificateAuthority, hasPathLengthConstraint, pathLengthConstraint) = encoded.DecodeObject( CryptDecodeObjectStructType.X509_BASIC_CONSTRAINTS, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CERT_BASIC_CONSTRAINTS_INFO)); CERT_BASIC_CONSTRAINTS_INFO* pBasicConstraints = (CERT_BASIC_CONSTRAINTS_INFO*)pvDecoded; - localCertificateAuthority = (pBasicConstraints->SubjectType.pbData[0] & CERT_BASIC_CONSTRAINTS_INFO.CERT_CA_SUBJECT_FLAG) != 0; - localHasPathLengthConstraint = pBasicConstraints->fPathLenConstraint != 0; - localPathLengthConstraint = pBasicConstraints->dwPathLenConstraint; - } - ); - - certificateAuthority = localCertificateAuthority; - hasPathLengthConstraint = localHasPathLengthConstraint; - pathLengthConstraint = localPathLengthConstraint; + return ((pBasicConstraints->SubjectType.pbData[0] & CERT_BASIC_CONSTRAINTS_INFO.CERT_CA_SUBJECT_FLAG) != 0, + pBasicConstraints->fPathLenConstraint != 0, + pBasicConstraints->dwPathLenConstraint); + }); } } @@ -121,25 +110,16 @@ public void DecodeX509BasicConstraints2Extension(byte[] encoded, out bool certif { unsafe { - bool localCertificateAuthority = false; - bool localHasPathLengthConstraint = false; - int localPathLengthConstraint = 0; - - encoded.DecodeObject( + (certificateAuthority, hasPathLengthConstraint, pathLengthConstraint) = encoded.DecodeObject( CryptDecodeObjectStructType.X509_BASIC_CONSTRAINTS2, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CERT_BASIC_CONSTRAINTS2_INFO)); CERT_BASIC_CONSTRAINTS2_INFO* pBasicConstraints2 = (CERT_BASIC_CONSTRAINTS2_INFO*)pvDecoded; - localCertificateAuthority = pBasicConstraints2->fCA != 0; - localHasPathLengthConstraint = pBasicConstraints2->fPathLenConstraint != 0; - localPathLengthConstraint = pBasicConstraints2->dwPathLenConstraint; - } - ); - - certificateAuthority = localCertificateAuthority; - hasPathLengthConstraint = localHasPathLengthConstraint; - pathLengthConstraint = localPathLengthConstraint; + return (pBasicConstraints2->fCA != 0, + pBasicConstraints2->fPathLenConstraint != 0, + pBasicConstraints2->dwPathLenConstraint); + }); } } @@ -163,14 +143,14 @@ public byte[] EncodeX509EnhancedKeyUsageExtension(OidCollection usages) public void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages) { - OidCollection localUsages = new OidCollection(); - unsafe { - encoded.DecodeObject( + usages = encoded.DecodeObject( CryptDecodeObjectStructType.X509_ENHANCED_KEY_USAGE, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { + var localUsages = new OidCollection(); + Debug.Assert(cbDecoded >= sizeof(CERT_ENHKEY_USAGE)); CERT_ENHKEY_USAGE* pEnhKeyUsage = (CERT_ENHKEY_USAGE*)pvDecoded; int count = pEnhKeyUsage->cUsageIdentifier; @@ -181,11 +161,10 @@ public void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollectio Oid oid = new Oid(oidValue); localUsages.Add(oid); } - } - ); - } - usages = localUsages; + return localUsages; + }); + } } public byte[] EncodeX509SubjectKeyIdentifierExtension(ReadOnlySpan subjectKeyIdentifier) @@ -204,17 +183,14 @@ public void DecodeX509SubjectKeyIdentifierExtension(byte[] encoded, out byte[] s { unsafe { - byte[] localSubjectKeyIdentifier = null!; - encoded.DecodeObject( + subjectKeyIdentifier = encoded.DecodeObject( Oids.SubjectKeyIdentifier, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CRYPTOAPI_BLOB)); CRYPTOAPI_BLOB* pBlob = (CRYPTOAPI_BLOB*)pvDecoded; - localSubjectKeyIdentifier = pBlob->ToByteArray(); - } - ); - subjectKeyIdentifier = localSubjectKeyIdentifier; + return pBlob->ToByteArray(); + }); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs index dd11ea04d0830..19bce7dfa75fa 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/X509Pal.PublicKey.cs @@ -283,46 +283,32 @@ private static byte[] ConstructDSSPublicKeyCspBlob(byte[] encodedKeyValue, byte[ { unsafe { - byte[]? decodedKeyValue = null; - - encodedKeyValue.DecodeObject( + return encodedKeyValue.DecodeObject( CryptDecodeObjectStructType.X509_DSS_PUBLICKEY, - delegate (void* pvDecoded, int cbDecoded) + static delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CRYPTOAPI_BLOB)); CRYPTOAPI_BLOB* pBlob = (CRYPTOAPI_BLOB*)pvDecoded; - decodedKeyValue = pBlob->ToByteArray(); - } - ); - - return decodedKeyValue; + return pBlob->ToByteArray(); + }); } } private static void DecodeDssParameters(byte[] encodedParameters, out byte[] p, out byte[] q, out byte[] g) { - byte[] pLocal = null!; - byte[] qLocal = null!; - byte[] gLocal = null!; - unsafe { - encodedParameters.DecodeObject( + (p, q, g) = encodedParameters.DecodeObject( CryptDecodeObjectStructType.X509_DSS_PARAMETERS, delegate (void* pvDecoded, int cbDecoded) { Debug.Assert(cbDecoded >= sizeof(CERT_DSS_PARAMETERS)); CERT_DSS_PARAMETERS* pCertDssParameters = (CERT_DSS_PARAMETERS*)pvDecoded; - pLocal = pCertDssParameters->p.ToByteArray(); - qLocal = pCertDssParameters->q.ToByteArray(); - gLocal = pCertDssParameters->g.ToByteArray(); - } - ); + return (pCertDssParameters->p.ToByteArray(), + pCertDssParameters->q.ToByteArray(), + pCertDssParameters->g.ToByteArray()); + }); } - - p = pLocal; - q = qLocal; - g = gLocal; } private static bool HasExplicitParameters(SafeBCryptKeyHandle bcryptHandle) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx index 6d619e428aa0b..97eefea5eff5b 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx @@ -103,9 +103,24 @@ Index was out of range. Must be non-negative and less than the size of the collection. + + An empty custom trust store is not supported on this platform. + The certificate has invalid policy. + + The certificate chain is incomplete. + + + The certificate's revocation status could not be determined. + + + The certificate chain setting '{0}' is not supported on this platform. + + + The verification flag '{0}' is not supported on this platform. + The provided value of {0} bytes does not match the expected size of {1} bytes for the algorithm ({2}). @@ -292,9 +307,18 @@ The X509 certificate store is read-only. + + The X509 certificate could not be removed from the store. + The platform does not have a definition for an X509 certificate store named '{0}' with a StoreLocation of '{1}', and does not support creating it. + + Adding a DSA private key to the store is not supported on this platform. + + + Failed to enumerate certificates from the store. + The certificate contents do not contain a PEM with a CERTIFICATE label, or the content is malformed. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj index 20dbf62166cc3..00b5c63c0fd49 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj @@ -10,9 +10,7 @@ SR.SystemSecurityCryptographyX509Certificates_PlatformNotSupported - - true + true true @@ -317,6 +315,7 @@ + + + + + + + /// Tests that when a certificate chain has a root certification which is not trusted by the trust provider, - /// Build returns false and a ChainStatus returns UntrustedRoot + /// Build returns false and a ChainStatus returns UntrustedRoot. + /// Android does not support the detailed status in this test. It always validates time + /// and trusted root. It will fail to build any chain if those are not valid. /// [Fact] [OuterLoop] + [PlatformSpecific(~TestPlatforms.Android)] public static void BuildChainExtraStoreUntrustedRoot() { using (var testCert = new X509Certificate2(TestFiles.ChainPfxFile, TestData.ChainPfxPassword)) @@ -245,18 +258,28 @@ public static void SystemTrustCertificateWithCustomRootTrust(bool addCertificate chain.ChainPolicy.CustomTrustStore.Add(testCert); } - Assert.False(chain.Build(microsoftDotCom)); - - // Linux and Windows do not search the default system root stores when CustomRootTrust is enabled - if (OperatingSystem.IsMacOS()) + if (PlatformDetection.IsAndroid) { - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); + // Android does not support an empty custom root trust + // Only self-issued certs are treated as trusted anchors, so building the chain + // should throw PNSE regardless of whether or not testCert is added to the store + Assert.Throws(() => chain.Build(microsoftDotCom)); } else { - Assert.Equal(2, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + Assert.False(chain.Build(microsoftDotCom)); + + // Linux and Windows do not search the default system root stores when CustomRootTrust is enabled + if (OperatingSystem.IsMacOS()) + { + Assert.Equal(3, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags()); + } + else + { + Assert.Equal(2, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + } } } } @@ -269,59 +292,62 @@ public enum BuildChainCustomTrustStoreTestArguments : int MultipleCalls } + public static IEnumerable BuildChainCustomTrustStoreData() + { + if (!PlatformDetection.IsAndroid) + { + // Android doesn't support an empty custom root + yield return new object[] { false, X509ChainStatusFlags.UntrustedRoot, BuildChainCustomTrustStoreTestArguments.TrustedIntermediateUntrustedRoot }; + } + + yield return new object[] { true, X509ChainStatusFlags.NoError, BuildChainCustomTrustStoreTestArguments.UntrustedIntermediateTrustedRoot }; + yield return new object[] { true, X509ChainStatusFlags.NoError, BuildChainCustomTrustStoreTestArguments.TrustedIntermediateTrustedRoot }; + yield return new object[] { true, X509ChainStatusFlags.NoError, BuildChainCustomTrustStoreTestArguments.MultipleCalls }; + } + [Theory] - [InlineData(false, X509ChainStatusFlags.UntrustedRoot, BuildChainCustomTrustStoreTestArguments.TrustedIntermediateUntrustedRoot)] - [InlineData(true, X509ChainStatusFlags.NoError, BuildChainCustomTrustStoreTestArguments.UntrustedIntermediateTrustedRoot)] - [InlineData(true, X509ChainStatusFlags.NoError, BuildChainCustomTrustStoreTestArguments.TrustedIntermediateTrustedRoot)] - [InlineData(true, X509ChainStatusFlags.NoError, BuildChainCustomTrustStoreTestArguments.MultipleCalls)] + [MemberData(nameof(BuildChainCustomTrustStoreData))] public static void BuildChainCustomTrustStore( bool chainBuildsSuccessfully, X509ChainStatusFlags chainFlags, BuildChainCustomTrustStoreTestArguments testArguments) { - using (var microsoftDotCom = new X509Certificate2(TestData.MicrosoftDotComSslCertBytes)) - using (var chainHolderPrep = new ChainHolder()) + using (var endCert = new X509Certificate2(TestData.MicrosoftDotComSslCertBytes)) + using (var issuerCert = new X509Certificate2(TestData.MicrosoftDotComIssuerBytes)) + using (var rootCert = new X509Certificate2(TestData.MicrosoftDotComRootBytes)) + using (var chainHolder = new ChainHolder()) { - X509Chain chainPrep = chainHolderPrep.Chain; - chainPrep.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - chainPrep.ChainPolicy.VerificationTime = microsoftDotCom.NotBefore.AddSeconds(1); - - chainPrep.Build(microsoftDotCom); - X509Certificate2 rootCert = chainPrep.ChainElements[2].Certificate; + X509Chain chainTest = chainHolder.Chain; + chainTest.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chainTest.ChainPolicy.VerificationTime = endCert.NotBefore.AddSeconds(1); + chainTest.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + chainTest.ChainPolicy.ExtraStore.Add(issuerCert); - using (var chainHolderTest = new ChainHolder()) + switch (testArguments) { - X509Chain chainTest = chainHolderTest.Chain; - chainTest.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - chainTest.ChainPolicy.VerificationTime = microsoftDotCom.NotBefore.AddSeconds(1); - chainTest.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - - switch (testArguments) - { - case BuildChainCustomTrustStoreTestArguments.TrustedIntermediateUntrustedRoot: - chainTest.ChainPolicy.ExtraStore.Add(rootCert); - break; - case BuildChainCustomTrustStoreTestArguments.UntrustedIntermediateTrustedRoot: - chainTest.ChainPolicy.CustomTrustStore.Add(rootCert); - break; - case BuildChainCustomTrustStoreTestArguments.TrustedIntermediateTrustedRoot: - chainTest.ChainPolicy.CustomTrustStore.Add(rootCert); - break; - case BuildChainCustomTrustStoreTestArguments.MultipleCalls: - chainTest.ChainPolicy.CustomTrustStore.Add(rootCert); - chainTest.Build(microsoftDotCom); - chainHolderTest.DisposeChainElements(); - chainTest.ChainPolicy.CustomTrustStore.Remove(rootCert); - chainTest.ChainPolicy.TrustMode = X509ChainTrustMode.System; - break; - default: - throw new InvalidDataException(); - } - - Assert.Equal(chainBuildsSuccessfully, chainTest.Build(microsoftDotCom)); - Assert.Equal(3, chainTest.ChainElements.Count); - Assert.Equal(chainFlags, chainTest.AllStatusFlags()); + case BuildChainCustomTrustStoreTestArguments.TrustedIntermediateUntrustedRoot: + chainTest.ChainPolicy.ExtraStore.Add(rootCert); + break; + case BuildChainCustomTrustStoreTestArguments.UntrustedIntermediateTrustedRoot: + chainTest.ChainPolicy.CustomTrustStore.Add(rootCert); + break; + case BuildChainCustomTrustStoreTestArguments.TrustedIntermediateTrustedRoot: + chainTest.ChainPolicy.CustomTrustStore.Add(rootCert); + break; + case BuildChainCustomTrustStoreTestArguments.MultipleCalls: + chainTest.ChainPolicy.CustomTrustStore.Add(rootCert); + chainTest.Build(endCert); + chainHolder.DisposeChainElements(); + chainTest.ChainPolicy.CustomTrustStore.Remove(rootCert); + chainTest.ChainPolicy.TrustMode = X509ChainTrustMode.System; + break; + default: + throw new InvalidDataException(); } + + Assert.Equal(chainBuildsSuccessfully, chainTest.Build(endCert)); + Assert.Equal(3, chainTest.ChainElements.Count); + Assert.Equal(chainFlags, chainTest.AllStatusFlags()); } } @@ -448,10 +474,21 @@ public static void VerifyExpiration_LocalTime(DateTime verificationTime, bool sh Assert.Equal(shouldBeValid, builtSuccessfully); - // If we failed to build the chain, ensure that NotTimeValid is one of the reasons. + // If we failed to build the chain, validate the chain status if (!shouldBeValid) { - Assert.Contains(chain.ChainStatus, s => s.Status == X509ChainStatusFlags.NotTimeValid); + if (PlatformDetection.IsAndroid) + { + // Android always validates timestamp as part of building a path, + // so invalid time comes back as PartialChain with no elements + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + Assert.Equal(0, chain.ChainElements.Count); + } + else + { + // Ensure that NotTimeValid is one of the reasons. + Assert.Contains(chain.ChainStatus, s => s.Status == X509ChainStatusFlags.NotTimeValid); + } } } } @@ -459,20 +496,19 @@ public static void VerifyExpiration_LocalTime(DateTime verificationTime, bool sh [Fact] public static void BuildChain_WithApplicationPolicy_Match() { - using (var msCer = new X509Certificate2(TestData.MsCertificate)) + using (var cert = new X509Certificate2(TestData.CertWithEnhancedKeyUsage)) using (var chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; // Code Signing chain.ChainPolicy.ApplicationPolicy.Add(new Oid("1.3.6.1.5.5.7.3.3")); - chain.ChainPolicy.VerificationTime = msCer.NotBefore.AddHours(2); - chain.ChainPolicy.VerificationFlags = - X509VerificationFlags.AllowUnknownCertificateAuthority; + chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); + chain.AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(cert); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - bool valid = chain.Build(msCer); + bool valid = chain.Build(cert); Assert.True(valid, "Chain built validly"); } } @@ -480,15 +516,14 @@ public static void BuildChain_WithApplicationPolicy_Match() [Fact] public static void BuildChain_WithApplicationPolicy_NoMatch() { - using (var cert = new X509Certificate2(TestData.MsCertificate)) + using (var cert = new X509Certificate2(TestData.CertWithEnhancedKeyUsage)) using (var chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; // Gibberish. (Code Signing + ".1") chain.ChainPolicy.ApplicationPolicy.Add(new Oid("1.3.6.1.5.5.7.3.3.1")); - chain.ChainPolicy.VerificationFlags = - X509VerificationFlags.AllowUnknownCertificateAuthority; + chain.AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(cert); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); @@ -517,9 +552,8 @@ public static void BuildChain_WithCertificatePolicy_Match() // Code Signing chain.ChainPolicy.CertificatePolicy.Add(new Oid("2.18.19")); - chain.ChainPolicy.VerificationFlags = - X509VerificationFlags.AllowUnknownCertificateAuthority; chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); + chain.AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(cert); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; @@ -537,11 +571,10 @@ public static void BuildChain_WithCertificatePolicy_NoMatch() X509Chain chain = chainHolder.Chain; chain.ChainPolicy.CertificatePolicy.Add(new Oid("2.999")); - chain.ChainPolicy.VerificationFlags = - X509VerificationFlags.AllowUnknownCertificateAuthority; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); + chain.AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(cert); bool valid = chain.Build(cert); Assert.False(valid, "Chain built validly"); @@ -744,6 +777,12 @@ public static void InvalidSelfSignedSignature() X509ChainStatusFlags.UntrustedRoot | X509ChainStatusFlags.PartialChain; } + else if (OperatingSystem.IsAndroid()) + { + // Android always validates signature as part of building a path, + // so invalid signature comes back as PartialChain with no elements + expectedFlags = X509ChainStatusFlags.PartialChain; + } else { expectedFlags = @@ -784,6 +823,9 @@ public static void InvalidSelfSignedSignature() } [Fact] + // Android does not support the detailed status in this test. It always validates time + // and trusted root. It will fail to build any chain if those are not valid. + [PlatformSpecific(~TestPlatforms.Android)] public static void ChainErrorsAtMultipleLayers() { // These certificates were generated for this test using CertificateRequest @@ -868,6 +910,7 @@ public static void ChainErrorsAtMultipleLayers() } [Fact] + [PlatformSpecific(~TestPlatforms.Android)] // Chain building on Android fails with an empty subject public static void ChainWithEmptySubject() { using (var cert = new X509Certificate2(TestData.EmptySubjectCertificate)) @@ -876,7 +919,7 @@ public static void ChainWithEmptySubject() { X509Chain chain = chainHolder.Chain; chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority; + chain.AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(issuer); chain.ChainPolicy.ExtraStore.Add(issuer); Assert.True(chain.Build(cert), "chain.Build(cert)"); @@ -893,15 +936,18 @@ public static void BuildInvalidSignatureTwice() byte[] bytes = (byte[])TestData.MsCertificate.Clone(); bytes[bytes.Length - 1] ^= 0xFF; + using (X509Certificate2 microsoftDotComIssuer = new X509Certificate2(TestData.MicrosoftDotComIssuerBytes)) + using (X509Certificate2 microsoftDotComRoot = new X509Certificate2(TestData.MicrosoftDotComRootBytes)) using (X509Certificate2 cert = new X509Certificate2(bytes)) using (ChainHolder chainHolder = new ChainHolder()) { X509Chain chain = chainHolder.Chain; chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); - chain.ChainPolicy.VerificationFlags = - X509VerificationFlags.AllowUnknownCertificateAuthority; + chain.AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(microsoftDotComRoot); chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + chain.ChainPolicy.ExtraStore.Add(microsoftDotComRoot); + chain.ChainPolicy.ExtraStore.Add(microsoftDotComIssuer); int iter = 0; @@ -924,6 +970,14 @@ void CheckChain() X509ChainStatusFlags.PartialChain, allFlags); } + else if (OperatingSystem.IsAndroid()) + { + // Android always validates signature as part of building a path, + // so invalid signature comes back as PartialChain with no elements + Assert.Equal(X509ChainStatusFlags.PartialChain, allFlags); + Assert.Equal(0, chain.ChainElements.Count); + Assert.False(valid, $"Chain should not be valid"); + } else { // These asserts are "most informative first". @@ -1002,13 +1056,23 @@ public static void BuildChainForFraudulentCertificate() chain.ChainPolicy.VerificationTime = cert.NotBefore.AddHours(2); Assert.False(chain.Build(cert)); - X509ChainElement certElement = chain.ChainElements - .OfType() - .Single(e => e.Certificate.Subject == cert.Subject); + if (PlatformDetection.IsAndroid) + { + // Android always validates trust as part of building a path, + // so violations comes back as PartialChain with no elements + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + Assert.Equal(0, chain.ChainElements.Count); + } + else + { + X509ChainElement certElement = chain.ChainElements + .OfType() + .Single(e => e.Certificate.Subject == cert.Subject); - const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.ExplicitDistrust; - X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); - Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); + const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.ExplicitDistrust; + X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); + Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); + } } } @@ -1079,13 +1143,23 @@ public static void BuildChainForCertificateSignedWithDisallowedKey() chain.ChainPolicy.ExtraStore.Add(intermediateCert); Assert.False(chain.Build(cert)); - X509ChainElement certElement = chain.ChainElements - .OfType() - .Single(e => e.Certificate.Subject == intermediateCert.Subject); + if (PlatformDetection.IsAndroid) + { + // Android always validates trust as part of building a path, + // so violations comes back as PartialChain with no elements + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + Assert.Equal(0, chain.ChainElements.Count); + } + else + { + X509ChainElement certElement = chain.ChainElements + .OfType() + .Single(e => e.Certificate.Subject == intermediateCert.Subject); - const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.ExplicitDistrust; - X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); - Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); + const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.ExplicitDistrust; + X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); + Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); + } } } @@ -1166,5 +1240,28 @@ internal static X509ChainStatusFlags AllStatusFlags(this X509ChainElement chainE X509ChainStatusFlags.NoError, (f, s) => f | s.Status); } + + internal static void AllowUnknownAuthorityOrAddSelfSignedToCustomTrust(this X509Chain chain, X509Certificate2 cert) + { + if (!PlatformDetection.IsAndroid) + { + chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority; + return; + } + + // Many tests set AllowUnknownCertificateAuthority in order to build a valid chain for testing the + // validation of other properties. + // Android does not support building a path that does not lead to a trusted root. Using a custom + // root trust with a self-signed cert allows for building a valid chain with the cert. + if (cert.SubjectName.RawData.SequenceEqual(cert.IssuerName.RawData)) + { + chain.ChainPolicy.CustomTrustStore.Add(cert); + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + } + else + { + Assert.True(false, "Could not configure chain policy to handle unknown certificate authority"); + } + } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs index 7c1cec9f1e37c..223bd49fecbc9 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs @@ -3,6 +3,7 @@ using System.Formats.Asn1; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Test.Cryptography; using Xunit; @@ -71,10 +72,12 @@ public static void BuildInvalidSignatureTwice( X509ChainStatusFlags intermediateErrors, X509ChainStatusFlags rootErrors) { + string testName = $"{nameof(BuildInvalidSignatureTwice)} {endEntityErrors} {intermediateErrors} {rootErrors}"; TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, - out X509Certificate2 rootCert); + out X509Certificate2 rootCert, + testName: testName); X509Certificate2 TamperIfNeeded(X509Certificate2 input, X509ChainStatusFlags flags) { @@ -142,6 +145,15 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl } } } + else if (OperatingSystem.IsAndroid()) + { + // Android always validates signature as part of building a path, + // so invalid signature comes back as PartialChain with no elements + expectedCount = 0; + endEntityErrors = X509ChainStatusFlags.PartialChain; + intermediateErrors = X509ChainStatusFlags.PartialChain; + rootErrors = X509ChainStatusFlags.PartialChain; + } else if (OperatingSystem.IsWindows()) { // Windows only reports NotTimeValid on the start-of-chain (end-entity in this case) @@ -153,11 +165,21 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl X509ChainStatusFlags expectedAllErrors = endEntityErrors | intermediateErrors | rootErrors; - // If PartialChain or UntrustedRoot are the only remaining errors, the chain will succeed. - const X509ChainStatusFlags SuccessCodes = - X509ChainStatusFlags.UntrustedRoot | X509ChainStatusFlags.PartialChain; + bool expectSuccess; + if (PlatformDetection.IsAndroid) + { + // Android always validates signature as part of building a path, so chain + // building is expected to fail + expectSuccess = false; + } + else + { + // If PartialChain or UntrustedRoot are the only remaining errors, the chain will succeed. + const X509ChainStatusFlags SuccessCodes = + X509ChainStatusFlags.UntrustedRoot | X509ChainStatusFlags.PartialChain; - bool expectSuccess = (expectedAllErrors & ~SuccessCodes) == 0; + expectSuccess = (expectedAllErrors & ~SuccessCodes) == 0; + } using (endEntityCert) using (intermediateCert) @@ -170,8 +192,12 @@ DateTime RewindIfNeeded(DateTime input, X509Certificate2 cert, X509ChainStatusFl chain.ChainPolicy.ExtraStore.Add(intermediateCert); chain.ChainPolicy.ExtraStore.Add(rootCert); - chain.ChainPolicy.VerificationFlags |= - X509VerificationFlags.AllowUnknownCertificateAuthority; + // Android doesn't respect AllowUnknownCertificateAuthority + if (!PlatformDetection.IsAndroid) + { + chain.ChainPolicy.VerificationFlags |= + X509VerificationFlags.AllowUnknownCertificateAuthority; + } int i = 0; @@ -193,7 +219,10 @@ void CheckChain() Assert.Equal(expectedCount, chain.ChainElements.Count); Assert.Equal(expectedAllErrors, chain.AllStatusFlags()); - Assert.Equal(endEntityErrors, chain.ChainElements[0].AllStatusFlags()); + if (expectedCount > 0) + { + Assert.Equal(endEntityErrors, chain.ChainElements[0].AllStatusFlags()); + } if (expectedCount > 2) { @@ -257,7 +286,7 @@ public static void BasicConstraints_ExceedMaximumPathLength() chain.ChainPolicy.ExtraStore.Add(intermediateCert2); Assert.False(chain.Build(endEntityCert)); - Assert.Equal(X509ChainStatusFlags.InvalidBasicConstraints, chain.AllStatusFlags()); + Assert.Equal(PlatformBasicConstraints(X509ChainStatusFlags.InvalidBasicConstraints), chain.AllStatusFlags()); } } @@ -287,7 +316,7 @@ public static void BasicConstraints_ViolatesCaFalse() rootCert, intermediateCert, endEntityCert, - expectedFlags: X509ChainStatusFlags.InvalidBasicConstraints); + expectedFlags: PlatformBasicConstraints(X509ChainStatusFlags.InvalidBasicConstraints)); } } @@ -319,15 +348,26 @@ public static void TestLeafCertificateWithUnknownCriticalExtension() chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; Assert.False(chain.Build(cert)); - X509ChainElement certElement = chain.ChainElements.OfType().Single(); - const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.HasNotSupportedCriticalExtension; - X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); - Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); + if (PlatformDetection.IsAndroid) + { + // Android always unsupported critical extensions as part of building a path, + // so errors comes back as PartialChain with no elements + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + Assert.Equal(0, chain.ChainElements.Count); + } + else + { + X509ChainElement certElement = chain.ChainElements.OfType().Single(); + const X509ChainStatusFlags ExpectedFlag = X509ChainStatusFlags.HasNotSupportedCriticalExtension; + X509ChainStatusFlags actualFlags = certElement.AllStatusFlags(); + Assert.True((actualFlags & ExpectedFlag) == ExpectedFlag, $"Has expected flag {ExpectedFlag} but was {actualFlags}"); + } } } } [Fact] + [PlatformSpecific(~TestPlatforms.Android)] // Android does not support AIA fetching public static void TestInvalidAia() { using (RSA key = RSA.Create()) @@ -405,10 +445,12 @@ public static void CustomRootTrustDoesNotTrustIntermediates( bool saveAllInCustomTrustStore, X509ChainStatusFlags chainFlags) { + string testName = $"{nameof(CustomRootTrustDoesNotTrustIntermediates)} {saveAllInCustomTrustStore} {chainFlags}"; TestDataGenerator.MakeTestChain3( out X509Certificate2 endEntityCert, out X509Certificate2 intermediateCert, - out X509Certificate2 rootCert); + out X509Certificate2 rootCert, + testName: testName); using (endEntityCert) using (intermediateCert) @@ -430,9 +472,19 @@ public static void CustomRootTrustDoesNotTrustIntermediates( chain.ChainPolicy.ExtraStore.Add(rootCert); } - Assert.Equal(saveAllInCustomTrustStore, chain.Build(endEntityCert)); - Assert.Equal(3, chain.ChainElements.Count); - Assert.Equal(chainFlags, chain.AllStatusFlags()); + if (PlatformDetection.IsAndroid && !saveAllInCustomTrustStore) + { + // Android does not support an empty custom root trust + // Only self-issued certs are treated as trusted anchors, so building the chain + // should through PNSE even though the intermediate cert is added to the store + Assert.Throws(() => chain.Build(endEntityCert)); + } + else + { + Assert.Equal(saveAllInCustomTrustStore, chain.Build(endEntityCert)); + Assert.Equal(3, chain.ChainElements.Count); + Assert.Equal(chainFlags, chain.AllStatusFlags()); + } } } @@ -454,9 +506,17 @@ public static void CustomTrustModeWithNoCustomTrustCerts() chain.ChainPolicy.VerificationTime = endEntityCert.NotBefore.AddSeconds(1); chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - Assert.False(chain.Build(endEntityCert)); - Assert.Equal(1, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + if (PlatformDetection.IsAndroid) + { + // Android does not support an empty custom root trust + Assert.Throws(() => chain.Build(endEntityCert)); + } + else + { + Assert.False(chain.Build(endEntityCert)); + Assert.Equal(1, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags()); + } } } @@ -481,8 +541,16 @@ public static void NameConstraintViolation_ExcludedTree_Dns() SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder(); builder.AddDnsName("www.example.com"); - // excluded DNS name constraint for example.com. + // excluded DNS name constraint for .example.com. string nameConstraints = "3012A110300E820C2E6578616D706C652E636F6D"; + if (PlatformDetection.IsAndroid) + { + // Android does not consider the constraint as being violated when it has + // the leading period. It checks expects the period as part of the left-side + // labels and not the constraint when doing validation. + // Use an excluded DNS name constraint without the period: example.com + nameConstraints = "3011A10F300D820B6578616D706C652E636F6D"; + } TestNameConstrainedChain(nameConstraints, builder, (bool result, X509Chain chain) => { Assert.False(result, "chain.Build"); @@ -507,7 +575,9 @@ public static void NameConstraintViolation_PermittedTree_HasMin() } [Fact] - [PlatformSpecific(~TestPlatforms.Windows)] // Windows seems to skip over nonsense GeneralNames. + // Windows seems to skip over nonsense GeneralNames. + // Android will check for a match. Since the permitted name does match the subject alt name, it succeeds. + [PlatformSpecific(~(TestPlatforms.Windows | TestPlatforms.Android))] public static void NameConstraintViolation_InvalidGeneralNames() { SubjectAlternativeNameBuilder builder = new SubjectAlternativeNameBuilder(); @@ -761,9 +831,21 @@ public static void PolicyConstraints_Mapped() } } + private static X509ChainStatusFlags PlatformBasicConstraints(X509ChainStatusFlags flags) + { + if (OperatingSystem.IsAndroid()) + { + // Android always validates basic constraints as part of building a path + // so violations comes back as PartialChain with no elements. + flags = X509ChainStatusFlags.PartialChain; + } + + return flags; + } + private static X509ChainStatusFlags PlatformNameConstraints(X509ChainStatusFlags flags) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (OperatingSystem.IsMacOS()) { const X509ChainStatusFlags AnyNameConstraintFlags = X509ChainStatusFlags.HasExcludedNameConstraint | @@ -778,13 +860,19 @@ private static X509ChainStatusFlags PlatformNameConstraints(X509ChainStatusFlags flags |= X509ChainStatusFlags.InvalidNameConstraints; } } + else if (OperatingSystem.IsAndroid()) + { + // Android always validates name constraints as part of building a path + // so violations comes back as PartialChain with no elements. + flags = X509ChainStatusFlags.PartialChain; + } return flags; } private static X509ChainStatusFlags PlatformPolicyConstraints(X509ChainStatusFlags flags) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (OperatingSystem.IsMacOS()) { const X509ChainStatusFlags AnyPolicyConstraintFlags = X509ChainStatusFlags.NoIssuanceChainPolicy; @@ -795,6 +883,12 @@ private static X509ChainStatusFlags PlatformPolicyConstraints(X509ChainStatusFla flags |= X509ChainStatusFlags.InvalidPolicyConstraints; } } + else if (OperatingSystem.IsAndroid()) + { + // Android always validates policy constraints as part of building a path + // so violations comes back as PartialChain with no elements. + flags = X509ChainStatusFlags.PartialChain; + } return flags; } @@ -802,7 +896,8 @@ private static X509ChainStatusFlags PlatformPolicyConstraints(X509ChainStatusFla private static void TestNameConstrainedChain( string intermediateNameConstraints, SubjectAlternativeNameBuilder endEntitySanBuilder, - Action body) + Action body, + [CallerMemberName] string testName = null) { X509Extension[] endEntityExtensions = new [] { @@ -832,7 +927,8 @@ private static void TestNameConstrainedChain( out X509Certificate2 intermediateCert, out X509Certificate2 rootCert, intermediateExtensions: intermediateExtensions, - endEntityExtensions: endEntityExtensions); + endEntityExtensions: endEntityExtensions, + testName: testName); using (endEntityCert) using (intermediateCert) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs index 7f0455ea74d7c..0051bc4da70e6 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs @@ -76,8 +76,7 @@ public static void ExportAsPfx() [Fact] public static void ExportAsPfxWithPassword() { - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Password for testing purpose.")] - const string password = "Cotton"; + const string password = "PLACEHOLDER"; using (X509Certificate2 c1 = new X509Certificate2(TestData.MsCertificate)) { @@ -95,8 +94,7 @@ public static void ExportAsPfxWithPassword() [Fact] public static void ExportAsPfxVerifyPassword() { - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Password for testing purpose.")] - const string password = "Cotton"; + const string password = "PLACEHOLDER"; using (X509Certificate2 c1 = new X509Certificate2(TestData.MsCertificate)) { @@ -111,8 +109,7 @@ public static void ExportAsPfxWithPrivateKeyVerifyPassword() using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) { Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey"); - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Password for testing purpose.")] - const string password = "Cotton"; + const string password = "PLACEHOLDER"; byte[] pfx = cert.Export(X509ContentType.Pkcs12, password); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs index 411ca75782958..346cf59002698 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs @@ -489,7 +489,12 @@ public static void TestECDsaPublicKey_BrainpoolP160r1_ValidatesSignature(byte[] Assert.Equal("1.2.840.10045.2.1", cert.PublicKey.Oid.Value); bool isSignatureValid = publicKey.VerifyData(helloBytes, existingSignature, HashAlgorithmName.SHA256); - Assert.True(isSignatureValid, "isSignatureValid"); + + if (!isSignatureValid) + { + Assert.True(PlatformDetection.IsAndroid, "signature invalid on Android only"); + return; + } unchecked { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index aa6d7af75e798..c3729b97b2532 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -7,6 +7,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests { + [PlatformSpecific(~TestPlatforms.Android)] // Android does not support AIA fetching public static class AiaTests { [Fact] diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.Android.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.Android.cs new file mode 100644 index 0000000000000..fd733341851fc --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.Android.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Security.Cryptography.X509Certificates.Tests.Common; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests +{ + public static partial class DynamicRevocationTests + { + public static bool SupportsDynamicRevocation { get; } = Interop.AndroidCrypto.X509ChainSupportsRevocationOptions(); + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.Default.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.Default.cs new file mode 100644 index 0000000000000..34384d913b4f0 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.Default.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Security.Cryptography.X509Certificates.Tests.Common; +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests +{ + public static partial class DynamicRevocationTests + { + public static bool SupportsDynamicRevocation => true; + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs index d832cb91e33c3..ebf8910855825 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs @@ -10,18 +10,34 @@ namespace System.Security.Cryptography.X509Certificates.Tests.RevocationTests { [OuterLoop("These tests run serially at about 1 second each, and the code shouldn't change that often.")] - public static class DynamicRevocationTests + [ConditionalClass(typeof(DynamicRevocationTests), nameof(SupportsDynamicRevocation))] + public static partial class DynamicRevocationTests { // The CI machines are doing an awful lot of things at once, be generous with the timeout; internal static readonly TimeSpan s_urlRetrievalLimit = TimeSpan.FromSeconds(15); private static readonly Oid s_tlsServerOid = new Oid("1.3.6.1.5.5.7.3.1", null); + private static bool SupportsEntireChainCheck => !PlatformDetection.IsAndroid; + private static readonly X509ChainStatusFlags ThisOsRevocationStatusUnknown = - PlatformDetection.IsOSX ? + PlatformDetection.IsOSX || PlatformDetection.IsAndroid ? X509ChainStatusFlags.RevocationStatusUnknown : X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; + // Android will stop checking after the first revocation error, so any revoked certificates + // after will have RevocationStatusUnknown instead of Revoked + private static readonly X509ChainStatusFlags ThisOsRevokedWithPreviousRevocationError = + PlatformDetection.IsAndroid ? + X509ChainStatusFlags.RevocationStatusUnknown : + X509ChainStatusFlags.Revoked; + + // Android will stop checking after the first revocation error, so any non-revoked certificates + // after will have RevocationStatusUnknown instead of NoError + private static readonly X509ChainStatusFlags ThisOsNoErrorWithPreviousRevocationError = + PlatformDetection.IsAndroid ? + X509ChainStatusFlags.RevocationStatusUnknown : + X509ChainStatusFlags.NoError; private delegate void RunSimpleTest( CertificateAuthority root, @@ -79,10 +95,22 @@ public static IEnumerable AllViableRevocation [MemberData(nameof(AllViableRevocation))] public static void NothingRevoked(PkiOptions pkiOptions) { + bool usingCrl = pkiOptions.HasFlag(PkiOptions.IssuerRevocationViaCrl) || pkiOptions.HasFlag(PkiOptions.EndEntityRevocationViaCrl); SimpleTest( pkiOptions, (root, intermediate, endEntity, holder, responder) => { + if (PlatformDetection.IsAndroid && usingCrl) + { + // Android uses the verification time when determining if a CRL is relevant. If there + // are no relevant CRLs based on that time, the revocation status will be unknown. + // SimpleTest sets the verification time to the end entity's NotBefore + 1 minute, + // while the revocation responder uses the current time to set thisUpdate/nextUpdate. + // If using CRLs, set the verification time to the current time so that fetched CRLs + // will be considered relevant. + holder.Chain.ChainPolicy.VerificationTime = DateTime.UtcNow; + } + SimpleRevocationBody( holder, endEntity, @@ -489,7 +517,7 @@ public static void RevokeEndEntity_RootUnrelatedOcsp(PkiOptions pkiOptions) chain, rootStatus: X509ChainStatusFlags.NoError, issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.NoError); + leafStatus: ThisOsNoErrorWithPreviousRevocationError); Assert.False(chainBuilt, "Chain built with ExcludeRoot."); holder.DisposeChainElements(); @@ -508,10 +536,24 @@ public static void RevokeEndEntity_RootUnrelatedOcsp(PkiOptions pkiOptions) }); } + public static IEnumerable PolicyErrorsNotTimeValidData + { + get + { + // Values are { policyErrors, notTimeValid } + yield return new object[] { true, false}; + + // Android always validates timestamp as part of building a path, + // so we don't include test cases with invalid time here + if (!PlatformDetection.IsAndroid) + { + yield return new object[] { false, true}; + yield return new object[] { true, true}; + } + } + } [Theory] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] + [MemberData(nameof(PolicyErrorsNotTimeValidData))] [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] public static void RevokeIntermediate_PolicyErrors_NotTimeValid(bool policyErrors, bool notTimeValid) { @@ -596,9 +638,7 @@ public static void RevokeIntermediate_PolicyErrors_NotTimeValid(bool policyError } [Theory] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] + [MemberData(nameof(PolicyErrorsNotTimeValidData))] [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] public static void RevokeEndEntity_PolicyErrors_NotTimeValid(bool policyErrors, bool notTimeValid) { @@ -719,7 +759,7 @@ public static void RevokeEndEntity_RootRevocationOffline(PkiOptions pkiOptions) chain, rootStatus: X509ChainStatusFlags.NoError, issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.Revoked); + leafStatus: ThisOsRevokedWithPreviousRevocationError); Assert.False(chainBuilt, "Chain built with ExcludeRoot."); holder.DisposeChainElements(); @@ -737,19 +777,22 @@ public static void RevokeEndEntity_RootRevocationOffline(PkiOptions pkiOptions) Assert.False(chainBuilt, "Chain built with EndCertificateOnly"); holder.DisposeChainElements(); - chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; + if (SupportsEntireChainCheck) + { + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; - chainBuilt = chain.Build(endEntity); + chainBuilt = chain.Build(endEntity); - // Potentially surprising result: Even in EntireChain mode, - // root revocation is NoError, not RevocationStatusUnknown. - AssertChainStatus( - chain, - rootStatus: X509ChainStatusFlags.NoError, - issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.Revoked); + // Potentially surprising result: Even in EntireChain mode, + // root revocation is NoError, not RevocationStatusUnknown. + AssertChainStatus( + chain, + rootStatus: X509ChainStatusFlags.NoError, + issrStatus: ThisOsRevocationStatusUnknown, + leafStatus: X509ChainStatusFlags.Revoked); - Assert.False(chainBuilt, "Chain built with EntireChain"); + Assert.False(chainBuilt, "Chain built with EntireChain"); + } } } @@ -792,7 +835,7 @@ public static void NothingRevoked_RootRevocationOffline(PkiOptions pkiOptions) chain, rootStatus: X509ChainStatusFlags.NoError, issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.NoError); + leafStatus: ThisOsNoErrorWithPreviousRevocationError); Assert.False(chainBuilt, "Chain built with ExcludeRoot."); holder.DisposeChainElements(); @@ -810,19 +853,22 @@ public static void NothingRevoked_RootRevocationOffline(PkiOptions pkiOptions) Assert.True(chainBuilt, "Chain built with EndCertificateOnly"); holder.DisposeChainElements(); - chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; + if (SupportsEntireChainCheck) + { + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; - chainBuilt = chain.Build(endEntity); + chainBuilt = chain.Build(endEntity); - // Potentially surprising result: Even in EntireChain mode, - // root revocation is NoError, not RevocationStatusUnknown. - AssertChainStatus( - chain, - rootStatus: X509ChainStatusFlags.NoError, - issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.NoError); + // Potentially surprising result: Even in EntireChain mode, + // root revocation is NoError, not RevocationStatusUnknown. + AssertChainStatus( + chain, + rootStatus: X509ChainStatusFlags.NoError, + issrStatus: ThisOsRevocationStatusUnknown, + leafStatus: X509ChainStatusFlags.NoError); - Assert.False(chainBuilt, "Chain built with EntireChain"); + Assert.False(chainBuilt, "Chain built with EntireChain"); + } } } @@ -893,6 +939,19 @@ public static void RevokeEndEntityWithExpiredRevocation(PkiOptions pkiOptions) (root, intermediate, endEntity, holder, responder) => { DateTime revocationTime = endEntity.NotBefore; + if (PlatformDetection.IsAndroid) + { + // Android seems to use different times (+/- some buffer) to determine whether or not + // to use the revocation data it fetches. + // CRL : verification time + // OCSP : current time + // This test dynamically build the certs such that the current time falls within their + // period of validity (with more than a one second range), so we should be able to use + // the current time as revocation time and one second past that as verification time. + revocationTime = DateTime.UtcNow; + Assert.True(revocationTime >= endEntity.NotBefore && revocationTime < endEntity.NotAfter); + } + holder.Chain.ChainPolicy.VerificationTime = revocationTime.AddSeconds(1); intermediate.RevocationExpiration = revocationTime; @@ -917,6 +976,20 @@ public static void RevokeIntermediateWithExpiredRevocation(PkiOptions pkiOptions (root, intermediate, endEntity, holder, responder) => { DateTime revocationTime = endEntity.NotBefore; + if (PlatformDetection.IsAndroid) + { + // Android seems to use different times (+/- some buffer) to determine whether or not + // to use the revocation data it fetches. + // CRL : verification time + // OCSP : current time + // This test dynamically build the certs such that the current time falls within their + // period of validity (with more than a one second range), so we should be able to use + // the current time as revocation time and one second past that as verification time. + // This should allow the fetched data from both CRL and OCSP to be considered relevant. + revocationTime = DateTime.UtcNow; + Assert.True(revocationTime >= endEntity.NotBefore && revocationTime < endEntity.NotAfter); + } + holder.Chain.ChainPolicy.VerificationTime = revocationTime.AddSeconds(1); using (X509Certificate2 intermediatePub = intermediate.CloneIssuerCert()) @@ -938,11 +1011,23 @@ public static void RevokeIntermediateWithExpiredRevocation(PkiOptions pkiOptions [MemberData(nameof(AllViableRevocation))] public static void CheckEndEntityWithExpiredRevocation(PkiOptions pkiOptions) { + bool usingCrl = pkiOptions.HasFlag(PkiOptions.IssuerRevocationViaCrl) || pkiOptions.HasFlag(PkiOptions.EndEntityRevocationViaCrl); SimpleTest( pkiOptions, (root, intermediate, endEntity, holder, responder) => { intermediate.RevocationExpiration = endEntity.NotBefore; + if (PlatformDetection.IsAndroid && usingCrl) + { + // Android seems to use different times (+/- some buffer) to determine whether or not + // to use the revocation data it fetches. + // CRL : verification time + // OCSP : current time + // If using CRL, set the verification time to the current time. This should result in + // the fetched CRL for checking the issuer being considered relevant and that for the + // end entity considered irrelevant. + holder.Chain.ChainPolicy.VerificationTime = DateTime.UtcNow; + } RunWithInconclusiveEndEntityRevocation(holder, endEntity); }); @@ -953,11 +1038,23 @@ public static void CheckEndEntityWithExpiredRevocation(PkiOptions pkiOptions) [ActiveIssue("https://github.com/dotnet/runtime/issues/31249", TestPlatforms.OSX)] public static void CheckIntermediateWithExpiredRevocation(PkiOptions pkiOptions) { + bool usingCrl = pkiOptions.HasFlag(PkiOptions.IssuerRevocationViaCrl) || pkiOptions.HasFlag(PkiOptions.EndEntityRevocationViaCrl); SimpleTest( pkiOptions, (root, intermediate, endEntity, holder, responder) => { root.RevocationExpiration = endEntity.NotBefore; + if (PlatformDetection.IsAndroid && usingCrl) + { + // Android seems to use different times (+/- some buffer) to determine whether or not + // to use the revocation data it fetches. + // CRL : verification time + // OCSP : current time + // If using CRL, set the verification time to the current time. This should result in + // the fetched CRL for checking the issuer being considered irrelevant and that for the + // end entity considered relevant. + holder.Chain.ChainPolicy.VerificationTime = DateTime.UtcNow; + } RunWithInconclusiveIntermediateRevocation(holder, endEntity); }); @@ -977,12 +1074,26 @@ public static void TestRevocationWithNoNextUpdate_NotRevoked() // cache. We don't care about the build result. holder.Chain.Build(endEntity); - SimpleRevocationBody( - holder, - endEntity, - rootRevoked: false, - issrRevoked: false, - leafRevoked: false); + if (PlatformDetection.IsAndroid) + { + // Android uses the verification time when determining if a CRL is relevant. + // Set the verification time to the current time so that fetched CRLs will + // be considered relevant. + holder.Chain.ChainPolicy.VerificationTime = DateTime.UtcNow; + + // Android treats not having NextUpdate as invalid for determining revocation status, + // so the revocation status will be unknown + RunWithInconclusiveEndEntityRevocation(holder, endEntity); + } + else + { + SimpleRevocationBody( + holder, + endEntity, + rootRevoked: false, + issrRevoked: false, + leafRevoked: false); + } }); } @@ -1004,17 +1115,26 @@ public static void TestRevocationWithNoNextUpdate_Revoked() // cache. We don't care about the build result. holder.Chain.Build(endEntity); - SimpleRevocationBody( - holder, - endEntity, - rootRevoked: false, - issrRevoked: false, - leafRevoked: true); + if (PlatformDetection.IsAndroid) + { + // Android treats not having NextUpdate as invalid for determining revocation status, + // so the revocation status will be unknown + RunWithInconclusiveEndEntityRevocation(holder, endEntity); + } + else + { + SimpleRevocationBody( + holder, + endEntity, + rootRevoked: false, + issrRevoked: false, + leafRevoked: true); + } }); } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] //macOS does not support offline chain building. + [PlatformSpecific(~(TestPlatforms.Android | TestPlatforms.OSX))] // Android and macOS do not support offline revocation chain building. public static void TestRevocation_Offline_NotRevoked() { SimpleTest( @@ -1058,7 +1178,7 @@ public static void TestRevocation_Offline_NotRevoked() } [Fact] - [PlatformSpecific(~TestPlatforms.OSX)] //macOS does not support offline chain building. + [PlatformSpecific(~(TestPlatforms.Android | TestPlatforms.OSX))] // Android and macOS do not support offline revocation chain building. public static void TestRevocation_Offline_Revoked() { SimpleTest( @@ -1147,15 +1267,20 @@ private static void CheckRevokedRootDirectly( holder.DisposeChainElements(); X509Chain chain = holder.Chain; - chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; - bool chainBuilt = chain.Build(rootCert); + bool chainBuilt; + if (SupportsEntireChainCheck) + { + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; + chainBuilt = chain.Build(rootCert); + + Assert.Equal(1, chain.ChainElements.Count); + Assert.Equal(X509ChainStatusFlags.Revoked, chain.ChainElements[0].AllStatusFlags()); + Assert.Equal(X509ChainStatusFlags.Revoked, chain.AllStatusFlags()); + Assert.False(chainBuilt, "Chain validated with revoked root self-test, EntireChain"); - Assert.Equal(1, chain.ChainElements.Count); - Assert.Equal(X509ChainStatusFlags.Revoked, chain.ChainElements[0].AllStatusFlags()); - Assert.Equal(X509ChainStatusFlags.Revoked, chain.AllStatusFlags()); - Assert.False(chainBuilt, "Chain validated with revoked root self-test, EntireChain"); + holder.DisposeChainElements(); + } - holder.DisposeChainElements(); chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; chainBuilt = chain.Build(rootCert); @@ -1227,7 +1352,7 @@ private static void RunWithInconclusiveIntermediateRevocation( chain, rootStatus: X509ChainStatusFlags.NoError, issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.NoError); + leafStatus: ThisOsNoErrorWithPreviousRevocationError); Assert.False(chainBuilt, "Chain built with ExcludeRoot (without flags)"); holder.DisposeChainElements(); @@ -1248,6 +1373,12 @@ private static void RunWithInconclusiveIntermediateRevocation( chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown; + if (PlatformDetection.IsAndroid) + { + // Android stops validation at the first failure, so the end certificate would + // end up marked with RevocationStatusUnknown + chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.IgnoreEndRevocationUnknown; + } chainBuilt = chain.Build(endEntity); @@ -1255,7 +1386,7 @@ private static void RunWithInconclusiveIntermediateRevocation( chain, rootStatus: X509ChainStatusFlags.NoError, issrStatus: ThisOsRevocationStatusUnknown, - leafStatus: X509ChainStatusFlags.NoError); + leafStatus: ThisOsNoErrorWithPreviousRevocationError); Assert.True(chainBuilt, "Chain built with ExcludeRoot (with ignore flags)"); } @@ -1281,7 +1412,7 @@ private static void SimpleRevocationBody( AssertRevocationLevel(chain, endEntityCert, false, false, leafRevoked); - if (testWithRootRevocation) + if (testWithRootRevocation && SupportsEntireChainCheck) { holder.DisposeChainElements(); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj index 21c115a7f2288..af574bfff240d 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj @@ -4,11 +4,14 @@ $(DefineConstants);HAVE_THUMBPRINT_OVERLOADS $(DefineConstants);Unix true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS + + + true + true - - + @@ -92,7 +95,27 @@ Link="Common\System\IO\PersistedFiles.Names.Unix.cs" /> - + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs index ad689987d3aec..a36a4017283e1 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs @@ -153,7 +153,7 @@ internal static class TestData -----END CERTIFICATE----- "); - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test password.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test password.")] public const string PfxDataPassword = "12345"; public static SecureString CreatePfxDataPasswordSecureString() @@ -602,7 +602,7 @@ public static SecureString CreatePfxDataPasswordSecureString() ? Dsa1024Pfx_RC2ContentEncryption : Dsa1024Pfx_TripleDESContentEncryption; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test password.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test password.")] public const string Dsa1024PfxPassword = "1234"; public static byte[] Dsa1024Cert = ( @@ -636,6 +636,31 @@ public static SecureString CreatePfxDataPasswordSecureString() "3000302D021500B9316CC7E05C9F79197E0B41F6FD4E3FCEB72A8A0214075505" + "CCAECB18B7EF4C00F9C069FA3BC78014DE").HexToByteArray(); + public static byte[] CertWithEnhancedKeyUsage = ( + "308202BD308201A5A00302010202106144CAE1C8BF3049BB9BF62C08B74F1330" + + "0D06092A864886F70D01010B0500300E310C300A06035504031303466F6F301E" + + "170D3135303330313232343735385A170D3136303330313034343735385A300E" + + "310C300A06035504031303466F6F30820122300D06092A864886F70D01010105" + + "000382010F003082010A0282010100C2B02AED5493FDACE7F0D91118D3433F56" + + "A1320202CCABA9C4A78C8F6B60BC896F6E579E39453C2392C9C12AFC57C0D81E" + + "ACAA92186921A52FE3EC36769780EE60776CB3EEF1327197C05C1D539C105DBF" + + "DD3762C941E892F838DDFA87D0DD5642A1AC526E46CF4FF6F450ABD970189DC6" + + "90ABE886DB65F3B34F7F2F2C60A59A2F85E531C54D4B182878D14D79A9B88EB3" + + "29F14FFCEBBFA0DFDC97D3609C55F0A0D1D242E53B7143ACC64ABB7D985A1E43" + + "9EF3EF0A0F8074574CE48069E0499F9C23F4E496373C0F18EE7AFCA08BCE5993" + + "78637AAE51D2C57793FA899047BEA820D7E3FF1B935A32023BA13234F7D725C7" + + "A8F3C9C16757E19F4710DEE2C0B2E10203010001A317301530130603551D2504" + + "0C300A06082B06010505070303300D06092A864886F70D01010B050003820101" + + "0062A51D36C1E2945D4A7BC80385961084C2A600391D8A8D4F14572A0E994062" + + "6C4071B7533C2FB8894B55AFCEEE6C59E697244D43D136336DF04A595BB7C09F" + + "A2885C43926AA61BECA482EA5A4F62279C123206C8908F299DC41A62C80ECE98" + + "A36D1F12FEC80919E8DEB209C5B02EBD6D98BE00A84CB61781DC3C1585D79C91" + + "0490365DEB5B96899B1469B930CB1CE6874E256B45573F43D7DD16BD4F590B9A" + + "EEAA2E64987B64C6A96DDA5E6CA2EF96357B78ABFAF815DF4D021A15980EC7D9" + + "ECE244B1ADDF8878FC15236101BFA8E804ADA7C36DE088A16E3BEAABB65D1A91" + + "BA7010C7E3AA701A03B843A93AC1A291A791B3DBEEB511CBF73E2252691CBA33" + + "E2").HexToByteArray(); + public static byte[] CertWithPolicies = ( "308201f33082015ca0030201020210134fb7082cf69bbb4930bfc8e1ca446130" + "0d06092a864886f70d0101050500300e310c300a06035504031303466f6f301e" + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs index 5df66a5c18c81..0e729c7661b08 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/TestDataGenerator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Runtime.CompilerServices; namespace System.Security.Cryptography.X509Certificates.Tests { @@ -13,7 +14,8 @@ internal static void MakeTestChain3( out X509Certificate2 rootCert, IEnumerable endEntityExtensions = null, IEnumerable intermediateExtensions = null, - IEnumerable rootExtensions = null) + IEnumerable rootExtensions = null, + [CallerMemberName] string testName = null) { using (RSA rootKey = RSA.Create()) using (RSA intermediateKey = RSA.Create()) @@ -32,7 +34,8 @@ internal static void MakeTestChain3( certs, endEntityExtensions, intermediateExtensions, - rootExtensions); + rootExtensions, + testName); endEntityCert = certs[0]; intermediateCert = certs[1]; @@ -48,7 +51,8 @@ internal static void MakeTestChain4( out X509Certificate2 rootCert, IEnumerable endEntityExtensions = null, IEnumerable intermediateExtensions = null, - IEnumerable rootExtensions = null) + IEnumerable rootExtensions = null, + [CallerMemberName] string testName = null) { using (RSA rootKey = RSA.Create()) using (RSA intermediateKey = RSA.Create()) @@ -68,7 +72,8 @@ internal static void MakeTestChain4( certs, endEntityExtensions, intermediateExtensions, - rootExtensions); + rootExtensions, + testName); endEntityCert = certs[0]; intermediateCert1 = certs[1]; @@ -82,7 +87,8 @@ internal static void MakeTestChain( Span certs, IEnumerable endEntityExtensions, IEnumerable intermediateExtensions, - IEnumerable rootExtensions) + IEnumerable rootExtensions, + string testName) { if (keys.Length < 2) throw new ArgumentException(nameof(keys)); @@ -128,7 +134,7 @@ internal static void MakeTestChain( RSASignaturePadding signaturePadding = RSASignaturePadding.Pkcs1; CertificateRequest rootReq = new CertificateRequest( - "CN=Test Root", + $"CN=Test Root, O=\"{testName}\"", keys[rootIndex], hashAlgorithm, signaturePadding); @@ -155,7 +161,7 @@ internal static void MakeTestChain( presentationNumber++; CertificateRequest intermediateReq = new CertificateRequest( - $"CN=Intermediate Layer {presentationNumber}", + $"CN=Intermediate Layer {presentationNumber}, O=\"{testName}\"", keys[i], hashAlgorithm, signaturePadding); @@ -177,7 +183,7 @@ internal static void MakeTestChain( } CertificateRequest eeReq = new CertificateRequest( - "CN=End-Entity", + $"CN=End-Entity, O=\"{testName}\"", keys[0], hashAlgorithm, signaturePadding); diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs index f8b2ec72e0031..e364614d4ec93 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X500DistinguishedNameTests.cs @@ -223,6 +223,13 @@ public static void OrganizationUnitMultiValueWithIncorrectlySortedDerSet() Assert.Equal("OU=zzzz + OU=aaaa", dn.Decode(X500DistinguishedNameFlags.None)); } + [Fact] + public static void NameWithSTIdentifierForState() + { + X500DistinguishedName dn = new X500DistinguishedName("ST=VA, C=US"); + Assert.Equal("C=US, S=VA", dn.Decode(X500DistinguishedNameFlags.None)); + } + public static readonly object[][] WhitespaceBeforeCases = { // Regular space. diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.Android.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.Android.cs new file mode 100644 index 0000000000000..76e6082691659 --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.Android.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + public static partial class X509StoreMutableTests + { + public static bool PermissionsAllowStoreWrite { get; } = true; + + [Theory] + [InlineData(nameof(TestData.RsaCertificate), TestData.RsaCertificate, TestData.RsaPkcs8Key)] + [InlineData(nameof(TestData.EcDhCertificate), TestData.EcDhCertificate, TestData.EcDhPkcs8Key)] + [InlineData(nameof(TestData.ECDsaCertificate), TestData.ECDsaCertificate, TestData.ECDsaPkcs8Key)] + public static void AddRemove_CertWithPrivateKey(string testCase, string certPem, string keyPem) + { + _ = testCase; + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = X509Certificate2.CreateFromPem(certPem, keyPem)) + { + store.Open(OpenFlags.ReadWrite); + + // Make sure cert is not already in the store + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "Certificate should not be found on pre-condition"); + + // Add + store.Add(cert); + Assert.True(IsCertInStore(cert, store), "Certificate should be found after add"); + Assert.True(StoreHasPrivateKey(store, cert), "Certificate in store should have a private key"); + + // Remove + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "Certificate should not be found after remove"); + } + } + + [Fact] + public static void Add_CertWithPrivateKey_NotSupportedAlgorithm() + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = X509Certificate2.CreateFromPem(TestData.DsaCertificate, TestData.DsaPkcs8Key)) + { + store.Open(OpenFlags.ReadWrite); + + // Make sure cert is not already in the store + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "Certificate should not be found on pre-condition"); + + // Add - throws PNSE + Assert.Throws(() => store.Add(cert)); + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs index 422c71a4988a1..7678da5780b45 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs @@ -5,9 +5,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests { - [OuterLoop("Modifies system state")] [PlatformSpecific(TestPlatforms.OSX)] - public static class X509StoreMutableTests_OSX + public static partial class X509StoreMutableTests { public static bool PermissionsAllowStoreWrite { get; } = TestPermissions(); @@ -116,133 +115,6 @@ public static void AddToStore_Exportable() } } - [ConditionalFact(nameof(PermissionsAllowStoreWrite))] - public static void AddToStoreTwice() - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) - using (var certOnly = new X509Certificate2(cert.RawData)) - { - store.Open(OpenFlags.ReadWrite); - - // Defensive removal. - store.Remove(certOnly); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition"); - - store.Add(cert); - Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add"); - - // No exception for duplicate item. - store.Add(cert); - - // Cleanup - store.Remove(certOnly); - } - } - - [ConditionalFact(nameof(PermissionsAllowStoreWrite))] - public static void AddPrivateAfterPublic() - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) - using (var certOnly = new X509Certificate2(cert.RawData)) - { - store.Open(OpenFlags.ReadWrite); - - // Defensive removal. - store.Remove(certOnly); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition"); - - store.Add(certOnly); - Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add"); - Assert.False(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after public-only add"); - - // Add the private key - store.Add(cert); - Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after PFX add"); - - // Cleanup - store.Remove(certOnly); - } - } - - [ConditionalFact(nameof(PermissionsAllowStoreWrite))] - public static void AddPublicAfterPrivate() - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) - using (var certOnly = new X509Certificate2(cert.RawData)) - { - store.Open(OpenFlags.ReadWrite); - - // Defensive removal. - store.Remove(certOnly); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition"); - - // Add the private key - store.Add(cert); - Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add"); - Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after PFX add"); - - // Add the public key with no private key - store.Add(certOnly); - Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after public-only add"); - - // Cleanup - store.Remove(certOnly); - } - } - - [ConditionalFact(nameof(PermissionsAllowStoreWrite))] - public static void VerifyRemove() - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) - { - store.Open(OpenFlags.ReadWrite); - - // Defensive removal. Sort of circular, but it's the best we can do. - store.Remove(cert); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition"); - - store.Add(cert); - Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after add"); - - store.Remove(cert); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found after remove"); - } - } - - [ConditionalFact(nameof(PermissionsAllowStoreWrite))] - public static void RemovePublicDeletesPrivateKey() - { - using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) - using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) - using (var certOnly = new X509Certificate2(cert.RawData)) - { - store.Open(OpenFlags.ReadWrite); - - // Defensive removal. - store.Remove(cert); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition"); - - // Add the private key - store.Add(cert); - Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after add"); - - store.Remove(certOnly); - Assert.False(IsCertInStore(cert, store), "PtxData certificate was found after remove"); - - // Add back the public key only - store.Add(certOnly); - Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after public-only add"); - Assert.False(StoreHasPrivateKey(store, cert), "Store has a private key for cert after public-only add"); - - // Cleanup - store.Remove(certOnly); - } - } - [ConditionalFact(nameof(PermissionsAllowStoreWrite))] public static void CustomStore_ReadWrite() { @@ -320,47 +192,6 @@ public static void CustomStore_InvalidFileName() Assert.ThrowsAny(() => store.Open(OpenFlags.ReadWrite)); } - private static bool StoreHasPrivateKey(X509Store store, X509Certificate2 forCert) - { - using (ImportedCollection coll = new ImportedCollection(store.Certificates)) - { - foreach (X509Certificate2 storeCert in coll.Collection) - { - if (forCert.Equals(storeCert)) - { - return storeCert.HasPrivateKey; - } - } - } - - Assert.True(false, $"Certificate ({forCert.Subject}) exists in the store"); - return false; - } - - private static bool IsCertInStore(X509Certificate2 cert, X509Store store) - { - using (ImportedCollection coll = new ImportedCollection(store.Certificates)) - { - foreach (X509Certificate2 storeCert in coll.Collection) - { - if (cert.Equals(storeCert)) - { - return true; - } - } - } - - return false; - } - - private static int GetStoreCertificateCount(X509Store store) - { - using (var coll = new ImportedCollection(store.Certificates)) - { - return coll.Collection.Count; - } - } - private class TemporaryX509Store : IDisposable { private X509Store _store; diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.cs new file mode 100644 index 0000000000000..e49b1c81d3a3e --- /dev/null +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Security.Cryptography.X509Certificates.Tests +{ + [OuterLoop("Modifies system state")] + public static partial class X509StoreMutableTests + { + [ConditionalFact(nameof(PermissionsAllowStoreWrite))] + public static void AddToStoreTwice() + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) + using (var certOnly = new X509Certificate2(cert.RawData)) + { + store.Open(OpenFlags.ReadWrite); + + // Defensive removal. + store.Remove(certOnly); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found on pre-condition"); + + store.Add(cert); + Assert.True(IsCertInStore(certOnly, store), "PfxData certificate was found after add"); + + // No exception for duplicate item. + store.Add(cert); + + // Cleanup + store.Remove(certOnly); + } + } + + [ConditionalFact(nameof(PermissionsAllowStoreWrite))] + public static void AddPrivateAfterPublic() + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) + using (var certOnly = new X509Certificate2(cert.RawData)) + { + store.Open(OpenFlags.ReadWrite); + + // Defensive removal. + store.Remove(certOnly); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found on pre-condition"); + + store.Add(certOnly); + Assert.True(IsCertInStore(certOnly, store), "PfxData certificate was found after add"); + Assert.False(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after public-only add"); + + // Add the private key + store.Add(cert); + Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after PFX add"); + + // Cleanup + store.Remove(certOnly); + } + } + + [ConditionalFact(nameof(PermissionsAllowStoreWrite))] + public static void AddPublicAfterPrivate() + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) + using (var certOnly = new X509Certificate2(cert.RawData)) + { + store.Open(OpenFlags.ReadWrite); + + // Defensive removal. + store.Remove(certOnly); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found on pre-condition"); + + // Add the private key + store.Add(cert); + Assert.True(IsCertInStore(certOnly, store), "PfxData certificate was found after add"); + Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after PFX add"); + + // Add the public key with no private key + store.Add(certOnly); + Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after public-only add"); + + // Cleanup + store.Remove(certOnly); + } + } + + [ConditionalTheory(nameof(PermissionsAllowStoreWrite))] + [InlineData(true)] + [InlineData(false)] + public static void VerifyRemove(bool withPrivateKey) + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var certWithPrivateKey = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) + using (var certOnly = new X509Certificate2(certWithPrivateKey.RawData)) + { + X509Certificate2 cert = withPrivateKey ? certWithPrivateKey : certOnly; + store.Open(OpenFlags.ReadWrite); + + // Defensive removal. Sort of circular, but it's the best we can do. + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found on pre-condition"); + + store.Add(cert); + Assert.True(IsCertInStore(cert, store), "PfxData certificate was found after add"); + + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found after remove"); + } + } + + [ConditionalFact(nameof(PermissionsAllowStoreWrite))] + public static void RemovePublicDeletesPrivateKey() + { + using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) + using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable)) + using (var certOnly = new X509Certificate2(cert.RawData)) + { + store.Open(OpenFlags.ReadWrite); + + // Defensive removal. + store.Remove(cert); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found on pre-condition"); + + // Add the private key + store.Add(cert); + Assert.True(IsCertInStore(cert, store), "PfxData certificate was found after add"); + + store.Remove(certOnly); + Assert.False(IsCertInStore(cert, store), "PfxData certificate was found after remove"); + + // Add back the public key only + store.Add(certOnly); + Assert.True(IsCertInStore(cert, store), "PfxData certificate was found after public-only add"); + Assert.False(StoreHasPrivateKey(store, cert), "Store has a private key for cert after public-only add"); + + // Cleanup + store.Remove(certOnly); + } + } + + private static bool StoreHasPrivateKey(X509Store store, X509Certificate2 forCert) + { + using (ImportedCollection coll = new ImportedCollection(store.Certificates)) + { + foreach (X509Certificate2 storeCert in coll.Collection) + { + if (forCert.Equals(storeCert)) + { + return storeCert.HasPrivateKey; + } + } + } + + Assert.True(false, $"Certificate ({forCert.Subject}) exists in the store"); + return false; + } + + private static bool IsCertInStore(X509Certificate2 cert, X509Store store) + { + using (ImportedCollection coll = new ImportedCollection(store.Certificates)) + { + foreach (X509Certificate2 storeCert in coll.Collection) + { + if (cert.Equals(storeCert)) + { + return true; + } + } + } + + return false; + } + + private static int GetStoreCertificateCount(X509Store store) + { + using (var coll = new ImportedCollection(store.Certificates)) + { + return coll.Collection.Count; + } + } + } +} diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs index 71b13d8e0de31..ea0906d73e1d2 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs @@ -9,6 +9,7 @@ using System.IO; using System.Runtime.InteropServices; using Microsoft.DotNet.RemoteExecutor; +using Test.Cryptography; using Xunit; namespace System.Security.Cryptography.X509Certificates.Tests @@ -94,9 +95,9 @@ public static void Constructor_StoreHandle() } } - [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] // API not supported via OpenSSL + [PlatformSpecific(PlatformSupport.OpenSSL)] // API not supported via OpenSSL [Fact] - public static void Constructor_StoreHandle_Unix() + public static void Constructor_StoreHandle_OpenSSL() { using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser)) { diff --git a/src/libraries/System.Security.Cryptography.Xml/ref/System.Security.Cryptography.Xml.csproj b/src/libraries/System.Security.Cryptography.Xml/ref/System.Security.Cryptography.Xml.csproj index 4d4f20e941f50..2ce349313da0e 100644 --- a/src/libraries/System.Security.Cryptography.Xml/ref/System.Security.Cryptography.Xml.csproj +++ b/src/libraries/System.Security.Cryptography.Xml/ref/System.Security.Cryptography.Xml.csproj @@ -7,7 +7,7 @@ true - + diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj b/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj index 908e51bd795fc..bbf3d45bef7d1 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj +++ b/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj @@ -91,7 +91,7 @@ - + diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/AttributeSortOrder.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/AttributeSortOrder.cs index 3c40f356f5fad..c0ff1120f8b18 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/AttributeSortOrder.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/AttributeSortOrder.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // This class does lexicographic sorting by NamespaceURI first and then by LocalName. - internal class AttributeSortOrder : IComparer + internal sealed class AttributeSortOrder : IComparer { internal AttributeSortOrder() { } diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/C14NAncestralNamespaceContextManager.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/C14NAncestralNamespaceContextManager.cs index 88574cc22f47c..9eb170965b7e5 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/C14NAncestralNamespaceContextManager.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/C14NAncestralNamespaceContextManager.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Xml { // the stack of currently active NamespaceFrame contexts. this // object also maintains the inclusive prefix list in a tokenized form. - internal class C14NAncestralNamespaceContextManager : AncestralNamespaceContextManager + internal sealed class C14NAncestralNamespaceContextManager : AncestralNamespaceContextManager { internal C14NAncestralNamespaceContextManager() { } diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXml.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXml.cs index 3ccf5ccecd878..e55dd306cc0a5 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXml.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXml.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { - internal class CanonicalXml + internal sealed class CanonicalXml { private readonly CanonicalXmlDocument _c14nDoc; private readonly C14NAncestralNamespaceContextManager _ancMgr; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs index 3fe25246c8135..ed06c365672b2 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlAttribute.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlAttribute - internal class CanonicalXmlAttribute : XmlAttribute, ICanonicalizableNode + internal sealed class CanonicalXmlAttribute : XmlAttribute, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlCDataSection.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlCDataSection.cs index f85f79d7989c7..ff5d9d4529150 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlCDataSection.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlCDataSection.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlCDataSection - internal class CanonicalXmlCDataSection : XmlCDataSection, ICanonicalizableNode + internal sealed class CanonicalXmlCDataSection : XmlCDataSection, ICanonicalizableNode { private bool _isInNodeSet; public CanonicalXmlCDataSection(string data, XmlDocument doc, bool defaultNodeSetInclusionState) : base(data, doc) diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlComment.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlComment.cs index b14a4b7ce3b82..99c4a2fdbcd7f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlComment.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlComment.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlComment - internal class CanonicalXmlComment : XmlComment, ICanonicalizableNode + internal sealed class CanonicalXmlComment : XmlComment, ICanonicalizableNode { private bool _isInNodeSet; private readonly bool _includeComments; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlDocument.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlDocument.cs index 12caec4df1889..9713d998428f2 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlDocument.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlDocument.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Xml { // all input types eventually lead to the creation of an XmlDocument document // of this type. it maintains the node subset state and performs output rendering during canonicalization - internal class CanonicalXmlDocument : XmlDocument, ICanonicalizableNode + internal sealed class CanonicalXmlDocument : XmlDocument, ICanonicalizableNode { private readonly bool _defaultNodeSetInclusionState; private readonly bool _includeComments; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs index 3b876fc4e6f43..fb1f9c599cfda 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlElement.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlElement - internal class CanonicalXmlElement : XmlElement, ICanonicalizableNode + internal sealed class CanonicalXmlElement : XmlElement, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlEntityReference.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlEntityReference.cs index b3439b42e11e0..f6ae518d57e74 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlEntityReference.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlEntityReference.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlEntityReference - internal class CanonicalXmlEntityReference : XmlEntityReference, ICanonicalizableNode + internal sealed class CanonicalXmlEntityReference : XmlEntityReference, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlNodeList.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlNodeList.cs index 6d09ff769099e..e1308ea884980 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlNodeList.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlNodeList.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Xml { - internal class CanonicalXmlNodeList : XmlNodeList, IList + internal sealed class CanonicalXmlNodeList : XmlNodeList, IList { private readonly ArrayList _nodeArray; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs index af0939e8a7ade..2bc2502eb9f67 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlProcessingInstruction.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlProcessingInstruction - internal class CanonicalXmlProcessingInstruction : XmlProcessingInstruction, ICanonicalizableNode + internal sealed class CanonicalXmlProcessingInstruction : XmlProcessingInstruction, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlSignificantWhitespace.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlSignificantWhitespace.cs index 26fa26c93adf9..cd4ec4820e247 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlSignificantWhitespace.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlSignificantWhitespace.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlSignificantWhitespace - internal class CanonicalXmlSignificantWhitespace : XmlSignificantWhitespace, ICanonicalizableNode + internal sealed class CanonicalXmlSignificantWhitespace : XmlSignificantWhitespace, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlText.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlText.cs index 23b43b269e2be..90982628d4be3 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlText.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlText.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlText - internal class CanonicalXmlText : XmlText, ICanonicalizableNode + internal sealed class CanonicalXmlText : XmlText, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlWhitespace.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlWhitespace.cs index c15b186badee0..38fae307ed4dc 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlWhitespace.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalXmlWhitespace.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { // the class that provides node subset state and canonicalization function to XmlWhitespace - internal class CanonicalXmlWhitespace : XmlWhitespace, ICanonicalizableNode + internal sealed class CanonicalXmlWhitespace : XmlWhitespace, ICanonicalizableNode { private bool _isInNodeSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs index 80a8f8affa010..b634b00fae4d9 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/CanonicalizationDispatcher.cs @@ -8,10 +8,8 @@ namespace System.Security.Cryptography.Xml { // the central dispatcher for canonicalization writes. not all node classes // implement ICanonicalizableNode; so a manual dispatch is sometimes necessary. - internal class CanonicalizationDispatcher + internal static class CanonicalizationDispatcher { - private CanonicalizationDispatcher() { } - public static void Write(XmlNode node, StringBuilder strBuilder, DocPosition docPos, AncestralNamespaceContextManager anc) { if (node is ICanonicalizableNode) diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/DSASignatureDescription.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/DSASignatureDescription.cs index 27a27ca8b2526..1d3fe08ab8dde 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/DSASignatureDescription.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/DSASignatureDescription.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Xml { - internal class DSASignatureDescription : SignatureDescription + internal sealed class DSASignatureDescription : SignatureDescription { private const string HashAlgorithm = "SHA1"; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcAncestralNamespaceContextManager.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcAncestralNamespaceContextManager.cs index 93319b74346ba..eadbdbf565ed9 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcAncestralNamespaceContextManager.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcAncestralNamespaceContextManager.cs @@ -8,7 +8,7 @@ namespace System.Security.Cryptography.Xml { // the stack of currently active NamespaceFrame contexts. this // object also maintains the inclusive prefix list in a tokenized form. - internal class ExcAncestralNamespaceContextManager : AncestralNamespaceContextManager + internal sealed class ExcAncestralNamespaceContextManager : AncestralNamespaceContextManager { private readonly Hashtable _inclusivePrefixSet; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcCanonicalXml.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcCanonicalXml.cs index 29488e29688e2..90dde6370104c 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcCanonicalXml.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/ExcCanonicalXml.cs @@ -7,7 +7,7 @@ namespace System.Security.Cryptography.Xml { - internal class ExcCanonicalXml + internal sealed class ExcCanonicalXml { private readonly CanonicalXmlDocument _c14nDoc; private readonly ExcAncestralNamespaceContextManager _ancMgr; diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/MyXmlDocument.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/MyXmlDocument.cs index 2911787c07e65..808a3bbbd777f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/MyXmlDocument.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/MyXmlDocument.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Xml { - internal class MyXmlDocument : XmlDocument + internal sealed class MyXmlDocument : XmlDocument { protected override XmlAttribute CreateDefaultAttribute(string prefix, string localName, string namespaceURI) { diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceFrame.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceFrame.cs index 2a8ce37fd1a2d..1af976d8fb8a3 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceFrame.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceFrame.cs @@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Xml // the namespaces context corresponding to one XmlElement. the rendered list contains the namespace nodes that are actually // rendered to the canonicalized output. the unrendered list contains the namespace nodes that are in the node set and have // the XmlElement as the owner, but are not rendered. - internal class NamespaceFrame + internal sealed class NamespaceFrame { private readonly Hashtable _rendered = new Hashtable(); private readonly Hashtable _unrendered = new Hashtable(); diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceSortOrder.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceSortOrder.cs index 9973abc00fa8a..1e998ef7e88e2 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceSortOrder.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/NamespaceSortOrder.cs @@ -6,7 +6,7 @@ namespace System.Security.Cryptography.Xml { - internal class NamespaceSortOrder : IComparer + internal sealed class NamespaceSortOrder : IComparer { internal NamespaceSortOrder() { } diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA1SignatureDescription.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA1SignatureDescription.cs index a3d92e475d785..82de982f9cd3a 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA1SignatureDescription.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA1SignatureDescription.cs @@ -5,7 +5,7 @@ namespace System.Security.Cryptography.Xml { - internal class RSAPKCS1SHA1SignatureDescription : RSAPKCS1SignatureDescription + internal sealed class RSAPKCS1SHA1SignatureDescription : RSAPKCS1SignatureDescription { public RSAPKCS1SHA1SignatureDescription() : base("SHA1") { diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA256SignatureDescription.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA256SignatureDescription.cs index a1dd393d79509..21c8967f563b1 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA256SignatureDescription.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA256SignatureDescription.cs @@ -3,7 +3,7 @@ namespace System.Security.Cryptography.Xml { - internal class RSAPKCS1SHA256SignatureDescription : RSAPKCS1SignatureDescription + internal sealed class RSAPKCS1SHA256SignatureDescription : RSAPKCS1SignatureDescription { public RSAPKCS1SHA256SignatureDescription() : base("SHA256") { diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA384SignatureDescription.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA384SignatureDescription.cs index 758b3b7f77742..9d472c896646f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA384SignatureDescription.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA384SignatureDescription.cs @@ -3,7 +3,7 @@ namespace System.Security.Cryptography.Xml { - internal class RSAPKCS1SHA384SignatureDescription : RSAPKCS1SignatureDescription + internal sealed class RSAPKCS1SHA384SignatureDescription : RSAPKCS1SignatureDescription { public RSAPKCS1SHA384SignatureDescription() : base("SHA384") { diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA512SignatureDescription.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA512SignatureDescription.cs index 5c946e21b07ce..0bc99fcc6c1b6 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA512SignatureDescription.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/RSAPKCS1SHA512SignatureDescription.cs @@ -3,7 +3,7 @@ namespace System.Security.Cryptography.Xml { - internal class RSAPKCS1SHA512SignatureDescription : RSAPKCS1SignatureDescription + internal sealed class RSAPKCS1SHA512SignatureDescription : RSAPKCS1SignatureDescription { public RSAPKCS1SHA512SignatureDescription() : base("SHA512") { diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs index 5195007774982..2ad68d28dd83f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXml.cs @@ -833,7 +833,7 @@ private int GetReferenceLevel(int index, ArrayList references) throw new CryptographicException(SR.Cryptography_Xml_InvalidReference); } - private class ReferenceLevelSortOrder : IComparer + private sealed class ReferenceLevelSortOrder : IComparer { private ArrayList _references; public ReferenceLevelSortOrder() { } diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs index 509934612ebbe..88333153fd4e7 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/SignedXmlDebugLog.cs @@ -237,7 +237,7 @@ private static string GetKeyName(object key) keyName = key.GetHashCode().ToString("x8", CultureInfo.InvariantCulture); } - return string.Format(CultureInfo.InvariantCulture, "{0}#{1}", key.GetType().Name, keyName); + return $"{key.GetType().Name}#{keyName}"; } /// @@ -247,9 +247,7 @@ private static string GetObjectId(object o) { Debug.Assert(o != null, "o != null"); - return string.Format(CultureInfo.InvariantCulture, - "{0}#{1}", o.GetType().Name, - o.GetHashCode().ToString("x8", CultureInfo.InvariantCulture)); + return $"{o.GetType().Name}#{o.GetHashCode().ToString("x8", CultureInfo.InvariantCulture)}"; } /// diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs index 123afc5d21056..512bc11826a61 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Utils.cs @@ -10,7 +10,7 @@ namespace System.Security.Cryptography.Xml { - internal class Utils + internal static class Utils { // The maximum number of characters in an XML document (0 means no limit). internal const int MaxCharactersInDocument = 0; @@ -23,8 +23,6 @@ internal class Utils // Keeping this number low will preserve some stack space internal const int XmlDsigSearchDepth = 20; - private Utils() { } - private static bool HasNamespace(XmlElement element, string prefix, string value) { if (IsCommittedNamespace(element, prefix, value)) return true; @@ -425,7 +423,7 @@ internal static string EscapeWhitespaceData(string data) StringBuilder sb = new StringBuilder(); sb.Append(data); Utils.SBReplaceCharWithString(sb, (char)13, " "); - return sb.ToString(); ; + return sb.ToString(); } internal static string EscapeTextData(string data) @@ -436,7 +434,7 @@ internal static string EscapeTextData(string data) sb.Replace("<", "<"); sb.Replace(">", ">"); SBReplaceCharWithString(sb, (char)13, " "); - return sb.ToString(); ; + return sb.ToString(); } internal static string EscapeCData(string data) diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs index f547ba74dda2e..79b19f77026d3 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTest.cs @@ -139,10 +139,10 @@ public void Sample2() { using (Aes aes = Aes.Create()) { + byte[] keydata = Convert.FromBase64String("o/ilseZu+keLBBWGGPlUHweqxIPc4gzZEFWr2nBt640="); aes.Mode = CipherMode.CBC; aes.KeySize = 256; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] - aes.Key = Convert.FromBase64String("o/ilseZu+keLBBWGGPlUHweqxIPc4gzZEFWr2nBt640="); + aes.Key = keydata; aes.Padding = PaddingMode.Zeros; XmlDocument doc = new XmlDocument(); @@ -171,11 +171,11 @@ public void RoundtripSample1() using (Aes aes = Aes.Create()) { + byte[] keydata = Convert.FromBase64String("o/ilseZu+keLBBWGGPlUHweqxIPc4gzZEFWr2nBt640="); aes.Mode = CipherMode.CBC; aes.KeySize = 256; aes.IV = Convert.FromBase64String("pBUM5P03rZ6AE4ZK5EyBrw=="); - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] - aes.Key = Convert.FromBase64String("o/ilseZu+keLBBWGGPlUHweqxIPc4gzZEFWr2nBt640="); + aes.Key = keydata; aes.Padding = PaddingMode.Zeros; EncryptedXml exml = new EncryptedXml(); @@ -203,10 +203,10 @@ public void RoundtripSample1() { using (Aes aes = Aes.Create()) { + byte[] keydata = Convert.FromBase64String("o/ilseZu+keLBBWGGPlUHweqxIPc4gzZEFWr2nBt640="); aes.Mode = CipherMode.CBC; aes.KeySize = 256; - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test key.")] - aes.Key = Convert.FromBase64String("o/ilseZu+keLBBWGGPlUHweqxIPc4gzZEFWr2nBt640="); + aes.Key = keydata; aes.Padding = PaddingMode.Zeros; XmlDocument doc = new XmlDocument(); diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTests.cs b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTests.cs index 286c089a23787..281d74a50d2b1 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTests.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/EncryptedXmlTests.cs @@ -11,6 +11,7 @@ namespace System.Security.Cryptography.Xml.Tests public static class EncryptedXmlTests { [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49871", TestPlatforms.Android)] public static void DecryptWithCertificate_NotInStore() { const string SecretMessage = "Grilled cheese is tasty"; diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs b/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs index 4cdcd7d483346..c2809ce29d898 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/TestHelpers.cs @@ -185,7 +185,7 @@ public static IEnumerable GetSymmetricAlgorithms(bool } private static readonly byte[] SamplePfx = Convert.FromBase64String( - // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Unit test dummy certificate.")] + // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Unit test dummy certificate.")] @"MIIFpQIBAzCCBV8GCSqGSIb3DQEHAaCCBVAEggVMMIIFSDCCAl8GCSqGSIb3DQEHBqCCAlAwggJMAgEAMIICRQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQIGTfVa4+vR1UCAgfQgIICGJuFE9alFWJFkaoeewKDIEnVwRxXfMsi8dcySYnp7jljEUQBfW/GIbOf7Lg2nHd0qxvxYI2YL4Zs+d0jWbqfNHamGFCMPe1dK957Z2PsKXR183vMSgnmlLAHktsIN+Gor7q1GbQ4ljfZkGqZ/rkgUsgsSYZSnJevP/uH0VnvxemljVJ7N7gKMYO0aqrca4qJ0O4YxBYyaerPFUOYunQlvk6DOF3SQXza5oFKcPGrSpE/9eQrnmm64BtbdnUE6qqEjfZfNa6MOD3vOnapLUBsel2TtVCu8tEl7I8FGxozTLXVTXOBkL3k7xLRS52ZtpbcU2JIhlDGpxeFXmjKYzdzHoL20iJubfdkUYtHwB0XjBKKLcI7jfgGgjNauaTLAx8FF+5O9s7Zbj2+SKWv56kqAwdX+iH21VgjAN9EByIXHb3p2ZOvy4ONDXTmfSn7jbuPLZTi+u6bxn2JOLf/gjEA8FiCuQDL9gF247bnUq08Z1uzuAUeaPL13U8mxwEuvCOXx5NEQIuf3cusnaH4+7uIhPk5tnfA5XOaABySetRjZhVN5dC5/g3KTwmaDamlW3Y7Az/NzAC4uKa2ny5jwYKBgHviEKOyJfLDKr5fOMRToOfgxvAdXZohQQTE1+TcBjp+eeV5koDfB1ReCKIRHugPZu5j9SCVcYanwFeJ5M4cEHZ9U1Ytsmzjh0fwV17D/hxQ4aS4VwVpOMypMIIC4QYJKoZIhvcNAQcBoIIC0gSCAs4wggLKMIICxgYLKoZIhvcNAQwKAQKgggKeMIICmjAcBgoqhkiG9w0BDAEDMA4ECBRdKqx022cfAgIH0ASCAnjZx9fvPCHizdH6apVzWWmfy/84HvDPjFOUV1TPehTnDPkNpF/uK/ya4jlbl4Kw0Zfknt5Xydl89SMXIWa2q+nWmxyG3XyfGqOAeBfJBSdCF5K3qkZZnzEfraKZZ5Hh8IEmK+ey45O6sltua6Xl5MRBmKLiwma7vX4ihXQTMfb0WlWDYCXZi85OeF0OlUjRWAwz4PeeiBK4nmI/vNmF1EzDVdZGkrrE8mot3Y4z6bvwqip2tUUbHuMnC+/1ikAcJzCOw4NpnEWCRtIJxgJ9es8E8CUfHESnWKe4nh6tJVJ15B8/7oF7N6j7oq4Oj346JthKoWWkzifNaH79A60/uFh08Rv7zrtJf6kedY6Ve2bR5lhWn0cv9Q6IaoqTmKKTmKJnjdQO9lKRCR6iI2OsYtXBropD8xhNNqsyfpNmP0G6wFiEZZxZjWOkZEJLUzFbH+Su+7l2l4FN9sM7k211/l3/3YF1QJHwZsgL98DZL4qE+nkuZQcdtOUx8QTyTOcVb3IzgCAwZm0rgdXQpJ9yRBgOC/6MnqaCPI0jJuavXF/a28GJWWGlazx7SWTrbzNVJ83ZhQ+pfPEPtMi3t0YVLLvapu3otgpiMkv4ew/ssXwYbg6xBWfotK+NG1cPwVFy9/V9+H5dpdvRI/le2QG0F5xCfCeKh/3AuNiMPEGoVUR5kj5cwFK6eskvt/+74ZenxfNPZ2Uttiw8DsqtTx1gxhcSZeU5YWpO7O78RaYE4Ll4kPbbvIaR18Napb6NKP846z02zvaw+feXARLe0HUY58TlmUjSX3MZRK4PEdyMIQ/URyPimj4rImaDfFrKPAHIjqT3EKv+KuNs8TEVMBMGCSqGSIb3DQEJFTEGBAQBAAAAMD0wITAJBgUrDgMCGgUABBRZOo132cuo2zNyy+SH2c+pN4OGmQQU2nQao3je7DTj2G6Gge8pooPf2ncCAgfQ"); public static X509Certificate2 GetSampleX509Certificate() diff --git a/src/libraries/System.Security.Permissions/Directory.Build.props b/src/libraries/System.Security.Permissions/Directory.Build.props index e909821a60101..556775dda5fa1 100644 --- a/src/libraries/System.Security.Permissions/Directory.Build.props +++ b/src/libraries/System.Security.Permissions/Directory.Build.props @@ -2,7 +2,6 @@ Open - true true Provides types supporting Code Access Security (CAS). diff --git a/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.Forwards.cs b/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.Forwards.cs index 6e691f997d500..4d9a824761f53 100644 --- a/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.Forwards.cs +++ b/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.Forwards.cs @@ -13,6 +13,8 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Permissions.SecurityAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Permissions.SecurityPermissionAttribute))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Permissions.SecurityPermissionFlag))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Policy.Evidence))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Policy.EvidenceBase))] #if NETCOREAPP [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.IStackWalk))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.PermissionSet))] diff --git a/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.cs b/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.cs index 88e6cd47c1461..51dec9a8f2279 100644 --- a/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.cs +++ b/src/libraries/System.Security.Permissions/ref/System.Security.Permissions.cs @@ -1942,43 +1942,6 @@ public void RemoveChild(System.Security.Policy.CodeGroup group) { } public System.Security.SecurityElement ToXml() { throw null; } public System.Security.SecurityElement ToXml(System.Security.Policy.PolicyLevel level) { throw null; } } - public sealed partial class Evidence : System.Collections.ICollection, System.Collections.IEnumerable - { - public Evidence() { } - [System.ObsoleteAttribute] - public Evidence(object[] hostEvidence, object[] assemblyEvidence) { } - public Evidence(System.Security.Policy.Evidence evidence) { } - public Evidence(System.Security.Policy.EvidenceBase[] hostEvidence, System.Security.Policy.EvidenceBase[] assemblyEvidence) { } - [System.ObsoleteAttribute] - public int Count { get { throw null; } } - public bool IsReadOnly { get { throw null; } } - public bool IsSynchronized { get { throw null; } } - public bool Locked { get { throw null; } set { } } - public object SyncRoot { get { throw null; } } - [System.ObsoleteAttribute] - public void AddAssembly(object id) { } - public void AddAssemblyEvidence(T evidence) where T : System.Security.Policy.EvidenceBase { } - [System.ObsoleteAttribute] - public void AddHost(object id) { } - public void AddHostEvidence(T evidence) where T : System.Security.Policy.EvidenceBase { } - public void Clear() { } - public System.Security.Policy.Evidence Clone() { throw null; } - [System.ObsoleteAttribute] - public void CopyTo(System.Array array, int index) { } - public System.Collections.IEnumerator GetAssemblyEnumerator() { throw null; } - public T GetAssemblyEvidence() where T : System.Security.Policy.EvidenceBase { throw null; } - [System.ObsoleteAttribute] - public System.Collections.IEnumerator GetEnumerator() { throw null; } - public System.Collections.IEnumerator GetHostEnumerator() { throw null; } - public T GetHostEvidence() where T : System.Security.Policy.EvidenceBase { throw null; } - public void Merge(System.Security.Policy.Evidence evidence) { } - public void RemoveType(System.Type t) { } - } - public abstract partial class EvidenceBase - { - protected EvidenceBase() { } - public virtual System.Security.Policy.EvidenceBase Clone() { throw null; } - } #if NET50_OBSOLETIONS [System.ObsoleteAttribute("Code Access Security is not supported or honored by the runtime.", DiagnosticId = "SYSLIB0003", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] #endif diff --git a/src/libraries/System.Security.Permissions/src/System.Security.Permissions.csproj b/src/libraries/System.Security.Permissions/src/System.Security.Permissions.csproj index 7fdbf99cc2dbb..445d797b29568 100644 --- a/src/libraries/System.Security.Permissions/src/System.Security.Permissions.csproj +++ b/src/libraries/System.Security.Permissions/src/System.Security.Permissions.csproj @@ -135,8 +135,6 @@ - - diff --git a/src/libraries/System.Security.Principal.Windows/src/ILLink/ILLinkTrim_LibraryBuild.xml b/src/libraries/System.Security.Principal.Windows/src/ILLink/ILLink.Descriptors.LibraryBuild.xml similarity index 100% rename from src/libraries/System.Security.Principal.Windows/src/ILLink/ILLinkTrim_LibraryBuild.xml rename to src/libraries/System.Security.Principal.Windows/src/ILLink/ILLink.Descriptors.LibraryBuild.xml diff --git a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs index 4951152f6cd51..489f66d485e5e 100644 --- a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs +++ b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/IRCollection.cs @@ -374,7 +374,7 @@ public IdentityReferenceCollection Translate(Type targetType, bool forceSuccess) #endregion } - internal class IdentityReferenceEnumerator : IEnumerator, IDisposable + internal sealed class IdentityReferenceEnumerator : IEnumerator, IDisposable { #region Private members diff --git a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs index 1291a1f42fa69..63906bad4dbc2 100644 --- a/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs +++ b/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs @@ -1108,7 +1108,7 @@ private static IdentityReferenceCollection TranslateToNTAccounts(IdentityReferen case SidNameUse.Alias: case SidNameUse.Computer: case SidNameUse.WellKnownGroup: - string account = Marshal.PtrToStringUni(Ltn.Name.Buffer, Ltn.Name.Length / sizeof(char)); ; + string account = Marshal.PtrToStringUni(Ltn.Name.Buffer, Ltn.Name.Length / sizeof(char)); string domain = ReferencedDomains[Ltn.DomainIndex]; Result.Add(new NTAccount(domain, account)); break; diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs index c1d7778ff97c0..f0e5fb91b2607 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Channels/UriGenerator.cs @@ -6,7 +6,7 @@ namespace System.ServiceModel.Channels { - internal class UriGenerator + internal sealed class UriGenerator { private long _id; private readonly string _prefix; diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs index b3d9fa52f98d0..0bed469263e6a 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/NullNotAllowedCollection.cs @@ -5,7 +5,7 @@ namespace System.ServiceModel.Syndication { - internal class NullNotAllowedCollection : Collection where TCollectionItem : class + internal sealed class NullNotAllowedCollection : Collection where TCollectionItem : class { public NullNotAllowedCollection() : base() { diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs index 699b1d2c1b946..fc3822a860a50 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/Syndication/SyndicationElementExtension.cs @@ -223,7 +223,7 @@ private void EnsureOuterNameAndNs() } // this class holds the extension data and the associated serializer (either DataContractSerializer or XmlSerializer but not both) - private class ExtensionDataWriter + private sealed class ExtensionDataWriter { private readonly XmlObjectSerializer _dataContractSerializer; private readonly object _extensionData; diff --git a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs index ab673b242c235..08f68ba039aca 100644 --- a/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs +++ b/src/libraries/System.ServiceModel.Syndication/src/System/ServiceModel/XmlBuffer.cs @@ -7,7 +7,7 @@ namespace System.ServiceModel { - internal class XmlBuffer + internal sealed class XmlBuffer { private readonly List
_sections; private byte[] _buffer; diff --git a/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs b/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs index c35a297a29df3..f06e87cda95a7 100644 --- a/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs +++ b/src/libraries/System.ServiceModel.Syndication/tests/Utils/XmlDiffDocument.cs @@ -659,7 +659,7 @@ public void IgnoreValues(XPathExpression expr) XPathNodeIterator _iter = _nav.Select(expr); while (_iter.MoveNext()) { - ((XmlDiffNavigator)_iter.Current).CurrentNode.IgnoreValue = true; ; + ((XmlDiffNavigator)_iter.Current).CurrentNode.IgnoreValue = true; } } diff --git a/src/libraries/System.Speech/src/AudioFormat/AudioFormatConverter.cs b/src/libraries/System.Speech/src/AudioFormat/AudioFormatConverter.cs index 9911734ec9513..66521fc6fc76e 100644 --- a/src/libraries/System.Speech/src/AudioFormat/AudioFormatConverter.cs +++ b/src/libraries/System.Speech/src/AudioFormat/AudioFormatConverter.cs @@ -284,7 +284,7 @@ private enum WaveFormatId } [StructLayout(LayoutKind.Sequential)] - private class WaveFormatEx + private sealed class WaveFormatEx { public ushort wFormatTag; public ushort nChannels; diff --git a/src/libraries/System.Speech/src/Internal/AlphabetConverter.cs b/src/libraries/System.Speech/src/Internal/AlphabetConverter.cs index ee822252c40e2..180daabdec2c7 100644 --- a/src/libraries/System.Speech/src/Internal/AlphabetConverter.cs +++ b/src/libraries/System.Speech/src/Internal/AlphabetConverter.cs @@ -228,7 +228,7 @@ private PhoneMapData CreateMap(string resourceName) internal class PhoneMapData { - private class ConversionUnit + private sealed class ConversionUnit { public string sapi; public string ups; diff --git a/src/libraries/System.Speech/src/Internal/PhonemeConverter.cs b/src/libraries/System.Speech/src/Internal/PhonemeConverter.cs index 8196685d9b78a..51ef6c02eddf4 100644 --- a/src/libraries/System.Speech/src/Internal/PhonemeConverter.cs +++ b/src/libraries/System.Speech/src/Internal/PhonemeConverter.cs @@ -222,7 +222,7 @@ private static PhoneMap[] DecompressPhoneMaps(PhoneMapCompressed[] pmComps) #region Private Types - private class PhoneMap + private sealed class PhoneMap { internal PhoneMap() { } @@ -230,7 +230,7 @@ internal PhoneMap() { } internal PhoneId[] _phoneIds; } - private class PhoneId : IComparer + private sealed class PhoneId : IComparer { internal PhoneId() { } @@ -250,7 +250,7 @@ int IComparer.Compare(PhoneId x, PhoneId y) /// has a length of 1 character. If the length is greater than 1, then the 'pron' string is appended with -1 values, one per extra code /// point. ///
- private class PhoneMapCompressed + private sealed class PhoneMapCompressed { internal PhoneMapCompressed() { } diff --git a/src/libraries/System.Speech/src/Internal/RBList.cs b/src/libraries/System.Speech/src/Internal/RBList.cs index c469ddff542d8..353abf50d8093 100644 --- a/src/libraries/System.Speech/src/Internal/RBList.cs +++ b/src/libraries/System.Speech/src/Internal/RBList.cs @@ -572,7 +572,7 @@ private static void FixRemovalCase6(TreeNode sibling) #region Private Types - private class MyEnumerator : IEnumerator + private sealed class MyEnumerator : IEnumerator { internal MyEnumerator(TreeNode node) { @@ -632,7 +632,7 @@ public void Reset() #if DEBUG [DebuggerDisplay("{((System.Speech.Internal.SrgsCompiler.Arc)Key).ToString ()}")] #endif - private class TreeNode + private sealed class TreeNode { internal TreeNode(object key) { diff --git a/src/libraries/System.Speech/src/Internal/SrgsCompiler/Arc.cs b/src/libraries/System.Speech/src/Internal/SrgsCompiler/Arc.cs index bedc7164ca8a3..8d7f0e1d271da 100644 --- a/src/libraries/System.Speech/src/Internal/SrgsCompiler/Arc.cs +++ b/src/libraries/System.Speech/src/Internal/SrgsCompiler/Arc.cs @@ -797,7 +797,7 @@ private static string GetSemanticValue(CfgSemanticTag tag, StringBlob symbols, o } #pragma warning restore 0618 - return tag._nameOffset > 0 ? symbols.FromOffset(tag._nameOffset) : tag._nameOffset.ToString(CultureInfo.InvariantCulture); ; + return tag._nameOffset > 0 ? symbols.FromOffset(tag._nameOffset) : tag._nameOffset.ToString(CultureInfo.InvariantCulture); } #endif diff --git a/src/libraries/System.Speech/src/Internal/SrgsCompiler/ArcList.cs b/src/libraries/System.Speech/src/Internal/SrgsCompiler/ArcList.cs index c7b1d8940deb4..2142889a595d3 100644 --- a/src/libraries/System.Speech/src/Internal/SrgsCompiler/ArcList.cs +++ b/src/libraries/System.Speech/src/Internal/SrgsCompiler/ArcList.cs @@ -63,7 +63,7 @@ private int Count } // Used by the debugger display attribute - private class ArcListDebugDisplay + private sealed class ArcListDebugDisplay { public ArcListDebugDisplay(ArcList item) { diff --git a/src/libraries/System.Speech/src/Internal/SrgsCompiler/BackEnd.cs b/src/libraries/System.Speech/src/Internal/SrgsCompiler/BackEnd.cs index a68dd999c7c50..7052b26d8904f 100644 --- a/src/libraries/System.Speech/src/Internal/SrgsCompiler/BackEnd.cs +++ b/src/libraries/System.Speech/src/Internal/SrgsCompiler/BackEnd.cs @@ -498,7 +498,7 @@ internal State CloneSubGraph(State srcFromState, State srcEndState, State destFr } else { - destToState = SrcToDestHash[srcToState]; ; + destToState = SrcToDestHash[srcToState]; } } diff --git a/src/libraries/System.Speech/src/Internal/SrgsParser/XmlParser.cs b/src/libraries/System.Speech/src/Internal/SrgsParser/XmlParser.cs index 7374226095f89..f6b262081274d 100644 --- a/src/libraries/System.Speech/src/Internal/SrgsParser/XmlParser.cs +++ b/src/libraries/System.Speech/src/Internal/SrgsParser/XmlParser.cs @@ -1060,7 +1060,7 @@ private IElement ParseTag(IElement parent, XmlReader reader) System.Diagnostics.Debug.Assert(_parser.Grammar.TagFormat == SrgsTagFormat.KeyValuePairs); - IPropertyTag propertyTag = _parser.CreatePropertyTag(parent); ; + IPropertyTag propertyTag = _parser.CreatePropertyTag(parent); string name; object value; ParsePropertyTag(content, out name, out value); diff --git a/src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.cs b/src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.cs index d59f8810d1b21..b5650685fbeac 100644 --- a/src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.cs +++ b/src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.cs @@ -201,7 +201,7 @@ internal static byte[] GetWaveFormat(BinaryReader br) if (riff._id != RIFF_MARKER && riff._type != WAVE_MARKER) { - return null; ; + return null; } BLOCKHDR block = new(); @@ -210,7 +210,7 @@ internal static byte[] GetWaveFormat(BinaryReader br) if (block._id != FMT_MARKER) { - return null; ; + return null; } // If the format is of type WAVEFORMAT then fake a cbByte with a length of zero diff --git a/src/libraries/System.Speech/src/Internal/Synthesis/AudioDeviceOut.cs b/src/libraries/System.Speech/src/Internal/Synthesis/AudioDeviceOut.cs index 6d0e59f278602..5f1ea529efeb6 100644 --- a/src/libraries/System.Speech/src/Internal/Synthesis/AudioDeviceOut.cs +++ b/src/libraries/System.Speech/src/Internal/Synthesis/AudioDeviceOut.cs @@ -445,7 +445,7 @@ private void CheckForAbort() /// so that the pinned buffer containing the data is not /// released before it is finished being played ///
- private class InItem : IDisposable + private sealed class InItem : IDisposable { internal InItem(WaveHeader waveHeader) { diff --git a/src/libraries/System.Speech/src/Internal/Synthesis/VoiceSynthesis.cs b/src/libraries/System.Speech/src/Internal/Synthesis/VoiceSynthesis.cs index b45768358f903..fb3fd90d05211 100644 --- a/src/libraries/System.Speech/src/Internal/Synthesis/VoiceSynthesis.cs +++ b/src/libraries/System.Speech/src/Internal/Synthesis/VoiceSynthesis.cs @@ -755,7 +755,7 @@ private void ThreadProc() if (paramSpeak._prompt.Exception == null) { // No lexicon yet - List lexicons = new(); ; + List lexicons = new(); //--- Create a single speak info structure for all the text TTSVoice voice = _currentVoice != null ? _currentVoice : GetVoice(false); @@ -1742,7 +1742,7 @@ private enum Action SpeakText, } - private class Parameters + private sealed class Parameters { internal Parameters(Action action, object parameter) { @@ -1754,7 +1754,7 @@ internal Parameters(Action action, object parameter) internal object _parameter; } - private class ParametersSpeak + private sealed class ParametersSpeak { internal ParametersSpeak(string textToSpeak, Prompt prompt, bool isXml, Uri audioFile) { diff --git a/src/libraries/System.Speech/src/Recognition/Grammar.cs b/src/libraries/System.Speech/src/Recognition/Grammar.cs index 827fc8b56fd5c..1aa35512db8ce 100644 --- a/src/libraries/System.Speech/src/Recognition/Grammar.cs +++ b/src/libraries/System.Speech/src/Recognition/Grammar.cs @@ -862,7 +862,7 @@ private static NameValuePair[] ParseInitParams(string initParameters) { if (string.IsNullOrEmpty(initParameters)) { - return Array.Empty(); ; + return Array.Empty(); } string[] parameters = initParameters.Split(new char[] { ';' }, StringSplitOptions.None); diff --git a/src/libraries/System.Speech/src/Recognition/GrammarBuilder.cs b/src/libraries/System.Speech/src/Recognition/GrammarBuilder.cs index c08faeafd2dbf..203d6adfa346e 100644 --- a/src/libraries/System.Speech/src/Recognition/GrammarBuilder.cs +++ b/src/libraries/System.Speech/src/Recognition/GrammarBuilder.cs @@ -462,7 +462,7 @@ private void AddItem(GrammarBuilderBase item) #region Private Type - private class InternalGrammarBuilder : BuilderElements + private sealed class InternalGrammarBuilder : BuilderElements { #region Internal Methods diff --git a/src/libraries/System.Speech/src/Recognition/RecognizerBase.cs b/src/libraries/System.Speech/src/Recognition/RecognizerBase.cs index 452ab92a21550..be7453e44661b 100644 --- a/src/libraries/System.Speech/src/Recognition/RecognizerBase.cs +++ b/src/libraries/System.Speech/src/Recognition/RecognizerBase.cs @@ -888,7 +888,7 @@ internal static Exception ExceptionFromSapiCreateRecognizerError(SAPIErrorCodes return new PlatformNotSupportedException(SR.Get(srid)); default: - Exception exReturn = null; ; + Exception exReturn = null; if (srid >= 0) { exReturn = new InvalidOperationException(SR.Get(srid)); @@ -3068,7 +3068,7 @@ private static void CheckGrammarOptionsOnSapi51(Grammar grammar) private RecognizerBaseThunk _recoThunk; #endregion - private class RecognizerBaseThunk : ISpGrammarResourceLoader + private sealed class RecognizerBaseThunk : ISpGrammarResourceLoader { internal RecognizerBaseThunk(RecognizerBase recognizer) { diff --git a/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs b/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs index 68c3787f44864..446d744cae0a6 100644 --- a/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs +++ b/src/libraries/System.Speech/src/Result/RecognizedPhrase.cs @@ -1112,7 +1112,7 @@ private void AppendAttributes(XmlElement propertyNode, SemanticValue semanticVal #region Private Types [DebuggerDisplay("{DisplayDebugInfo()}")] - private class RuleNode + private sealed class RuleNode { internal RuleNode(Grammar grammar, string rule, float confidence, uint first, uint count) { diff --git a/src/libraries/System.Speech/src/Synthesis/PromptBuilder.cs b/src/libraries/System.Speech/src/Synthesis/PromptBuilder.cs index aa01bc00c96f3..8f5913b281603 100644 --- a/src/libraries/System.Speech/src/Synthesis/PromptBuilder.cs +++ b/src/libraries/System.Speech/src/Synthesis/PromptBuilder.cs @@ -1075,7 +1075,7 @@ internal AttributeItem(string ns, string key, string value) } [Serializable] - private class Element + private sealed class Element { internal ElementType _type; internal string _text; diff --git a/src/libraries/System.Speech/src/Synthesis/TTSEngine/TTSEngineTypes.cs b/src/libraries/System.Speech/src/Synthesis/TTSEngine/TTSEngineTypes.cs index f2a56e31365e9..7fe6b0c90921c 100644 --- a/src/libraries/System.Speech/src/Synthesis/TTSEngine/TTSEngineTypes.cs +++ b/src/libraries/System.Speech/src/Synthesis/TTSEngine/TTSEngineTypes.cs @@ -286,10 +286,10 @@ public void SetContourPoints(ContourPoint[] points) } public Prosody() { - Pitch = new ProsodyNumber((int)ProsodyPitch.Default); ; - Range = new ProsodyNumber((int)ProsodyRange.Default); ; + Pitch = new ProsodyNumber((int)ProsodyPitch.Default); + Range = new ProsodyNumber((int)ProsodyRange.Default); Rate = new ProsodyNumber((int)ProsodyRate.Default); - Volume = new ProsodyNumber((int)ProsodyVolume.Default); ; + Volume = new ProsodyNumber((int)ProsodyVolume.Default); } internal Prosody Clone() diff --git a/src/libraries/System.Speech/src/System.Speech.csproj b/src/libraries/System.Speech/src/System.Speech.csproj index 503fd439e13e8..10db56e602a19 100644 --- a/src/libraries/System.Speech/src/System.Speech.csproj +++ b/src/libraries/System.Speech/src/System.Speech.csproj @@ -92,7 +92,7 @@ - + @@ -121,17 +121,17 @@ - + - + - - - + + + @@ -254,4 +254,4 @@
- \ No newline at end of file + diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs index 92da49d2a0c4b..531f26876242a 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/DBCSCodePageEncoding.cs @@ -1156,7 +1156,7 @@ public override Decoder GetDecoder() return new DBCSDecoder(this); } - internal class DBCSDecoder : DecoderNLS + internal sealed class DBCSDecoder : DecoderNLS { // Need a place for the last left over byte internal byte bLeftOver; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs index 1bb05163f67cd..7c4b7485d0c08 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EUCJPEncoding.cs @@ -44,7 +44,7 @@ namespace System.Text { - internal class EUCJPEncoding : DBCSCodePageEncoding + internal sealed class EUCJPEncoding : DBCSCodePageEncoding { // This pretends to be CP 932 as far as memory tables are concerned. public EUCJPEncoding() : base(51932, 932) diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs index 3c42ac90761ea..0d9bed68345d5 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncoderBestFitFallback.cs @@ -14,7 +14,7 @@ namespace System.Text { - internal class InternalEncoderBestFitFallback : EncoderFallback + internal sealed class InternalEncoderBestFitFallback : EncoderFallback { // Our variables internal BaseCodePageEncoding encoding; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingByteBuffer.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingByteBuffer.cs index a2a20b2984f3c..a4a4a0db58fa7 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingByteBuffer.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingByteBuffer.cs @@ -5,7 +5,7 @@ namespace System.Text { - internal class EncodingByteBuffer + internal sealed class EncodingByteBuffer { private unsafe byte* _bytes; private readonly unsafe byte* _byteStart; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs index 38648d08db533..c65756dc34719 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingCharBuffer.cs @@ -5,7 +5,7 @@ namespace System.Text { - internal class EncodingCharBuffer + internal sealed class EncodingCharBuffer { private unsafe char* _chars; private readonly unsafe char* _charStart; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs index 4c8d55e77f0e7..e630706877a36 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISCIIEncoding.cs @@ -24,7 +24,7 @@ namespace System.Text // Forms D & KD have things like 0934, which decomposes to 0933 + 093C, so not normal. // Form IDNA has the above problems plus case mapping, so false (like most encodings) // - internal class ISCIIEncoding : EncodingNLS, ISerializable + internal sealed class ISCIIEncoding : EncodingNLS, ISerializable { // Constants private const int CodeDevanagari = 2; // 0x42 57002 @@ -691,7 +691,7 @@ public override int GetHashCode() return _defaultCodePage + EncoderFallback.GetHashCode() + DecoderFallback.GetHashCode(); } - internal class ISCIIEncoder : EncoderNLS + internal sealed class ISCIIEncoder : EncoderNLS { // Need to remember the default code page (for HasState) internal int defaultCodePage; @@ -729,7 +729,7 @@ internal override bool HasState } } - internal class ISCIIDecoder : DecoderNLS + internal sealed class ISCIIDecoder : DecoderNLS { // Need a place to store any our current code page and last ATR flag internal int currentCodePage; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs index cc97ee3c3dab7..84e61cb5620c4 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/ISO2022Encoding.cs @@ -47,7 +47,7 @@ namespace System.Text ** ==============================================================================*/ - internal class ISO2022Encoding : DBCSCodePageEncoding + internal sealed class ISO2022Encoding : DBCSCodePageEncoding { private const byte SHIFT_OUT = (byte)0x0E; private const byte SHIFT_IN = (byte)0x0F; @@ -1787,7 +1787,7 @@ public override Decoder GetDecoder() return new ISO2022Decoder(this); } - internal class ISO2022Encoder : System.Text.EncoderNLS + internal sealed class ISO2022Encoder : System.Text.EncoderNLS { internal ISO2022Modes currentMode; internal ISO2022Modes shiftInOutMode; @@ -1819,7 +1819,7 @@ internal override bool HasState } } - internal class ISO2022Decoder : System.Text.DecoderNLS + internal sealed class ISO2022Decoder : System.Text.DecoderNLS { internal byte[] bytesLeftOver = null!; // initialized by base calling Reset internal int bytesLeftOverCount; diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs index cd969d377e7b3..b0266c6f4ecba 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/SBCSCodePageEncoding.cs @@ -13,7 +13,7 @@ namespace System.Text { - internal class SBCSCodePageEncoding : BaseCodePageEncoding + internal sealed class SBCSCodePageEncoding : BaseCodePageEncoding { // Pointers to our memory section parts private unsafe char* _mapBytesToUnicode = null; // char 256 diff --git a/src/libraries/System.Text.Encodings.Web/ref/System.Text.Encodings.Web.csproj b/src/libraries/System.Text.Encodings.Web/ref/System.Text.Encodings.Web.csproj index eff1dfbb548ca..51fc45949ef50 100644 --- a/src/libraries/System.Text.Encodings.Web/ref/System.Text.Encodings.Web.csproj +++ b/src/libraries/System.Text.Encodings.Web/ref/System.Text.Encodings.Web.csproj @@ -1,6 +1,6 @@ - $(NetCoreAppCurrent);netstandard2.0;net461 + $(NetCoreAppCurrent);netcoreapp3.1;netstandard2.0;net461 true true @@ -11,12 +11,13 @@
- - - + + + + + \ No newline at end of file diff --git a/src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.cs b/src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.cs new file mode 100644 index 0000000000000..2edf3d88601c8 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Contains a polyfill implementation of System.Numerics.BitOperations that works on netstandard2.0. +// Implementation copied from: +// https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +// +// Some routines inspired by the Stanford Bit Twiddling Hacks by Sean Eron Anderson: +// http://graphics.stanford.edu/~seander/bithacks.html + +namespace System.Numerics +{ + internal static class BitOperations + { + private static ReadOnlySpan Log2DeBruijn => new byte[32] + { + 00, 09, 01, 10, 13, 21, 02, 29, + 11, 14, 16, 18, 22, 25, 03, 30, + 08, 12, 20, 28, 15, 17, 24, 07, + 19, 27, 23, 06, 26, 05, 04, 31 + }; + + /// + /// Returns the integer (floor) log of the specified value, base 2. + /// Note that by convention, input value 0 returns 0 since log(0) is undefined. + /// + /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Log2(uint value) + { + // Fallback contract is 0->0 + return Log2SoftwareFallback(value | 1); + } + + /// + /// Returns the integer (floor) log of the specified value, base 2. + /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. + /// Does not directly use any hardware intrinsics, nor does it incur branching. + /// + /// The value. + private static int Log2SoftwareFallback(uint value) + { + // No AggressiveInlining due to large method size + // Has conventional contract 0->0 (Log(0) is undefined) + + // Fill trailing zeros with ones, eg 00010010 becomes 00011111 + value |= value >> 01; + value |= value >> 02; + value |= value >> 04; + value |= value >> 08; + value |= value >> 16; + + // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check + return Unsafe.AddByteOffset( + // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u + ref MemoryMarshal.GetReference(Log2DeBruijn), + // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here + (nint)((value * 0x07C4ACDDu) >> 27)); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Text.Rune.netstandard20.cs b/src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Text.Rune.netstandard20.cs new file mode 100644 index 0000000000000..f99e39b691ce4 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Text.Rune.netstandard20.cs @@ -0,0 +1,546 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.Encodings.Web; + +// Contains a polyfill implementation of System.Text.Rune that works on netstandard2.0. +// Implementation copied from: +// https://github.com/dotnet/runtime/blob/177d6f1a0bfdc853ae9ffeef4be99ff984c4f5dd/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs + +namespace System.Text +{ + internal readonly struct Rune + { + private const int MaxUtf16CharsPerRune = 2; // supplementary plane code points are encoded as 2 UTF-16 code units + + private const char HighSurrogateStart = '\ud800'; + private const char LowSurrogateStart = '\udc00'; + private const int HighSurrogateRange = 0x3FF; + + private readonly uint _value; + + /// + /// Creates a from the provided Unicode scalar value. + /// + /// + /// If does not represent a value Unicode scalar value. + /// + public Rune(uint value) + { + if (!UnicodeUtility.IsValidUnicodeScalar(value)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value); + } + _value = value; + } + + /// + /// Creates a from the provided Unicode scalar value. + /// + /// + /// If does not represent a value Unicode scalar value. + /// + public Rune(int value) + : this((uint)value) + { + } + + // non-validating ctor + private Rune(uint scalarValue, bool unused) + { + UnicodeDebug.AssertIsValidScalar(scalarValue); + _value = scalarValue; + } + + /// + /// Returns true if and only if this scalar value is ASCII ([ U+0000..U+007F ]) + /// and therefore representable by a single UTF-8 code unit. + /// + public bool IsAscii => UnicodeUtility.IsAsciiCodePoint(_value); + + /// + /// Returns true if and only if this scalar value is within the BMP ([ U+0000..U+FFFF ]) + /// and therefore representable by a single UTF-16 code unit. + /// + public bool IsBmp => UnicodeUtility.IsBmpCodePoint(_value); + + public static bool operator ==(Rune left, Rune right) => left._value == right._value; + + public static bool operator !=(Rune left, Rune right) => left._value != right._value; + + public static bool IsControl(Rune value) + { + // Per the Unicode stability policy, the set of control characters + // is forever fixed at [ U+0000..U+001F ], [ U+007F..U+009F ]. No + // characters will ever be added to or removed from the "control characters" + // group. See https://www.unicode.org/policies/stability_policy.html. + + // Logic below depends on Rune.Value never being -1 (since Rune is a validating type) + // 00..1F (+1) => 01..20 (&~80) => 01..20 + // 7F..9F (+1) => 80..A0 (&~80) => 00..20 + + return ((value._value + 1) & ~0x80u) <= 0x20u; + } + + /// + /// A instance that represents the Unicode replacement character U+FFFD. + /// + public static Rune ReplacementChar => UnsafeCreate(UnicodeUtility.ReplacementChar); + + /// + /// Returns the length in code units () of the + /// UTF-16 sequence required to represent this scalar value. + /// + /// + /// The return value will be 1 or 2. + /// + public int Utf16SequenceLength + { + get + { + int codeUnitCount = UnicodeUtility.GetUtf16SequenceLength(_value); + Debug.Assert(codeUnitCount > 0 && codeUnitCount <= MaxUtf16CharsPerRune); + return codeUnitCount; + } + } + + /// + /// Returns the Unicode scalar value as an integer. + /// + public int Value => (int)_value; + + /// + /// Decodes the at the beginning of the provided UTF-16 source buffer. + /// + /// + /// + /// If the source buffer begins with a valid UTF-16 encoded scalar value, returns , + /// and outs via the decoded and via the + /// number of s used in the input buffer to encode the . + /// + /// + /// If the source buffer is empty or contains only a standalone UTF-16 high surrogate character, returns , + /// and outs via and via the length of the input buffer. + /// + /// + /// If the source buffer begins with an ill-formed UTF-16 encoded scalar value, returns , + /// and outs via and via the number of + /// s used in the input buffer to encode the ill-formed sequence. + /// + /// + /// + /// The general calling convention is to call this method in a loop, slicing the buffer by + /// elements on each iteration of the loop. On each iteration of the loop + /// will contain the real scalar value if successfully decoded, or it will contain if + /// the data could not be successfully decoded. This pattern provides convenient automatic U+FFFD substitution of + /// invalid sequences while iterating through the loop. + /// + public static OperationStatus DecodeFromUtf16(ReadOnlySpan source, out Rune result, out int charsConsumed) + { + if (!source.IsEmpty) + { + // First, check for the common case of a BMP scalar value. + // If this is correct, return immediately. + + char firstChar = source[0]; + if (TryCreate(firstChar, out result)) + { + charsConsumed = 1; + return OperationStatus.Done; + } + + // First thing we saw was a UTF-16 surrogate code point. + // Let's optimistically assume for now it's a high surrogate and hope + // that combining it with the next char yields useful results. + + if (1 < (uint)source.Length) + { + char secondChar = source[1]; + if (TryCreate(firstChar, secondChar, out result)) + { + // Success! Formed a supplementary scalar value. + charsConsumed = 2; + return OperationStatus.Done; + } + else + { + // Either the first character was a low surrogate, or the second + // character was not a low surrogate. This is an error. + goto InvalidData; + } + } + else if (!char.IsHighSurrogate(firstChar)) + { + // Quick check to make sure we're not going to report NeedMoreData for + // a single-element buffer where the data is a standalone low surrogate + // character. Since no additional data will ever make this valid, we'll + // report an error immediately. + goto InvalidData; + } + } + + // If we got to this point, the input buffer was empty, or the buffer + // was a single element in length and that element was a high surrogate char. + + charsConsumed = source.Length; + result = ReplacementChar; + return OperationStatus.NeedMoreData; + + InvalidData: + + charsConsumed = 1; // maximal invalid subsequence for UTF-16 is always a single code unit in length + result = ReplacementChar; + return OperationStatus.InvalidData; + } + + /// + /// Decodes the at the beginning of the provided UTF-8 source buffer. + /// + /// + /// + /// If the source buffer begins with a valid UTF-8 encoded scalar value, returns , + /// and outs via the decoded and via the + /// number of s used in the input buffer to encode the . + /// + /// + /// If the source buffer is empty or contains only a partial UTF-8 subsequence, returns , + /// and outs via and via the length of the input buffer. + /// + /// + /// If the source buffer begins with an ill-formed UTF-8 encoded scalar value, returns , + /// and outs via and via the number of + /// s used in the input buffer to encode the ill-formed sequence. + /// + /// + /// + /// The general calling convention is to call this method in a loop, slicing the buffer by + /// elements on each iteration of the loop. On each iteration of the loop + /// will contain the real scalar value if successfully decoded, or it will contain if + /// the data could not be successfully decoded. This pattern provides convenient automatic U+FFFD substitution of + /// invalid sequences while iterating through the loop. + /// + public static OperationStatus DecodeFromUtf8(ReadOnlySpan source, out Rune result, out int bytesConsumed) + { + // This method follows the Unicode Standard's recommendation for detecting + // the maximal subpart of an ill-formed subsequence. See The Unicode Standard, + // Ch. 3.9 for more details. In summary, when reporting an invalid subsequence, + // it tries to consume as many code units as possible as long as those code + // units constitute the beginning of a longer well-formed subsequence per Table 3-7. + + int index = 0; + + // Try reading input[0]. + + if ((uint)index >= (uint)source.Length) + { + goto NeedsMoreData; + } + + uint tempValue = source[index]; + if (!UnicodeUtility.IsAsciiCodePoint(tempValue)) + { + goto NotAscii; + } + + Finish: + + bytesConsumed = index + 1; + Debug.Assert(1 <= bytesConsumed && bytesConsumed <= 4); // Valid subsequences are always length [1..4] + result = UnsafeCreate(tempValue); + return OperationStatus.Done; + + NotAscii: + + // Per Table 3-7, the beginning of a multibyte sequence must be a code unit in + // the range [C2..F4]. If it's outside of that range, it's either a standalone + // continuation byte, or it's an overlong two-byte sequence, or it's an out-of-range + // four-byte sequence. + + if (!UnicodeUtility.IsInRangeInclusive(tempValue, 0xC2, 0xF4)) + { + goto FirstByteInvalid; + } + + tempValue = (tempValue - 0xC2) << 6; + + // Try reading input[1]. + + index++; + if ((uint)index >= (uint)source.Length) + { + goto NeedsMoreData; + } + + // Continuation bytes are of the form [10xxxxxx], which means that their two's + // complement representation is in the range [-65..-128]. This allows us to + // perform a single comparison to see if a byte is a continuation byte. + + int thisByteSignExtended = (sbyte)source[index]; + if (thisByteSignExtended >= -64) + { + goto Invalid; + } + + tempValue += (uint)thisByteSignExtended; + tempValue += 0x80; // remove the continuation byte marker + tempValue += (0xC2 - 0xC0) << 6; // remove the leading byte marker + + if (tempValue < 0x0800) + { + Debug.Assert(UnicodeUtility.IsInRangeInclusive(tempValue, 0x0080, 0x07FF)); + goto Finish; // this is a valid 2-byte sequence + } + + // This appears to be a 3- or 4-byte sequence. Since per Table 3-7 we now have + // enough information (from just two code units) to detect overlong or surrogate + // sequences, we need to perform these checks now. + + if (!UnicodeUtility.IsInRangeInclusive(tempValue, ((0xE0 - 0xC0) << 6) + (0xA0 - 0x80), ((0xF4 - 0xC0) << 6) + (0x8F - 0x80))) + { + // The first two bytes were not in the range [[E0 A0]..[F4 8F]]. + // This is an overlong 3-byte sequence or an out-of-range 4-byte sequence. + goto Invalid; + } + + if (UnicodeUtility.IsInRangeInclusive(tempValue, ((0xED - 0xC0) << 6) + (0xA0 - 0x80), ((0xED - 0xC0) << 6) + (0xBF - 0x80))) + { + // This is a UTF-16 surrogate code point, which is invalid in UTF-8. + goto Invalid; + } + + if (UnicodeUtility.IsInRangeInclusive(tempValue, ((0xF0 - 0xC0) << 6) + (0x80 - 0x80), ((0xF0 - 0xC0) << 6) + (0x8F - 0x80))) + { + // This is an overlong 4-byte sequence. + goto Invalid; + } + + // The first two bytes were just fine. We don't need to perform any other checks + // on the remaining bytes other than to see that they're valid continuation bytes. + + // Try reading input[2]. + + index++; + if ((uint)index >= (uint)source.Length) + { + goto NeedsMoreData; + } + + thisByteSignExtended = (sbyte)source[index]; + if (thisByteSignExtended >= -64) + { + goto Invalid; // this byte is not a UTF-8 continuation byte + } + + tempValue <<= 6; + tempValue += (uint)thisByteSignExtended; + tempValue += 0x80; // remove the continuation byte marker + tempValue -= (0xE0 - 0xC0) << 12; // remove the leading byte marker + + if (tempValue <= 0xFFFF) + { + Debug.Assert(UnicodeUtility.IsInRangeInclusive(tempValue, 0x0800, 0xFFFF)); + goto Finish; // this is a valid 3-byte sequence + } + + // Try reading input[3]. + + index++; + if ((uint)index >= (uint)source.Length) + { + goto NeedsMoreData; + } + + thisByteSignExtended = (sbyte)source[index]; + if (thisByteSignExtended >= -64) + { + goto Invalid; // this byte is not a UTF-8 continuation byte + } + + tempValue <<= 6; + tempValue += (uint)thisByteSignExtended; + tempValue += 0x80; // remove the continuation byte marker + tempValue -= (0xF0 - 0xE0) << 18; // remove the leading byte marker + + UnicodeDebug.AssertIsValidSupplementaryPlaneScalar(tempValue); + goto Finish; // this is a valid 4-byte sequence + + FirstByteInvalid: + + index = 1; // Invalid subsequences are always at least length 1. + + Invalid: + + Debug.Assert(1 <= index && index <= 3); // Invalid subsequences are always length 1..3 + bytesConsumed = index; + result = ReplacementChar; + return OperationStatus.InvalidData; + + NeedsMoreData: + + Debug.Assert(0 <= index && index <= 3); // Incomplete subsequences are always length 0..3 + bytesConsumed = index; + result = ReplacementChar; + return OperationStatus.NeedMoreData; + } + + public override bool Equals(object? obj) => (obj is Rune other) && Equals(other); + + public bool Equals(Rune other) => this == other; + + public override int GetHashCode() => Value; + + /// + /// Attempts to create a from the provided input value. + /// + public static bool TryCreate(char ch, out Rune result) + { + uint extendedValue = ch; + if (!UnicodeUtility.IsSurrogateCodePoint(extendedValue)) + { + result = UnsafeCreate(extendedValue); + return true; + } + else + { + result = default; + return false; + } + } + + /// + /// Attempts to create a from the provided UTF-16 surrogate pair. + /// Returns if the input values don't represent a well-formed UTF-16surrogate pair. + /// + public static bool TryCreate(char highSurrogate, char lowSurrogate, out Rune result) + { + // First, extend both to 32 bits, then calculate the offset of + // each candidate surrogate char from the start of its range. + + uint highSurrogateOffset = (uint)highSurrogate - HighSurrogateStart; + uint lowSurrogateOffset = (uint)lowSurrogate - LowSurrogateStart; + + // This is a single comparison which allows us to check both for validity at once since + // both the high surrogate range and the low surrogate range are the same length. + // If the comparison fails, we call to a helper method to throw the correct exception message. + + if ((highSurrogateOffset | lowSurrogateOffset) <= HighSurrogateRange) + { + // The 0x40u << 10 below is to account for uuuuu = wwww + 1 in the surrogate encoding. + result = UnsafeCreate((highSurrogateOffset << 10) + ((uint)lowSurrogate - LowSurrogateStart) + (0x40u << 10)); + return true; + } + else + { + // Didn't have a high surrogate followed by a low surrogate. + result = default; + return false; + } + } + + /// + /// Encodes this to a UTF-16 destination buffer. + /// + /// The buffer to which to write this value as UTF-16. + /// + /// The number of s written to , + /// or 0 if the destination buffer is not large enough to contain the output. + /// True if the value was written to the buffer; otherwise, false. + public bool TryEncodeToUtf16(Span destination, out int charsWritten) + { + if (destination.Length >= 1) + { + if (IsBmp) + { + destination[0] = (char)_value; + charsWritten = 1; + return true; + } + else if (destination.Length >= 2) + { + UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar(_value, out destination[0], out destination[1]); + charsWritten = 2; + return true; + } + } + + // Destination buffer not large enough + + charsWritten = default; + return false; + } + + /// + /// Encodes this to a destination buffer as UTF-8 bytes. + /// + /// The buffer to which to write this value as UTF-8. + /// + /// The number of s written to , + /// or 0 if the destination buffer is not large enough to contain the output. + /// True if the value was written to the buffer; otherwise, false. + public bool TryEncodeToUtf8(Span destination, out int bytesWritten) + { + // The bit patterns below come from the Unicode Standard, Table 3-6. + + if (destination.Length >= 1) + { + if (IsAscii) + { + destination[0] = (byte)_value; + bytesWritten = 1; + return true; + } + + if (destination.Length >= 2) + { + if (_value <= 0x7FFu) + { + // Scalar 00000yyy yyxxxxxx -> bytes [ 110yyyyy 10xxxxxx ] + destination[0] = (byte)((_value + (0b110u << 11)) >> 6); + destination[1] = (byte)((_value & 0x3Fu) + 0x80u); + bytesWritten = 2; + return true; + } + + if (destination.Length >= 3) + { + if (_value <= 0xFFFFu) + { + // Scalar zzzzyyyy yyxxxxxx -> bytes [ 1110zzzz 10yyyyyy 10xxxxxx ] + destination[0] = (byte)((_value + (0b1110 << 16)) >> 12); + destination[1] = (byte)(((_value & (0x3Fu << 6)) >> 6) + 0x80u); + destination[2] = (byte)((_value & 0x3Fu) + 0x80u); + bytesWritten = 3; + return true; + } + + if (destination.Length >= 4) + { + // Scalar 000uuuuu zzzzyyyy yyxxxxxx -> bytes [ 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ] + destination[0] = (byte)((_value + (0b11110 << 21)) >> 18); + destination[1] = (byte)(((_value & (0x3Fu << 12)) >> 12) + 0x80u); + destination[2] = (byte)(((_value & (0x3Fu << 6)) >> 6) + 0x80u); + destination[3] = (byte)((_value & 0x3Fu) + 0x80u); + bytesWritten = 4; + return true; + } + } + } + } + + // Destination buffer not large enough + + bytesWritten = default; + return false; + } + + /// + /// Creates a without performing validation on the input. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static Rune UnsafeCreate(uint scalarValue) => new Rune(scalarValue, false); + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj b/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj index f997799a00383..a4c22190c2628 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj +++ b/src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csproj @@ -1,59 +1,70 @@ true - $(NetCoreAppCurrent);netcoreapp3.0;netstandard2.1;netstandard2.0;net461 + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser;netcoreapp3.1;netstandard2.0;net461 true enable - - + - $(NoWarn);CS3019 + $(NoWarn);CS3011;CS3019 + $(DefineConstants);TARGET_BROWSER + + + + + + - + + + - - + - - - - - - - - - - - + - - + + + + + + + + + + + - + - + - + + + + + + + + - + diff --git a/src/libraries/System.Text.Encodings.Web/src/System/IO/TextWriterExtensions.cs b/src/libraries/System.Text.Encodings.Web/src/System/IO/TextWriterExtensions.cs new file mode 100644 index 0000000000000..bcb3cb0bd7783 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/IO/TextWriterExtensions.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +#if !NETCOREAPP +using System.Buffers; +#endif + +namespace System.IO +{ + internal static class TextWriterExtensions + { + /// + /// Writes a partial string (given offset and count) to the underlying TextWriter. + /// + public static void WritePartialString(this TextWriter writer, string value, int offset, int count) + { + Debug.Assert(writer != null); + Debug.Assert(value != null); + + if (offset == 0 && count == value.Length) + { + // on all platforms, prefer TextWriter.Write(string) if no slicing is required + writer.Write(value); + } + else + { + // if slicing is required, call TextWriter.Write(ROS) if available; + // otherwise rent an array and implement the Write routine ourselves + ReadOnlySpan sliced = value.AsSpan(offset, count); +#if NETCOREAPP + writer.Write(sliced); +#else + char[] rented = ArrayPool.Shared.Rent(sliced.Length); + sliced.CopyTo(rented); + writer.Write(rented, 0, sliced.Length); + ArrayPool.Shared.Return(rented); +#endif + } + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AdvSimdHelper.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AdvSimdHelper.cs deleted file mode 100644 index f7d8b3b1579e6..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AdvSimdHelper.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.Arm; - -namespace System.Text.Encodings.Web -{ - internal static class AdvSimdHelper - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(Vector128 sourceValue) - { - if (!AdvSimd.Arm64.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - // Anything in the control characters range, and anything above short.MaxValue but less than or equal char.MaxValue - // That's because anything between 32768 and 65535 (inclusive) will overflow and become negative. - Vector128 mask = AdvSimd.CompareLessThan(sourceValue, s_spaceMaskInt16); - - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_quotationMarkMaskInt16)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_reverseSolidusMaskInt16)); - - // Anything above the ASCII range, and also including the leftover control character in the ASCII range - 0x7F - // When this method is called with only ASCII data, 0x7F is the only value that would meet this comparison. - // However, when called from "Default", the source could contain characters outside the ASCII range. - mask = AdvSimd.Or(mask, AdvSimd.CompareGreaterThan(sourceValue, s_tildeMaskInt16)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(Vector128 sourceValue) - { - if (!AdvSimd.Arm64.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - // Anything in the control characters range (except 0x7F), and anything above sbyte.MaxValue but less than or equal byte.MaxValue - // That's because anything between 128 and 255 (inclusive) will overflow and become negative. - Vector128 mask = AdvSimd.CompareLessThan(sourceValue, s_spaceMaskSByte); - - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_quotationMarkMaskSByte)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_reverseSolidusMaskSByte)); - - // Leftover control character in the ASCII range - 0x7F - // Since we are dealing with sbytes, 0x7F is the only value that would meet this comparison. - mask = AdvSimd.Or(mask, AdvSimd.CompareGreaterThan(sourceValue, s_tildeMaskSByte)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin(Vector128 sourceValue) - { - if (!AdvSimd.Arm64.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - Vector128 mask = CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(sourceValue); - - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_ampersandMaskSByte)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_apostropheMaskSByte)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_plusSignMaskSByte)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_lessThanSignMaskSByte)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_greaterThanSignMaskSByte)); - mask = AdvSimd.Or(mask, AdvSimd.CompareEqual(sourceValue, s_graveAccentMaskSByte)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateAsciiMask(Vector128 sourceValue) - { - if (!AdvSimd.Arm64.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - // Anything above short.MaxValue but less than or equal char.MaxValue - // That's because anything between 32768 and 65535 (inclusive) will overflow and become negative. - Vector128 mask = AdvSimd.CompareLessThan(sourceValue, s_nullMaskInt16); - - // Anything above the ASCII range - mask = AdvSimd.Or(mask, AdvSimd.CompareGreaterThan(sourceValue, s_maxAsciiCharacterMaskInt16)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsNonAsciiByte(Vector128 value) - { - if (!AdvSimd.Arm64.IsSupported) - { - throw new PlatformNotSupportedException(); - } - - // most significant bit mask for a 64-bit byte vector - const ulong MostSignficantBitMask = 0x8080808080808080; - - value = AdvSimd.Arm64.MinPairwise(value, value); - return (value.AsUInt64().ToScalar() & MostSignficantBitMask) != 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetIndexOfFirstNonAsciiByte(Vector128 value) - { - if (!AdvSimd.Arm64.IsSupported || !BitConverter.IsLittleEndian) - { - throw new PlatformNotSupportedException(); - } - - // extractedBits[i] = (value[i] >> 7) & (1 << (12 * (i % 2))); - Vector128 mostSignificantBitIsSet = AdvSimd.ShiftRightArithmetic(value.AsSByte(), 7).AsByte(); - Vector128 extractedBits = AdvSimd.And(mostSignificantBitIsSet, s_bitmask); - - // collapse mask to lower bits - extractedBits = AdvSimd.Arm64.AddPairwise(extractedBits, extractedBits); - ulong mask = extractedBits.AsUInt64().ToScalar(); - - // calculate the index - int index = BitOperations.TrailingZeroCount(mask) >> 2; - Debug.Assert((mask != 0) ? index < 16 : index >= 16); - return index; - } - - private static readonly Vector128 s_nullMaskInt16 = Vector128.Zero; - private static readonly Vector128 s_spaceMaskInt16 = Vector128.Create((short)' '); - private static readonly Vector128 s_quotationMarkMaskInt16 = Vector128.Create((short)'"'); - private static readonly Vector128 s_reverseSolidusMaskInt16 = Vector128.Create((short)'\\'); - private static readonly Vector128 s_tildeMaskInt16 = Vector128.Create((short)'~'); - private static readonly Vector128 s_maxAsciiCharacterMaskInt16 = Vector128.Create((short)0x7F); // Delete control character - - private static readonly Vector128 s_spaceMaskSByte = Vector128.Create((sbyte)' '); - private static readonly Vector128 s_quotationMarkMaskSByte = Vector128.Create((sbyte)'"'); - private static readonly Vector128 s_ampersandMaskSByte = Vector128.Create((sbyte)'&'); - private static readonly Vector128 s_apostropheMaskSByte = Vector128.Create((sbyte)'\''); - private static readonly Vector128 s_plusSignMaskSByte = Vector128.Create((sbyte)'+'); - private static readonly Vector128 s_lessThanSignMaskSByte = Vector128.Create((sbyte)'<'); - private static readonly Vector128 s_greaterThanSignMaskSByte = Vector128.Create((sbyte)'>'); - private static readonly Vector128 s_reverseSolidusMaskSByte = Vector128.Create((sbyte)'\\'); - private static readonly Vector128 s_graveAccentMaskSByte = Vector128.Create((sbyte)'`'); - private static readonly Vector128 s_tildeMaskSByte = Vector128.Create((sbyte)'~'); - - private static readonly Vector128 s_bitmask = BitConverter.IsLittleEndian ? - Vector128.Create((ushort)0x1001).AsByte() : - Vector128.Create((ushort)0x0110).AsByte(); - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedBmpCodePointsBitmap.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedBmpCodePointsBitmap.cs new file mode 100644 index 0000000000000..377d1d0c071d4 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AllowedBmpCodePointsBitmap.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers.Binary; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text.Unicode; + +#if NETCOREAPP +using System.Numerics; +#endif + +namespace System.Text.Encodings.Web +{ + /// + /// A bitmap which represents all 64k codepoints in the + /// Basic Multilingual Plane. + /// + internal unsafe struct AllowedBmpCodePointsBitmap + { + private const int BitmapLengthInDWords = 64 * 1024 / 32; + private fixed uint Bitmap[BitmapLengthInDWords]; + + /// + /// Adds the given to the bitmap's allow list. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AllowChar(char value) + { + _GetIndexAndOffset(value, out nuint index, out int offset); + Bitmap[index] |= 1u << offset; + } + + /// + /// Removes the given from the bitmap's allow list. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ForbidChar(char value) + { + _GetIndexAndOffset(value, out nuint index, out int offset); + Bitmap[index] &= ~(1u << offset); + } + + /// + /// Removes all HTML-sensitive characters from the bitmap's allow list. + /// + public void ForbidHtmlCharacters() + { + ForbidChar('<'); + ForbidChar('>'); + ForbidChar('&'); + ForbidChar('\''); // can be used to escape attributes + ForbidChar('\"'); // can be used to escape attributes + ForbidChar('+'); // technically not HTML-specific, but can be used to perform UTF7-based attacks + } + + /// + /// Removes from the bitmap's allow list all code points which aren't mapped to defined characters + /// or which are otherwise always disallowed. + /// + /// + /// Always-disallowed categories include Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, and Zp. + /// + public void ForbidUndefinedCharacters() + { + fixed (uint* pBitmap = Bitmap) + { + ReadOnlySpan definedCharsBitmapAsLittleEndian = UnicodeHelpers.GetDefinedBmpCodePointsBitmapLittleEndian(); + Span thisAllowedCharactersBitmap = new Span(pBitmap, BitmapLengthInDWords); + Debug.Assert(definedCharsBitmapAsLittleEndian.Length == thisAllowedCharactersBitmap.Length * sizeof(uint)); + +#if NETCOREAPP + if (Vector.IsHardwareAccelerated && BitConverter.IsLittleEndian) + { + while (!definedCharsBitmapAsLittleEndian.IsEmpty) + { + (new Vector(definedCharsBitmapAsLittleEndian) & new Vector(thisAllowedCharactersBitmap)).CopyTo(thisAllowedCharactersBitmap); + definedCharsBitmapAsLittleEndian = definedCharsBitmapAsLittleEndian.Slice(Vector.Count); + thisAllowedCharactersBitmap = thisAllowedCharactersBitmap.Slice(Vector.Count); + } + Debug.Assert(thisAllowedCharactersBitmap.IsEmpty, "Both vectors should've been fully consumed."); + return; + } +#endif + + // Not Core, or not little-endian, or not SIMD-optimized. + for (int i = 0; i < thisAllowedCharactersBitmap.Length; i++) + { + thisAllowedCharactersBitmap[i] &= BinaryPrimitives.ReadUInt32LittleEndian(definedCharsBitmapAsLittleEndian.Slice(i * sizeof(uint))); + } + } + } + + /// + /// Queries the bitmap to see if the given is in the allow list. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool IsCharAllowed(char value) + { + // No bounds checks required: every char maps to a valid position in the bitmap + _GetIndexAndOffset(value, out nuint index, out int offset); + if ((Bitmap[index] & (1u << offset)) != 0) { return true; } + else { return false; } + } + + /// + /// Queries the bitmap to see if the given code point is in the allow list. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool IsCodePointAllowed(uint value) + { + if (!UnicodeUtility.IsBmpCodePoint(value)) { return false; } // we only understand BMP + _GetIndexAndOffset(value, out nuint index, out int offset); + if ((Bitmap[index] & (1u << offset)) != 0) { return true; } + else { return false; } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void _GetIndexAndOffset(uint value, out nuint index, out int offset) + { + UnicodeDebug.AssertIsBmpCodePoint(value); + index = value >> 5; + offset = (int)value & 0x1F; + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AsciiByteMap.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AsciiByteMap.cs new file mode 100644 index 0000000000000..17222ff8eda79 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/AsciiByteMap.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Text.Encodings.Web +{ + /// + /// A lookup map that maps individual ASCII chars to a single byte. + /// Storing a 0 byte indicates that no mapping exists for this input. + /// + internal unsafe struct AsciiByteMap + { + private const int BufferSize = 128; + private fixed byte Buffer[BufferSize]; + + internal void InsertAsciiChar(char key, byte value) + { + Debug.Assert(key < BufferSize); + Debug.Assert(value != 0); + + if (key < BufferSize) + { + Buffer[key] = value; + } + } + + /// + /// Returns false if is non-ASCII or if it + /// maps to a zero value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly bool TryLookup(Rune key, out byte value) + { + if (key.IsAscii) + { + byte entry = Buffer[(uint)key.Value]; + if (entry != 0) + { + value = entry; + return true; + } + } + + value = default; + return false; + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultHtmlEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultHtmlEncoder.cs new file mode 100644 index 0000000000000..50666e57a8f72 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultHtmlEncoder.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Numerics; +using System.Text.Unicode; + +namespace System.Text.Encodings.Web +{ + internal sealed class DefaultHtmlEncoder : HtmlEncoder + { + internal static readonly DefaultHtmlEncoder BasicLatinSingleton = new DefaultHtmlEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); + + private readonly OptimizedInboxTextEncoder _innerEncoder; + + internal DefaultHtmlEncoder(TextEncoderSettings settings) + { + if (settings is null) + { + throw new ArgumentNullException(nameof(settings)); + } + + _innerEncoder = new OptimizedInboxTextEncoder(EscaperImplementation.Singleton, settings.GetAllowedCodePointsBitmap()); + } + + public override int MaxOutputCharactersPerInputCharacter => 8; // "￿" is worst case for single char ("􏿿" [10 chars] worst case for arbitrary scalar value) + + /* + * These overrides should be copied to all other subclasses that are backed + * by the fast inbox escaping mechanism. + */ + +#pragma warning disable CS0618 // some of the adapters are intentionally marked [Obsolete] + private protected override OperationStatus EncodeCore(ReadOnlySpan source, Span destination, out int charsConsumed, out int charsWritten, bool isFinalBlock) + => _innerEncoder.Encode(source, destination, out charsConsumed, out charsWritten, isFinalBlock); + + private protected override OperationStatus EncodeUtf8Core(ReadOnlySpan utf8Source, Span utf8Destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) + => _innerEncoder.EncodeUtf8(utf8Source, utf8Destination, out bytesConsumed, out bytesWritten, isFinalBlock); + + private protected override int FindFirstCharacterToEncode(ReadOnlySpan text) + => _innerEncoder.GetIndexOfFirstCharToEncode(text); + + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + => _innerEncoder.FindFirstCharacterToEncode(text, textLength); + + public override int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) + => _innerEncoder.GetIndexOfFirstByteToEncode(utf8Text); + + public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) + => _innerEncoder.TryEncodeUnicodeScalar(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); + + public override bool WillEncode(int unicodeScalar) + => !_innerEncoder.IsScalarValueAllowed(new Rune(unicodeScalar)); +#pragma warning restore CS0618 + + /* + * End overrides section. + */ + + private sealed class EscaperImplementation : ScalarEscaperBase + { + internal static readonly EscaperImplementation Singleton = new EscaperImplementation(); + + private EscaperImplementation() { } + + internal override int EncodeUtf8(Rune value, Span destination) + { + if (value.Value == '<') + { + if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'l', (byte)'t', (byte)';')) { goto OutOfSpace; } + return 4; + } + else if (value.Value == '>') + { + if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'g', (byte)'t', (byte)';')) { goto OutOfSpace; } + return 4; + } + else if (value.Value == '&') + { + if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'a', (byte)'m', (byte)'p', (byte)';')) { goto OutOfSpace; } + return 5; + } + else if (value.Value == '\"') + { + if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'q', (byte)'u', (byte)'o', (byte)'t', (byte)';')) { goto OutOfSpace; } + return 6; + } + else + { + return TryEncodeScalarAsHex(this, (uint)value.Value, destination); + } + + OutOfSpace: + + return -1; + +#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller + static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span destination) +#pragma warning restore IDE0060 + { + UnicodeDebug.AssertIsValidScalar(scalarValue); + + // See comments in the UTF-16 equivalent method later in this file. + + int idxOfSemicolon = (int)((uint)BitOperations.Log2(scalarValue) / 4) + 4; + Debug.Assert(4 <= idxOfSemicolon && idxOfSemicolon <= 9, "Expected '�'..'􏿿'."); + + if (!SpanUtility.IsValidIndex(destination, idxOfSemicolon)) { goto OutOfSpaceInner; } + destination[idxOfSemicolon] = (byte)';'; + + if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'#', (byte)'x', (byte)'0')) + { + Debug.Fail("We should've had enough room to write 4 bytes."); + } + + destination = destination.Slice(3, idxOfSemicolon - 3); + for (int i = destination.Length - 1; SpanUtility.IsValidIndex(destination, i); i--) + { + char asUpperHex = HexConverter.ToCharUpper((int)scalarValue); + destination[i] = (byte)asUpperHex; + scalarValue >>= 4; // write a nibble - not a byte - at a time + } + + return destination.Length + 4; + + OutOfSpaceInner: + + return -1; + } + } + + internal override int EncodeUtf16(Rune value, Span destination) + { + if (value.Value == '<') + { + if (!SpanUtility.TryWriteChars(destination, '&', 'l', 't', ';')) { goto OutOfSpace; } + return 4; + } + else if (value.Value == '>') + { + if (!SpanUtility.TryWriteChars(destination, '&', 'g', 't', ';')) { goto OutOfSpace; } + return 4; + } + else if (value.Value == '&') + { + if (!SpanUtility.TryWriteChars(destination, '&', 'a', 'm', 'p', ';')) { goto OutOfSpace; } + return 5; + } + else if (value.Value == '\"') + { + if (!SpanUtility.TryWriteChars(destination, '&', 'q', 'u', 'o', 't', ';')) { goto OutOfSpace; } + return 6; + } + else + { + return TryEncodeScalarAsHex(this, (uint)value.Value, destination); + } + + OutOfSpace: + + return -1; + +#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller + static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span destination) +#pragma warning restore IDE0060 + { + UnicodeDebug.AssertIsValidScalar(scalarValue); + + // For inputs 0x0000..0x10FFFF, log2 will return 0..20. + // (It counts the number of bits following the highest set bit.) + // + // We divide by 4 to get the number of nibbles (this rounds down), + // then +1 to account for rounding effects. This also accounts for + // that when log2 results in an exact multiple of 4, no rounding has + // taken place, but we need to include a char for the preceding '0x1'. + // Finally, we +4 to account for the "&#x" prefix and the ";" suffix, + // then -1 to get the index of the last legal location we want to write to. + // >> +1 +4 -1 = +4 + + int idxOfSemicolon = (int)((uint)BitOperations.Log2(scalarValue) / 4) + 4; + Debug.Assert(4 <= idxOfSemicolon && idxOfSemicolon <= 9, "Expected '�'..'􏿿'."); + + if (!SpanUtility.IsValidIndex(destination, idxOfSemicolon)) { goto OutOfSpaceInner; } + destination[idxOfSemicolon] = ';'; + + // It's more efficient to write 4 chars at a time instead of 1 char. + // The '0' at the end will be overwritten. + if (!SpanUtility.TryWriteChars(destination, '&', '#', 'x', '0')) + { + Debug.Fail("We should've had enough room to write 4 chars."); + } + + destination = destination.Slice(3, idxOfSemicolon - 3); + for (int i = destination.Length - 1; SpanUtility.IsValidIndex(destination, i); i--) + { + char asUpperHex = HexConverter.ToCharUpper((int)scalarValue); + destination[i] = asUpperHex; + scalarValue >>= 4; // write a nibble - not a byte - at a time + } + + return destination.Length + 4; + + OutOfSpaceInner: + + return -1; + } + } + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoder.cs index def4d5f01c5af..7b23363e2e206 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoder.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoder.cs @@ -2,285 +2,215 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text.Internal; using System.Text.Unicode; -#if NETCOREAPP -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using System.Runtime.Intrinsics.Arm; -#endif - namespace System.Text.Encodings.Web { internal sealed class DefaultJavaScriptEncoder : JavaScriptEncoder { - private readonly AllowedCharactersBitmap _allowedCharacters; + internal static readonly DefaultJavaScriptEncoder BasicLatinSingleton = new DefaultJavaScriptEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); + internal static readonly DefaultJavaScriptEncoder UnsafeRelaxedEscapingSingleton = new DefaultJavaScriptEncoder(new TextEncoderSettings(UnicodeRanges.All), allowMinimalJsonEscaping: true); - private readonly int[] _asciiNeedsEscaping = new int[0x80]; + private readonly OptimizedInboxTextEncoder _innerEncoder; - public DefaultJavaScriptEncoder(TextEncoderSettings filter) + internal DefaultJavaScriptEncoder(TextEncoderSettings settings) + : this(settings, allowMinimalJsonEscaping: false) { - if (filter == null) + } + + private DefaultJavaScriptEncoder(TextEncoderSettings settings, bool allowMinimalJsonEscaping) + { + if (settings is null) { - throw new ArgumentNullException(nameof(filter)); + throw new ArgumentNullException(nameof(settings)); } - _allowedCharacters = filter.GetAllowedCharacters(); - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - _allowedCharacters.ForbidUndefinedCharacters(); - - // Forbid characters that are special in HTML. - // Even though this is a not HTML encoder, - // it's unfortunately common for developers to - // forget to HTML-encode a string once it has been JS-encoded, - // so this offers extra protection. - HtmlEncoderHelper.ForbidHtmlCharacters(_allowedCharacters); - // '\' (U+005C REVERSE SOLIDUS) must always be escaped in Javascript / ECMAScript / JSON. // '/' (U+002F SOLIDUS) is not Javascript / ECMAScript / JSON-sensitive so doesn't need to be escaped. - _allowedCharacters.ForbidCharacter('\\'); - // '`' (U+0060 GRAVE ACCENT) is ECMAScript-sensitive (see ECMA-262). - _allowedCharacters.ForbidCharacter('`'); - for (int i = 0; i < _asciiNeedsEscaping.Length; i++) - { - _asciiNeedsEscaping[i] = WillEncode(i) ? 1 : -1; - } + _innerEncoder = allowMinimalJsonEscaping + ? new OptimizedInboxTextEncoder(EscaperImplementation.SingletonMinimallyEscaped, settings.GetAllowedCodePointsBitmap(), forbidHtmlSensitiveCharacters: false, + extraCharactersToEscape: stackalloc char[] { '\"', '\\' }) + : new OptimizedInboxTextEncoder(EscaperImplementation.Singleton, settings.GetAllowedCodePointsBitmap(), forbidHtmlSensitiveCharacters: true, + extraCharactersToEscape: stackalloc char[] { '\\', '`' }); } - public DefaultJavaScriptEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } + public override int MaxOutputCharactersPerInputCharacter => 6; // "\uXXXX" for a single char ("\uXXXX\uYYYY" [12 chars] for supplementary scalar value) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool WillEncode(int unicodeScalar) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) - { - return true; - } + /* + * These overrides should be copied to all other subclasses that are backed + * by the fast inbox escaping mechanism. + */ - Debug.Assert(unicodeScalar >= char.MinValue && unicodeScalar <= char.MaxValue); +#pragma warning disable CS0618 // some of the adapters are intentionally marked [Obsolete] + private protected override OperationStatus EncodeCore(ReadOnlySpan source, Span destination, out int charsConsumed, out int charsWritten, bool isFinalBlock) + => _innerEncoder.Encode(source, destination, out charsConsumed, out charsWritten, isFinalBlock); - return !_allowedCharacters.IsUnicodeScalarAllowed(unicodeScalar); - } + private protected override OperationStatus EncodeUtf8Core(ReadOnlySpan utf8Source, Span utf8Destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) + => _innerEncoder.EncodeUtf8(utf8Source, utf8Destination, out bytesConsumed, out bytesWritten, isFinalBlock); + + private protected override int FindFirstCharacterToEncode(ReadOnlySpan text) + => _innerEncoder.GetIndexOfFirstCharToEncode(text); - [MethodImpl(MethodImplOptions.AggressiveInlining)] public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + => _innerEncoder.FindFirstCharacterToEncode(text, textLength); + + public override int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) + => _innerEncoder.GetIndexOfFirstByteToEncode(utf8Text); + + public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) + => _innerEncoder.TryEncodeUnicodeScalar(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); + + public override bool WillEncode(int unicodeScalar) + => !_innerEncoder.IsScalarValueAllowed(new Rune(unicodeScalar)); +#pragma warning restore CS0618 + + /* + * End overrides section. + */ + + private sealed class EscaperImplementation : ScalarEscaperBase { - if (text == null) + internal static readonly EscaperImplementation Singleton = new EscaperImplementation(allowMinimalEscaping: false); + internal static readonly EscaperImplementation SingletonMinimallyEscaped = new EscaperImplementation(allowMinimalEscaping: true); + + // Map stores the second byte for any ASCII input that can be escaped as the two-element sequence + // REVERSE SOLIDUS followed by a single character. For example, maps to the two chars "\n". + // The map does not contain an entry for chars which cannot be escaped in this manner. + private readonly AsciiByteMap _preescapedMap; + + private EscaperImplementation(bool allowMinimalEscaping) { - throw new ArgumentNullException(nameof(text)); + _preescapedMap.InsertAsciiChar('\b', (byte)'b'); + _preescapedMap.InsertAsciiChar('\t', (byte)'t'); + _preescapedMap.InsertAsciiChar('\n', (byte)'n'); + _preescapedMap.InsertAsciiChar('\f', (byte)'f'); + _preescapedMap.InsertAsciiChar('\r', (byte)'r'); + _preescapedMap.InsertAsciiChar('\\', (byte)'\\'); + + if (allowMinimalEscaping) + { + _preescapedMap.InsertAsciiChar('\"', (byte)'\"'); + } } - return _allowedCharacters.FindFirstCharacterToEncode(text, textLength); - } + // Writes a scalar value as a JavaScript-escaped character (or sequence of characters). + // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9 + // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 + // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf + // + // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not. + // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/" + // (in ECMA-262 this character is a NonEscape character); however, we + // don't encode SOLIDUS by default unless the caller has provided an + // explicit bitmap which does not contain it. In this case we'll assume + // that the caller didn't want a SOLIDUS written to the output at all, + // so it should be written using "\u002F" encoding. + // HTML-specific characters (including apostrophe and quotes) will + // be written out as numeric entities for defense-in-depth. - public override unsafe int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) - { - fixed (byte* ptr = utf8Text) + internal override int EncodeUtf8(Rune value, Span destination) { - int idx = 0; - -#if NETCOREAPP - if (Sse2.IsSupported || AdvSimd.Arm64.IsSupported) + if (_preescapedMap.TryLookup(value, out byte preescapedForm)) { - sbyte* startingAddress = (sbyte*)ptr; - while (utf8Text.Length - 16 >= idx) - { - Debug.Assert(startingAddress >= ptr && startingAddress <= (ptr + utf8Text.Length - 16)); - - bool containsNonAsciiBytes; - - // Load the next 16 bytes, and check for ASCII text. - // Any byte that's not in the ASCII range will already be negative when casted to signed byte. - if (Sse2.IsSupported) - { - Vector128 sourceValue = Sse2.LoadVector128(startingAddress); - containsNonAsciiBytes = Sse2Helper.ContainsNonAsciiByte(sourceValue); - } - else if (AdvSimd.Arm64.IsSupported) - { - Vector128 sourceValue = AdvSimd.LoadVector128(startingAddress); - containsNonAsciiBytes = AdvSimdHelper.ContainsNonAsciiByte(sourceValue); - } - else - { - throw new PlatformNotSupportedException(); - } - - if (containsNonAsciiBytes) - { - // At least one of the following 16 bytes is non-ASCII. - - int processNextSixteen = idx + 16; - Debug.Assert(processNextSixteen <= utf8Text.Length); - - while (idx < processNextSixteen) - { - Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length)); - - if (UnicodeUtility.IsAsciiCodePoint(ptr[idx])) - { - if (DoesAsciiNeedEncoding(ptr[idx]) == 1) - { - goto Return; - } - idx++; - } - else - { - OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar); - - Debug.Assert(nextScalarValue <= int.MaxValue); - if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue)) - { - goto Return; - } - - Debug.Assert(opStatus == OperationStatus.Done); - idx += utf8BytesConsumedForScalar; - } - } - } - else - { - if (DoesAsciiNeedEncoding(ptr[idx]) == 1 - - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1 - || DoesAsciiNeedEncoding(ptr[++idx]) == 1) - { - goto Return; - } - idx++; - } - startingAddress = (sbyte*)ptr + idx; - } + if (!SpanUtility.IsValidIndex(destination, 1)) { goto OutOfSpace; } + destination[0] = (byte)'\\'; + destination[1] = preescapedForm; + return 2; - // Process the remaining bytes. - Debug.Assert(utf8Text.Length - idx < 16); + OutOfSpace: + return -1; } -#endif - while (idx < utf8Text.Length) - { - Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length)); + return TryEncodeScalarAsHex(this, value, destination); - if (UnicodeUtility.IsAsciiCodePoint(ptr[idx])) +#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller + static int TryEncodeScalarAsHex(object @this, Rune value, Span destination) +#pragma warning restore IDE0060 + { + if (value.IsBmp) { - if (DoesAsciiNeedEncoding(ptr[idx]) == 1) - { - goto Return; - } - idx++; + // Write 6 bytes: "\uXXXX" + if (!SpanUtility.IsValidIndex(destination, 5)) { goto OutOfSpaceInner; } + destination[0] = (byte)'\\'; + destination[1] = (byte)'u'; + HexConverter.ToBytesBuffer((byte)value.Value, destination, 4); + HexConverter.ToBytesBuffer((byte)((uint)value.Value >> 8), destination, 2); + return 6; } else { - OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar); - - Debug.Assert(nextScalarValue <= int.MaxValue); - if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue)) - { - goto Return; - } - - Debug.Assert(opStatus == OperationStatus.Done); - idx += utf8BytesConsumedForScalar; + // Write 12 bytes: "\uXXXX\uYYYY" + UnicodeHelpers.GetUtf16SurrogatePairFromAstralScalarValue((uint)value.Value, out char highSurrogate, out char lowSurrogate); + if (!SpanUtility.IsValidIndex(destination, 11)) { goto OutOfSpaceInner; } + destination[0] = (byte)'\\'; + destination[1] = (byte)'u'; + HexConverter.ToBytesBuffer((byte)highSurrogate, destination, 4); + HexConverter.ToBytesBuffer((byte)((uint)highSurrogate >> 8), destination, 2); + destination[6] = (byte)'\\'; + destination[7] = (byte)'u'; + HexConverter.ToBytesBuffer((byte)lowSurrogate, destination, 10); + HexConverter.ToBytesBuffer((byte)((uint)lowSurrogate >> 8), destination, 8); + return 12; } - } - Debug.Assert(idx == utf8Text.Length); - idx = -1; // All bytes are allowed. + OutOfSpaceInner: - Return: - return idx; + return -1; + } } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int DoesAsciiNeedEncoding(byte value) - { - Debug.Assert(value <= 0x7F); - - int needsEscaping = _asciiNeedsEscaping[value]; + internal override int EncodeUtf16(Rune value, Span destination) + { + if (_preescapedMap.TryLookup(value, out byte preescapedForm)) + { + if (!SpanUtility.IsValidIndex(destination, 1)) { goto OutOfSpace; } + destination[0] = '\\'; + destination[1] = (char)preescapedForm; + return 2; - Debug.Assert(needsEscaping == 1 || needsEscaping == -1); + OutOfSpace: + return -1; + } - return needsEscaping; - } + return TryEncodeScalarAsHex(this, value, destination); - // The worst case encoding is 6 output chars per input char: [input] U+FFFF -> [output] "\uFFFF" - // We don't need to worry about astral code points since they're represented as encoded - // surrogate pairs in the output. - public override int MaxOutputCharactersPerInputCharacter => 12; // "\uFFFF\uFFFF" is the longest encoded form - - private const string s_b = "\\b"; - private const string s_t = "\\t"; - private const string s_n = "\\n"; - private const string s_f = "\\f"; - private const string s_r = "\\r"; - private const string s_back = "\\\\"; - - // Writes a scalar value as a JavaScript-escaped character (or sequence of characters). - // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9 - // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 - // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf - public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not. - // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/" - // (in ECMA-262 this character is a NonEscape character); however, we - // don't encode SOLIDUS by default unless the caller has provided an - // explicit bitmap which does not contain it. In this case we'll assume - // that the caller didn't want a SOLIDUS written to the output at all, - // so it should be written using "\u002F" encoding. - // HTML-specific characters (including apostrophe and quotes) will - // be written out as numeric entities for defense-in-depth. - // See UnicodeEncoderBase ctor comments for more info. +#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller + static int TryEncodeScalarAsHex(object @this, Rune value, Span destination) +#pragma warning restore IDE0060 + { + if (value.IsBmp) + { + // Write 6 chars: "\uXXXX" + if (!SpanUtility.IsValidIndex(destination, 5)) { goto OutOfSpaceInner; } + destination[0] = '\\'; + destination[1] = 'u'; + HexConverter.ToCharsBuffer((byte)value.Value, destination, 4); + HexConverter.ToCharsBuffer((byte)((uint)value.Value >> 8), destination, 2); + return 6; + } + else + { + // Write 12 chars: "\uXXXX\uYYYY" + UnicodeHelpers.GetUtf16SurrogatePairFromAstralScalarValue((uint)value.Value, out char highSurrogate, out char lowSurrogate); + if (!SpanUtility.IsValidIndex(destination, 11)) { goto OutOfSpaceInner; } + destination[0] = '\\'; + destination[1] = 'u'; + HexConverter.ToCharsBuffer((byte)highSurrogate, destination, 4); + HexConverter.ToCharsBuffer((byte)((uint)highSurrogate >> 8), destination, 2); + destination[6] = '\\'; + destination[7] = 'u'; + HexConverter.ToCharsBuffer((byte)lowSurrogate, destination, 10); + HexConverter.ToCharsBuffer((byte)((uint)lowSurrogate >> 8), destination, 8); + return 12; + } - Span destination = new Span(buffer, bufferLength); - if (!WillEncode(unicodeScalar)) - { - return TryWriteScalarAsChar(unicodeScalar, destination, out numberOfCharactersWritten); - } + OutOfSpaceInner: - string toCopy; - switch (unicodeScalar) - { - case '\b': toCopy = s_b; break; - case '\t': toCopy = s_t; break; - case '\n': toCopy = s_n; break; - case '\f': toCopy = s_f; break; - case '\r': toCopy = s_r; break; - case '\\': toCopy = s_back; break; - default: return JavaScriptEncoderHelper.TryWriteEncodedScalarAsNumericEntity(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); + return -1; + } } - return TryCopyCharacters(toCopy, destination, out numberOfCharactersWritten); } } } diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoderBasicLatin.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoderBasicLatin.cs deleted file mode 100644 index 5cabb79f596a2..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultJavaScriptEncoderBasicLatin.cs +++ /dev/null @@ -1,507 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text.Internal; -using System.Text.Unicode; - -#if NETCOREAPP -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using System.Runtime.Intrinsics.Arm; -#endif - -namespace System.Text.Encodings.Web -{ - internal sealed class DefaultJavaScriptEncoderBasicLatin : JavaScriptEncoder - { - internal static readonly DefaultJavaScriptEncoderBasicLatin s_singleton = new DefaultJavaScriptEncoderBasicLatin(); - - private DefaultJavaScriptEncoderBasicLatin() - { - var filter = new TextEncoderSettings(UnicodeRanges.BasicLatin); - - AllowedCharactersBitmap allowedCharacters = filter.GetAllowedCharacters(); - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - allowedCharacters.ForbidUndefinedCharacters(); - - // Forbid characters that are special in HTML. - // Even though this is a not HTML encoder, - // it's unfortunately common for developers to - // forget to HTML-encode a string once it has been JS-encoded, - // so this offers extra protection. - HtmlEncoderHelper.ForbidHtmlCharacters(allowedCharacters); - - // '\' (U+005C REVERSE SOLIDUS) must always be escaped in Javascript / ECMAScript / JSON. - // '/' (U+002F SOLIDUS) is not Javascript / ECMAScript / JSON-sensitive so doesn't need to be escaped. - allowedCharacters.ForbidCharacter('\\'); - - // '`' (U+0060 GRAVE ACCENT) is ECMAScript-sensitive (see ECMA-262). - allowedCharacters.ForbidCharacter('`'); - -#if DEBUG - // Verify and ensure that the AllowList bit map matches the set of allowed characters using AllowedCharactersBitmap - for (int i = 0; i < AllowList.Length; i++) - { - char ch = (char)i; - Debug.Assert((allowedCharacters.IsCharacterAllowed(ch) ? 1 : 0) == AllowList[ch]); - Debug.Assert(allowedCharacters.IsCharacterAllowed(ch) == !NeedsEscaping(ch)); - } - for (int i = AllowList.Length; i <= char.MaxValue; i++) - { - char ch = (char)i; - Debug.Assert(!allowedCharacters.IsCharacterAllowed(ch)); - Debug.Assert(NeedsEscaping(ch)); - } -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool WillEncode(int unicodeScalar) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) - { - return true; - } - - Debug.Assert(unicodeScalar >= char.MinValue && unicodeScalar <= char.MaxValue); - - return NeedsEscaping((char)unicodeScalar); - } - - public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) - { - if (text == null) - { - throw new ArgumentNullException(nameof(text)); - } - - Debug.Assert(textLength >= 0); - - if (textLength == 0) - { - goto AllAllowed; - } - - int idx = 0; - short* ptr = (short*)text; - short* end = ptr + (uint)textLength; - -#if NETCOREAPP - if (Sse2.IsSupported || (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian)) - { - if (textLength >= Vector128.Count) - { - goto VectorizedStart; - } - } - - Sequential: -#endif - Debug.Assert(textLength > 0 && ptr < end); - - // For performance on the Mono interpreter, avoid referencing the static table for every value. - ReadOnlySpan allowListLocal = AllowList; - - do - { - Debug.Assert(text <= ptr && ptr < (text + textLength)); - - char value = *(char*)ptr; - - // NeedsEscaping() is lifted below for perf; verify semantics remain consistent. - Debug.Assert((value > LastAsciiCharacter || allowListLocal[value] == 0) == NeedsEscaping(value)); - - if (value > LastAsciiCharacter || allowListLocal[value] == 0) - { - goto Return; - } - - ptr++; - idx++; - } - while (ptr < end); - - AllAllowed: - idx = -1; - - Return: - return idx; - -#if NETCOREAPP - VectorizedStart: - int index; - short* vectorizedEnd; - - if (textLength >= 2 * Vector128.Count) - { - vectorizedEnd = end - 2 * Vector128.Count; - - do - { - Debug.Assert(text <= ptr && ptr <= (text + textLength - 2 * Vector128.Count)); - - // Load the next 16 characters, combine them to one byte vector. - // Chars that don't cleanly convert to ASCII bytes will get converted (saturated) to - // somewhere in the range [0x7F, 0xFF], which the NeedsEscaping method will detect. - Vector128 sourceValue; - - if (Sse2.IsSupported) - { - sourceValue = Sse2.PackSignedSaturate( - Sse2.LoadVector128(ptr), - Sse2.LoadVector128(ptr + Vector128.Count)); - } - else if (AdvSimd.Arm64.IsSupported) - { - Vector64 lower = AdvSimd.ExtractNarrowingSaturateLower(AdvSimd.LoadVector128(ptr)); - sourceValue = AdvSimd.ExtractNarrowingSaturateUpper(lower, AdvSimd.LoadVector128(ptr + Vector128.Count)); - } - else - { - throw new PlatformNotSupportedException(); - } - - // Check if any of the 16 characters need to be escaped. - index = NeedsEscaping(sourceValue); - - // If index >= 16, that means none of the 16 characters needed to be escaped. - if (index < 16) - { - goto VectorizedFound; - } - - ptr += 2 * Vector128.Count; - } - while (ptr <= vectorizedEnd); - } - - vectorizedEnd = end - Vector128.Count; - - Vectorized: - // PERF: JIT produces better code for do-while as for a while-loop (no spills) - if (ptr <= vectorizedEnd) - { - do - { - Debug.Assert(text <= ptr && ptr <= (text + textLength - Vector128.Count)); - - // Load the next 8 characters + a dummy known that it must not be escaped. - // Put the dummy second, so it's easier for GetIndexOfFirstNeedToEscape. - Vector128 sourceValue; - - if (Sse2.IsSupported) - { - sourceValue = Sse2.PackSignedSaturate( - Sse2.LoadVector128(ptr), - Vector128.Create((short)'A')); // max. one "iteration", so no need to cache this vector - } - else if (AdvSimd.Arm64.IsSupported) - { - Vector64 saturated = AdvSimd.ExtractNarrowingSaturateLower(AdvSimd.LoadVector128(ptr)); - sourceValue = Vector128.Create(saturated, Vector64.Create((sbyte)'A')); - } - else - { - throw new PlatformNotSupportedException(); - } - - index = NeedsEscaping(sourceValue); - - // If index >= 16, that means none of the 16 bytes needed to be escaped. - if (index < 16) - { - goto VectorizedFound; - } - - ptr += Vector128.Count; - } - while (ptr <= vectorizedEnd); - } - - // Process the remaining characters. - Debug.Assert(end - ptr < Vector128.Count); - - // Process the remaining elements vectorized, only if the remaining count - // is above thresholdForRemainingVectorized, otherwise process them sequential. - // Threshold found by testing. - const int thresholdForRemainingVectorized = 5; - if (ptr < end - thresholdForRemainingVectorized) - { - ptr = vectorizedEnd; - goto Vectorized; - } - - idx = CalculateIndex(ptr, text); - - if (idx < textLength) - { - goto Sequential; - } - - goto AllAllowed; - - VectorizedFound: - index += CalculateIndex(ptr, text); - return index; - - static int CalculateIndex(short* ptr, char* text) - { - // Subtraction with short* results in a idiv, so use byte* and shift - return (int)(((byte*)ptr - (byte*)text) >> 1); - } -#endif - } - - public override unsafe int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) - { - fixed (byte* pValue = utf8Text) - { - uint textLength = (uint)utf8Text.Length; - - if (textLength == 0) - { - goto AllAllowed; - } - - int idx = 0; - byte* ptr = pValue; - byte* end = ptr + textLength; - -#if NETCOREAPP - if (Sse2.IsSupported || (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian)) - { - if (textLength >= Vector128.Count) - { - goto Vectorized; - } - } - - Sequential: -#endif - Debug.Assert(textLength > 0 && ptr < end); - - // For performance on the Mono interpreter, avoid referencing the static table for every value. - ReadOnlySpan allowListLocal = AllowList; - - do - { - Debug.Assert(pValue <= ptr && ptr < (pValue + utf8Text.Length)); - - // NeedsEscaping() is lifted below for perf; verify semantics remain consistent. - Debug.Assert((allowListLocal[*ptr] == 0) == NeedsEscaping(*ptr)); - - if (allowListLocal[*ptr] == 0) - { - goto Return; - } - - ptr++; - idx++; - } - while (ptr < end); - - AllAllowed: - idx = -1; - - Return: - return idx; - -#if NETCOREAPP - Vectorized: - byte* vectorizedEnd = end - Vector128.Count; - int index; - - do - { - Debug.Assert(pValue <= ptr && ptr <= (pValue + utf8Text.Length - Vector128.Count)); - - // Load the next 16 bytes - Vector128 sourceValue = Sse2.IsSupported ? - Sse2.LoadVector128((sbyte*)ptr) : - AdvSimd.LoadVector128((sbyte*)ptr); - - index = NeedsEscaping(sourceValue); - - // If index >= 16, that means none of the 16 bytes needed to be escaped. - if (index < 16) - { - goto VectorizedFound; - } - - ptr += Vector128.Count; - } - while (ptr <= vectorizedEnd); - - // Process the remaining elements. - Debug.Assert(end - ptr < Vector128.Count); - - // Process the remaining elements vectorized, only if the remaining count - // is above thresholdForRemainingVectorized, otherwise process them sequential. - const int thresholdForRemainingVectorized = 4; - if (ptr < end - thresholdForRemainingVectorized) - { - // PERF: duplicate instead of jumping at the beginning of the previous loop - // otherwise all the static data (vectors) will be re-assigned to registers, - // so they are re-used. - - Debug.Assert(pValue <= vectorizedEnd && vectorizedEnd <= (pValue + utf8Text.Length - Vector128.Count)); - - // Load the last 16 bytes - Vector128 sourceValue = Sse2.IsSupported ? - Sse2.LoadVector128((sbyte*)vectorizedEnd) : - AdvSimd.LoadVector128((sbyte*)vectorizedEnd); - - // If index >= 16, that means none of the 16 bytes needed to be escaped. - index = NeedsEscaping(sourceValue); - if (index < 16) - { - ptr = vectorizedEnd; - goto VectorizedFound; - } - - goto AllAllowed; - } - - idx = CalculateIndex(ptr, pValue); - - if (idx < textLength) - { - goto Sequential; - } - - goto AllAllowed; - - VectorizedFound: - index += CalculateIndex(ptr, pValue); - return index; - - static int CalculateIndex(byte* ptr, byte* pValue) => (int)(ptr - pValue); -#endif - } - } - - // The worst case encoding is 6 output chars per input char: [input] U+FFFF -> [output] "\uFFFF" - // We don't need to worry about astral code points since they're represented as encoded - // surrogate pairs in the output. - public override int MaxOutputCharactersPerInputCharacter => 12; // "\uFFFF\uFFFF" is the longest encoded form - - private const string s_b = "\\b"; - private const string s_t = "\\t"; - private const string s_n = "\\n"; - private const string s_f = "\\f"; - private const string s_r = "\\r"; - private const string s_back = "\\\\"; - - // Writes a scalar value as a JavaScript-escaped character (or sequence of characters). - // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9 - // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 - // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf - public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not. - // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/" - // (in ECMA-262 this character is a NonEscape character); however, we - // don't encode SOLIDUS by default unless the caller has provided an - // explicit bitmap which does not contain it. In this case we'll assume - // that the caller didn't want a SOLIDUS written to the output at all, - // so it should be written using "\u002F" encoding. - // HTML-specific characters (including apostrophe and quotes) will - // be written out as numeric entities for defense-in-depth. - // See UnicodeEncoderBase ctor comments for more info. - - Span destination = new Span(buffer, bufferLength); - if (!WillEncode(unicodeScalar)) - { - return TryWriteScalarAsChar(unicodeScalar, destination, out numberOfCharactersWritten); - } - - string toCopy; - switch (unicodeScalar) - { - case '\b': - toCopy = s_b; - break; - case '\t': - toCopy = s_t; - break; - case '\n': - toCopy = s_n; - break; - case '\f': - toCopy = s_f; - break; - case '\r': - toCopy = s_r; - break; - case '\\': - toCopy = s_back; - break; - default: - return JavaScriptEncoderHelper.TryWriteEncodedScalarAsNumericEntity(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); - } - return TryCopyCharacters(toCopy, destination, out numberOfCharactersWritten); - } - - private static ReadOnlySpan AllowList => new byte[byte.MaxValue + 1] - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+0000..U+000F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+0010..U+001F - 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, // U+0020..U+002F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, // U+0030..U+003F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // U+0040..U+004F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // U+0050..U+005F - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // U+0060..U+006F - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // U+0070..U+007F - - // Also include the ranges from U+0080 to U+00FF for performance to avoid UTF8 code from checking boundary. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // U+00F0..U+00FF - }; - - public const int LastAsciiCharacter = 0x7F; - - private static bool NeedsEscaping(byte value) => AllowList[value] == 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool NeedsEscaping(char value) => value > LastAsciiCharacter || AllowList[value] == 0; - -#if NETCOREAPP - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int NeedsEscaping(Vector128 sourceValue) - { - Debug.Assert(Sse2.IsSupported || AdvSimd.Arm64.IsSupported); - - if (Sse2.IsSupported) - { - // Check if any of the 16 bytes need to be escaped. - Vector128 mask = Ssse3.IsSupported - ? Ssse3Helper.CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin(sourceValue) - : Sse2Helper.CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin(sourceValue); - - int index = Sse2Helper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - return index; - } - else - { - Vector128 mask = AdvSimdHelper.CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin(sourceValue); - int index = AdvSimdHelper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - return index; - } - } -#endif - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultUrlEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultUrlEncoder.cs new file mode 100644 index 0000000000000..95f444c56368a --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/DefaultUrlEncoder.cs @@ -0,0 +1,201 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Text.Unicode; + +namespace System.Text.Encodings.Web +{ + internal sealed class DefaultUrlEncoder : UrlEncoder + { + internal static readonly DefaultUrlEncoder BasicLatinSingleton = new DefaultUrlEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); + + private readonly OptimizedInboxTextEncoder _innerEncoder; + + internal DefaultUrlEncoder(TextEncoderSettings settings) + { + if (settings is null) + { + throw new ArgumentNullException(nameof(settings)); + } + + // Per RFC 3987, Sec. 2.2, we want encodings that are safe for + // four particular components: 'isegment', 'ipath-noscheme', + // 'iquery', and 'ifragment'. The relevant definitions are below. + // + // ipath-noscheme = isegment-nz-nc *( "/" isegment ) + // + // isegment = *ipchar + // + // isegment-nz-nc = 1*( iunreserved / pct-encoded / sub-delims + // / "@" ) + // ; non-zero-length segment without any colon ":" + // + // ipchar = iunreserved / pct-encoded / sub-delims / ":" + // / "@" + // + // iquery = *( ipchar / iprivate / "/" / "?" ) + // + // ifragment = *( ipchar / "/" / "?" ) + // + // iunreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar + // + // ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF + // / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD + // / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD + // / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD + // / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD + // / %xD0000-DFFFD / %xE1000-EFFFD + // + // pct-encoded = "%" HEXDIG HEXDIG + // + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + // / "*" / "+" / "," / ";" / "=" + // + // The only common characters between these four components are the + // intersection of 'isegment-nz-nc' and 'ipchar', which is really + // just 'isegment-nz-nc' (colons forbidden). + // + // From this list, the base encoder already forbids "&", "'", "+", + // and we'll additionally forbid "=" since it has special meaning + // in x-www-form-urlencoded representations. + // + // This means that the full list of allowed characters from the + // Basic Latin set is: + // ALPHA / DIGIT / "-" / "." / "_" / "~" / "!" / "$" / "(" / ")" / "*" / "," / ";" / "@" + + _innerEncoder = new OptimizedInboxTextEncoder(EscaperImplementation.Singleton, settings.GetAllowedCodePointsBitmap(), extraCharactersToEscape: stackalloc char[] { + ' ', // chars from Basic Latin which aren't already disallowed by the base encoder + '#', + '%', + '/', + ':', + '=', + '?', + '[', + '\\', + ']', + '^', + '`', + '{', + '|', + '}', + '\uFFF0', // specials (U+FFF0 .. U+FFFF) are forbidden by the definition of 'ucschar' above + '\uFFF1', + '\uFFF2', + '\uFFF3', + '\uFFF4', + '\uFFF5', + '\uFFF6', + '\uFFF7', + '\uFFF8', + '\uFFF9', + '\uFFFA', + '\uFFFB', + '\uFFFC', + '\uFFFD', + '\uFFFE', + '\uFFFF', + }); + } + + public override int MaxOutputCharactersPerInputCharacter => 9; // "%XX%YY%ZZ" for a single char ("%XX%YY%ZZ%WW" [12 chars] for supplementary scalar value) + + /* + * These overrides should be copied to all other subclasses that are backed + * by the fast inbox escaping mechanism. + */ + +#pragma warning disable CS0618 // some of the adapters are intentionally marked [Obsolete] + private protected override OperationStatus EncodeCore(ReadOnlySpan source, Span destination, out int charsConsumed, out int charsWritten, bool isFinalBlock) + => _innerEncoder.Encode(source, destination, out charsConsumed, out charsWritten, isFinalBlock); + + private protected override OperationStatus EncodeUtf8Core(ReadOnlySpan utf8Source, Span utf8Destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) + => _innerEncoder.EncodeUtf8(utf8Source, utf8Destination, out bytesConsumed, out bytesWritten, isFinalBlock); + + private protected override int FindFirstCharacterToEncode(ReadOnlySpan text) + => _innerEncoder.GetIndexOfFirstCharToEncode(text); + + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + => _innerEncoder.FindFirstCharacterToEncode(text, textLength); + + public override int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) + => _innerEncoder.GetIndexOfFirstByteToEncode(utf8Text); + + public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) + => _innerEncoder.TryEncodeUnicodeScalar(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); + + public override bool WillEncode(int unicodeScalar) + => !_innerEncoder.IsScalarValueAllowed(new Rune(unicodeScalar)); +#pragma warning restore CS0618 + + /* + * End overrides section. + */ + + private sealed class EscaperImplementation : ScalarEscaperBase + { + internal static readonly EscaperImplementation Singleton = new EscaperImplementation(); + + private EscaperImplementation() { } + + internal override int EncodeUtf8(Rune value, Span destination) + { + uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)value.Value); + + if (!SpanUtility.IsValidIndex(destination, 2)) { goto OutOfSpace; } + destination[0] = (byte)'%'; + HexConverter.ToBytesBuffer((byte)utf8lsb, destination, startingIndex: 1); + if ((utf8lsb >>= 8) == 0) { return 3; } // "%XX" + + if (!SpanUtility.IsValidIndex(destination, 5)) { goto OutOfSpace; } + destination[3] = (byte)'%'; + HexConverter.ToBytesBuffer((byte)utf8lsb, destination, startingIndex: 4); + if ((utf8lsb >>= 8) == 0) { return 6; } // "%XX%YY" + + if (!SpanUtility.IsValidIndex(destination, 8)) { goto OutOfSpace; } + destination[6] = (byte)'%'; + HexConverter.ToBytesBuffer((byte)utf8lsb, destination, startingIndex: 7); + if ((utf8lsb >>= 8) == 0) { return 9; } // "%XX%YY%ZZ" + + if (!SpanUtility.IsValidIndex(destination, 11)) { goto OutOfSpace; } + destination[9] = (byte)'%'; + HexConverter.ToBytesBuffer((byte)utf8lsb, destination, startingIndex: 10); + return 12; // "%XX%YY%ZZ%WW" + + OutOfSpace: + + return -1; + } + + internal override int EncodeUtf16(Rune value, Span destination) + { + uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)value.Value); + + if (!SpanUtility.IsValidIndex(destination, 2)) { goto OutOfSpace; } + destination[0] = '%'; + HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 1); + if ((utf8lsb >>= 8) == 0) { return 3; } // "%XX" + + if (!SpanUtility.IsValidIndex(destination, 5)) { goto OutOfSpace; } + destination[3] = '%'; + HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 4); + if ((utf8lsb >>= 8) == 0) { return 6; } // "%XX%YY" + + if (!SpanUtility.IsValidIndex(destination, 8)) { goto OutOfSpace; } + destination[6] = '%'; + HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 7); + if ((utf8lsb >>= 8) == 0) { return 9; } // "%XX%YY%ZZ" + + if (!SpanUtility.IsValidIndex(destination, 11)) { goto OutOfSpace; } + destination[9] = '%'; + HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 10); + return 12; // "%XX%YY%ZZ%WW" + + OutOfSpace: + + return -1; + } + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/HtmlEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/HtmlEncoder.cs index 9a19f90b3b5cd..706bb85954635 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/HtmlEncoder.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/HtmlEncoder.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text.Internal; using System.Text.Unicode; namespace System.Text.Encodings.Web @@ -16,10 +13,7 @@ public abstract class HtmlEncoder : TextEncoder /// /// Returns a default built-in instance of . /// - public static HtmlEncoder Default - { - get { return DefaultHtmlEncoder.Singleton; } - } + public static HtmlEncoder Default => DefaultHtmlEncoder.BasicLatinSingleton; /// /// Creates a new instance of HtmlEncoder with provided settings. @@ -39,135 +33,7 @@ public static HtmlEncoder Create(TextEncoderSettings settings) /// Some characters in might still get encoded, i.e. this parameter is just telling the encoder what ranges it is allowed to not encode, not what characters it must not encode. public static HtmlEncoder Create(params UnicodeRange[] allowedRanges) { - return new DefaultHtmlEncoder(allowedRanges); - } - } - - internal sealed class DefaultHtmlEncoder : HtmlEncoder - { - private readonly AllowedCharactersBitmap _allowedCharacters; - internal static readonly DefaultHtmlEncoder Singleton = new DefaultHtmlEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); - - public DefaultHtmlEncoder(TextEncoderSettings settings) - { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } - - _allowedCharacters = settings.GetAllowedCharacters(); - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - _allowedCharacters.ForbidUndefinedCharacters(); - - HtmlEncoderHelper.ForbidHtmlCharacters(_allowedCharacters); - } - - public DefaultHtmlEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool WillEncode(int unicodeScalar) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) return true; - return !_allowedCharacters.IsUnicodeScalarAllowed(unicodeScalar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) - { - return _allowedCharacters.FindFirstCharacterToEncode(text, textLength); - } - - public override int MaxOutputCharactersPerInputCharacter - { - get { return 10; } // "􏿿" is the longest encoded form - } - - private const string s_quote = """; - private const string s_ampersand = "&"; - private const string s_lessthan = "<"; - private const string s_greaterthan = ">"; - - public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - Span destination = new Span(buffer, bufferLength); - if (!WillEncode(unicodeScalar)) { return TryWriteScalarAsChar(unicodeScalar, destination, out numberOfCharactersWritten); } - else if (unicodeScalar == '\"') { return TryCopyCharacters(s_quote, destination, out numberOfCharactersWritten); } - else if (unicodeScalar == '&') { return TryCopyCharacters(s_ampersand, destination, out numberOfCharactersWritten); } - else if (unicodeScalar == '<') { return TryCopyCharacters(s_lessthan, destination, out numberOfCharactersWritten); } - else if (unicodeScalar == '>') { return TryCopyCharacters(s_greaterthan, destination, out numberOfCharactersWritten); } - else { return TryWriteEncodedScalarAsNumericEntity(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); } - } - - private static unsafe bool TryWriteEncodedScalarAsNumericEntity(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - Debug.Assert(buffer != null && bufferLength >= 0); - - // We're writing the characters in reverse, first determine - // how many there are - const int nibbleSize = 4; - int numberOfHexCharacters = 0; - int compareUnicodeScalar = unicodeScalar; - - do - { - Debug.Assert(numberOfHexCharacters < 8, "Couldn't have written 8 characters out by this point."); - numberOfHexCharacters++; - compareUnicodeScalar >>= nibbleSize; - } while (compareUnicodeScalar != 0); - - numberOfCharactersWritten = numberOfHexCharacters + 4; // four chars are &, #, x, and ; - Debug.Assert(numberOfHexCharacters > 0, "At least one character should've been written."); - - if (numberOfHexCharacters + 4 > bufferLength) - { - numberOfCharactersWritten = 0; - return false; - } - // Finally, write out the HTML-encoded scalar value. - *buffer = '&'; - buffer++; - *buffer = '#'; - buffer++; - *buffer = 'x'; - - // Jump to the end of the hex position and write backwards - buffer += numberOfHexCharacters; - do - { - *buffer = HexConverter.ToCharUpper(unicodeScalar); - unicodeScalar >>= nibbleSize; - buffer--; - } - while (unicodeScalar != 0); - - buffer += numberOfHexCharacters + 1; - *buffer = ';'; - return true; - } - } - - /// - /// Separates static methods from HtmlEncoder and DefaultHtmlEncoder so those classes can be trimmed - /// when only these static methods are needed. - /// - internal static class HtmlEncoderHelper - { - internal static void ForbidHtmlCharacters(AllowedCharactersBitmap allowedCharacters) - { - allowedCharacters.ForbidCharacter('<'); - allowedCharacters.ForbidCharacter('>'); - allowedCharacters.ForbidCharacter('&'); - allowedCharacters.ForbidCharacter('\''); // can be used to escape attributes - allowedCharacters.ForbidCharacter('\"'); // can be used to escape attributes - allowedCharacters.ForbidCharacter('+'); // technically not HTML-specific, but can be used to perform UTF7-based attacks + return new DefaultHtmlEncoder(new TextEncoderSettings(allowedRanges)); } } } diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoder.cs index 19d306b874d73..0aa6ff6842bb5 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoder.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoder.cs @@ -13,7 +13,7 @@ public abstract class JavaScriptEncoder : TextEncoder /// /// Returns a default built-in instance of . /// - public static JavaScriptEncoder Default => DefaultJavaScriptEncoderBasicLatin.s_singleton; + public static JavaScriptEncoder Default => DefaultJavaScriptEncoder.BasicLatinSingleton; /// /// Returns a built-in instance of that is less strict about what gets encoded. @@ -33,7 +33,7 @@ public abstract class JavaScriptEncoder : TextEncoder /// Unlike the , this encoder instance allows some other characters to go through unescaped (for example, '+'), and hence must be used cautiously. /// /// - public static JavaScriptEncoder UnsafeRelaxedJsonEscaping => UnsafeRelaxedJavaScriptEncoder.s_singleton; + public static JavaScriptEncoder UnsafeRelaxedJsonEscaping => DefaultJavaScriptEncoder.UnsafeRelaxedEscapingSingleton; /// /// Creates a new instance of JavaScriptEncoder with provided settings. @@ -53,7 +53,7 @@ public static JavaScriptEncoder Create(TextEncoderSettings settings) /// Some characters in might still get encoded, i.e. this parameter is just telling the encoder what ranges it is allowed to not encode, not what characters it must not encode. public static JavaScriptEncoder Create(params UnicodeRange[] allowedRanges) { - return new DefaultJavaScriptEncoder(allowedRanges); + return new DefaultJavaScriptEncoder(new TextEncoderSettings(allowedRanges)); } } } diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoderHelper.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoderHelper.cs deleted file mode 100644 index f6433613148e5..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/JavaScriptEncoderHelper.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Text.Unicode; - -namespace System.Text.Encodings.Web -{ - internal static class JavaScriptEncoderHelper - { - public static unsafe bool TryWriteEncodedScalarAsNumericEntity(int unicodeScalar, char* buffer, int length, out int numberOfCharactersWritten) - { - Debug.Assert(buffer != null && length >= 0); - - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) - { - // Convert this back to UTF-16 and write out both characters. - UnicodeHelpers.GetUtf16SurrogatePairFromAstralScalarValue(unicodeScalar, out char leadingSurrogate, out char trailingSurrogate); - if (TryWriteEncodedSingleCharacter(leadingSurrogate, buffer, length, out int leadingSurrogateCharactersWritten) && - TryWriteEncodedSingleCharacter(trailingSurrogate, buffer + leadingSurrogateCharactersWritten, length - leadingSurrogateCharactersWritten, out numberOfCharactersWritten) - ) - { - numberOfCharactersWritten += leadingSurrogateCharactersWritten; - return true; - } - else - { - numberOfCharactersWritten = 0; - return false; - } - } - else - { - // This is only a single character. - return TryWriteEncodedSingleCharacter(unicodeScalar, buffer, length, out numberOfCharactersWritten); - } - } - - // Writes an encoded scalar value (in the BMP) as a JavaScript-escaped character. - private static unsafe bool TryWriteEncodedSingleCharacter(int unicodeScalar, char* buffer, int length, out int numberOfCharactersWritten) - { - Debug.Assert(buffer != null && length >= 0); - Debug.Assert(!UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar), "The incoming value should've been in the BMP."); - - if (length < 6) - { - numberOfCharactersWritten = 0; - return false; - } - - // Encode this as 6 chars "\uFFFF". - *buffer = '\\'; - buffer++; - *buffer = 'u'; - buffer++; - *buffer = HexConverter.ToCharUpper(unicodeScalar >> 12); - buffer++; - *buffer = HexConverter.ToCharUpper(unicodeScalar >> 8); - buffer++; - *buffer = HexConverter.ToCharUpper(unicodeScalar >> 4); - buffer++; - *buffer = HexConverter.ToCharUpper(unicodeScalar); - - numberOfCharactersWritten = 6; - return true; - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.AdvSimd64.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.AdvSimd64.cs new file mode 100644 index 0000000000000..9a2acba64d360 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.AdvSimd64.cs @@ -0,0 +1,257 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Arm; + +namespace System.Text.Encodings.Web +{ + internal sealed partial class OptimizedInboxTextEncoder + { + private unsafe nuint GetIndexOfFirstByteToEncodeAdvSimd64(byte* pData, nuint lengthInBytes) + { + Debug.Assert(AdvSimd.Arm64.IsSupported); + Debug.Assert(BitConverter.IsLittleEndian); + + Vector128 vec0xF = Vector128.Create((byte)0xF); + Vector128 vecPowersOfTwo = Vector128.Create(1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0); + Vector128 vecPairwiseAddNibbleBitmask = Vector128.Create((ushort)0xF00F).AsByte(); // little endian only + Vector128 allowedCodePoints = _allowedAsciiCodePoints.AsVector; + ulong resultScalar; + + nuint i = 0; + if (lengthInBytes >= 16) + { + nuint lastLegalIterationFor16CharRead = lengthInBytes & unchecked((nuint)(nint)~0xF); + + do + { + // Read 16 bytes at a time into a single 128-bit vector. + + Vector128 packed = AdvSimd.LoadVector128(pData + i); // unaligned read + + // Each element of the packed vector corresponds to a byte of untrusted source data. It will + // have the format [ ..., 0xYZ, ... ]. We use the low nibble of each byte to index into + // the 'allowedCodePoints' vector, and we use the high nibble of each byte to select a bit + // from the corresponding element in the 'allowedCodePoints' vector. + // + // Example: let packed := [ ..., 0x6D ('m'), ... ] + // The final 'result' vector will contain a non-zero value in the corresponding space iff the + // 0xD element in the 'allowedCodePoints' vector has its 1 << 0x6 bit set. + // + // We rely on the fact that when we perform an arithmetic shift of vector values to get the + // high nibble into the low 4 bits, we'll smear the high (non-ASCII) bit, causing the vector + // element value to be in the range [ 128..255 ]. This causes the tbl lookup to return 0x00 + // for that particular element in the 'vecPowersOfTwoShuffled' vector, meaning that escaping is required. + + var allowedCodePointsShuffled = AdvSimd.Arm64.VectorTableLookup(allowedCodePoints, AdvSimd.And(packed, vec0xF)); + var vecPowersOfTwoShuffled = AdvSimd.Arm64.VectorTableLookup(vecPowersOfTwo, AdvSimd.ShiftRightArithmetic(packed.AsSByte(), 4).AsByte()); + var result = AdvSimd.CompareTest(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + + // Now, each element of 'result' contains 0xFF if the corresponding element in 'packed' is allowed; + // and it contains a zero value if the corresponding element in 'packed' is disallowed. We'll convert + // this into a vector where if 0xFF occurs in an even-numbered index, it gets converted to 0x0F; and + // if 0xFF occurs in an odd-numbered index, it gets converted to 0xF0. This allows us to collapse + // the Vector128 to a 64-bit unsigned integer, where each of the 16 nibbles in the 64-bit integer + // corresponds to whether an element in the 'result' vector was originally 0xFF or 0x00. + + var maskedResult = AdvSimd.And(result, vecPairwiseAddNibbleBitmask); + resultScalar = AdvSimd.Arm64.AddPairwise(maskedResult, maskedResult).AsUInt64().ToScalar(); + + if (resultScalar != ulong.MaxValue) + { + goto PairwiseAddMaskContainsDataWhichRequiresEscaping; + } + } while ((i += 16) < lastLegalIterationFor16CharRead); + } + + if ((lengthInBytes & 8) != 0) + { + // Read 8 bytes at a time into a single 64-bit vector, extended to 128 bits. + // Same logic as the 16-byte case, but we don't need to worry about the pairwise add step. + // We'll treat the low 64 bits of the 'result' vector as its own scalar element. + + Vector128 packed = AdvSimd.LoadVector64(pData + i).ToVector128Unsafe(); // unaligned read + var allowedCodePointsShuffled = AdvSimd.Arm64.VectorTableLookup(allowedCodePoints, AdvSimd.And(packed, vec0xF)); + var vecPowersOfTwoShuffled = AdvSimd.Arm64.VectorTableLookup(vecPowersOfTwo, AdvSimd.ShiftRightArithmetic(packed.AsSByte(), 4).AsByte()); + var result = AdvSimd.CompareTest(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + resultScalar = result.AsUInt64().ToScalar(); + + if (resultScalar != ulong.MaxValue) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 8; + } + + if ((lengthInBytes & 4) != 0) + { + // Read 4 bytes at a time into a single element, extended to a 128-bit vector. + // Same logic as the 16-byte case, but we don't need to worry about the pairwise add step. + // We'll treat the low 32 bits of the 'result' vector as its own scalar element. + + Vector128 packed = Vector128.CreateScalarUnsafe(Unsafe.ReadUnaligned(pData + i)).AsByte(); + var allowedCodePointsShuffled = AdvSimd.Arm64.VectorTableLookup(allowedCodePoints, AdvSimd.And(packed, vec0xF)); + var vecPowersOfTwoShuffled = AdvSimd.Arm64.VectorTableLookup(vecPowersOfTwo, AdvSimd.ShiftRightArithmetic(packed.AsSByte(), 4).AsByte()); + var result = AdvSimd.CompareTest(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + resultScalar = result.AsUInt32().ToScalar(); // n.b. implicit conversion uint -> ulong; high 32 bits will be zeroed + + if (resultScalar != uint.MaxValue) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 4; + } + + // Beyond this point, vectorization isn't worthwhile. Just do a normal loop. + + if ((lengthInBytes & 3) != 0) + { + Debug.Assert(lengthInBytes - i <= 3); + + do + { + if (!_allowedAsciiCodePoints.IsAllowedAsciiCodePoint(pData[i])) { break; } + } while (++i != lengthInBytes); + } + + Return: + + return i; + + PairwiseAddMaskContainsDataWhichRequiresEscaping: + + Debug.Assert(resultScalar != ulong.MaxValue); + // Each nibble is 4 (1 << 2) bits, so we shr by 2 to account for per-nibble stride. + i += (uint)BitOperations.TrailingZeroCount(~resultScalar) >> 2; // location of lowest set bit is where we must begin escaping + goto Return; + + MaskContainsDataWhichRequiresEscaping: + + Debug.Assert(resultScalar != ulong.MaxValue); + // Each byte is 8 (1 << 3) bits, so we shr by 3 to account for per-byte stride. + i += (uint)BitOperations.TrailingZeroCount(~resultScalar) >> 3; // location of lowest set bit is where we must begin escaping + goto Return; + } + + private unsafe nuint GetIndexOfFirstCharToEncodeAdvSimd64(char* pData, nuint lengthInChars) + { + // See GetIndexOfFirstByteToEncodeAdvSimd64 for the central logic behind this method. + // The main difference here is that we need to pack WORDs to BYTEs before performing + // the main vectorized logic. It doesn't matter if we use signed or unsigned saturation + // while packing, as saturation will convert out-of-range (non-ASCII char) WORDs to + // 0x00 or 0x7F..0xFF, all of which are forbidden by the encoder. + + Debug.Assert(AdvSimd.Arm64.IsSupported); + Debug.Assert(BitConverter.IsLittleEndian); + + Vector128 vec0xF = Vector128.Create((byte)0xF); + Vector128 vecPowersOfTwo = Vector128.Create(1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0); + Vector128 vecPairwiseAddNibbleBitmask = Vector128.Create((ushort)0xF00F).AsByte(); // little endian only + Vector128 allowedCodePoints = _allowedAsciiCodePoints.AsVector; + ulong resultScalar; + + nuint i = 0; + if (lengthInChars >= 16) + { + nuint lastLegalIterationFor16CharRead = lengthInChars & unchecked((nuint)(nint)~0xF); + + do + { + // Read 16 chars at a time into 2x 128-bit vectors, then pack into a single 128-bit vector. + // We turn 16 chars (256 bits) into 16 nibbles (64 bits) during this process. + + Vector128 packed = AdvSimd.ExtractNarrowingSaturateUnsignedUpper( + AdvSimd.ExtractNarrowingSaturateUnsignedLower(AdvSimd.LoadVector128((/* unaligned */ short*)(pData + i))), + AdvSimd.LoadVector128((/* unaligned */ short*)(pData + 8 + i))); + var allowedCodePointsShuffled = AdvSimd.Arm64.VectorTableLookup(allowedCodePoints, AdvSimd.And(packed, vec0xF)); + var vecPowersOfTwoShuffled = AdvSimd.Arm64.VectorTableLookup(vecPowersOfTwo, AdvSimd.ShiftRightArithmetic(packed.AsSByte(), 4).AsByte()); + var result = AdvSimd.CompareTest(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + var maskedResult = AdvSimd.And(result, vecPairwiseAddNibbleBitmask); + resultScalar = AdvSimd.Arm64.AddPairwise(maskedResult, maskedResult).AsUInt64().ToScalar(); + + if (resultScalar != ulong.MaxValue) + { + goto PairwiseAddMaskContainsDataWhichRequiresEscaping; + } + } while ((i += 16) < lastLegalIterationFor16CharRead); + } + + if ((lengthInChars & 8) != 0) + { + // Read 8 chars at a time into a single 128-bit vector, then pack into a 64-bit + // vector, then extend to 128 bits. We turn 8 chars (128 bits) into 8 bytes (64 bits) + // during this process. Only the low 64 bits of the 'result' vector have meaningful + // data. + + Vector128 packed = AdvSimd.ExtractNarrowingSaturateUnsignedLower(AdvSimd.LoadVector128((/* unaligned */ short*)(pData + i))).AsByte().ToVector128Unsafe(); + var allowedCodePointsShuffled = AdvSimd.Arm64.VectorTableLookup(allowedCodePoints, AdvSimd.And(packed, vec0xF)); + var vecPowersOfTwoShuffled = AdvSimd.Arm64.VectorTableLookup(vecPowersOfTwo, AdvSimd.ShiftRightArithmetic(packed.AsSByte(), 4).AsByte()); + var result = AdvSimd.CompareTest(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + resultScalar = result.AsUInt64().ToScalar(); + + if (resultScalar != ulong.MaxValue) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 8; + } + + if ((lengthInChars & 4) != 0) + { + // Read 4 chars at a time into a single 64-bit vector, then pack into the low 32 bits + // of a 128-bit vector. We turn 4 chars (64 bits) into 4 bytes (32 bits) during this + // process. Only the low 32 bits of the 'result' vector have meaningful data. + + Vector128 packed = AdvSimd.ExtractNarrowingSaturateUnsignedLower(AdvSimd.LoadVector64((/* unaligned */ short*)(pData + i)).ToVector128Unsafe()).ToVector128Unsafe(); + var allowedCodePointsShuffled = AdvSimd.Arm64.VectorTableLookup(allowedCodePoints, AdvSimd.And(packed, vec0xF)); + var vecPowersOfTwoShuffled = AdvSimd.Arm64.VectorTableLookup(vecPowersOfTwo, AdvSimd.ShiftRightArithmetic(packed.AsSByte(), 4).AsByte()); + var result = AdvSimd.CompareTest(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + resultScalar = result.AsUInt32().ToScalar(); // n.b. implicit conversion uint -> ulong; high 32 bits will be zeroed + + if (resultScalar != uint.MaxValue) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 4; + } + + // Beyond this point, vectorization isn't worthwhile. Just do a normal loop. + + if ((lengthInChars & 3) != 0) + { + Debug.Assert(lengthInChars - i <= 3); + + do + { + if (!_allowedAsciiCodePoints.IsAllowedAsciiCodePoint(pData[i])) { break; } + } while (++i != lengthInChars); + } + + Return: + + return i; + + PairwiseAddMaskContainsDataWhichRequiresEscaping: + + Debug.Assert(resultScalar != ulong.MaxValue); + // Each nibble is 4 (1 << 2) bits, so we shr by 2 to account for per-nibble stride. + i += (uint)BitOperations.TrailingZeroCount(~resultScalar) >> 2; // location of lowest set bit is where we must begin escaping + goto Return; + + MaskContainsDataWhichRequiresEscaping: + + Debug.Assert(resultScalar != ulong.MaxValue); + // Each byte is 8 (1 << 3) bits, so we shr by 3 to account for per-byte stride. + i += (uint)BitOperations.TrailingZeroCount(~resultScalar) >> 3; // location of lowest set bit is where we must begin escaping + goto Return; + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.Ascii.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.Ascii.cs new file mode 100644 index 0000000000000..2c3ff234b9247 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.Ascii.cs @@ -0,0 +1,135 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if NETCOREAPP +using System.Runtime.Intrinsics; +#endif + +namespace System.Text.Encodings.Web +{ + internal sealed partial class OptimizedInboxTextEncoder + { + /// + /// A bitmap which represents allowed ASCII code points. + /// + [StructLayout(LayoutKind.Explicit)] + private unsafe partial struct AllowedAsciiCodePoints + { + [FieldOffset(0)] // ensure same offset with AsVector field + private fixed byte AsBytes[16]; + +#if NETCOREAPP +#if !TARGET_BROWSER + [FieldOffset(0)] // ensure same offset with AsBytes field + internal Vector128 AsVector; +#else + // This member shouldn't be accessed from browser-based code paths. + // All call sites should be trimmed away, which will also trim this member + // and the type hierarchy it links to. + internal Vector128 AsVector => throw new PlatformNotSupportedException(); +#endif +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly bool IsAllowedAsciiCodePoint(uint codePoint) + { + if (codePoint > 0x7F) + { + return false; // non-ASCII + } + + uint mask = AsBytes[codePoint & 0xF]; + if ((mask & (0x1u << (int)(codePoint >> 4))) == 0) + { + return false; // ASCII but disallowed + } + + return true; + } + + internal void PopulateAllowedCodePoints(in AllowedBmpCodePointsBitmap allowedBmpCodePoints) + { + this = default; // clear all existing data + + // we only care about ASCII non-control chars; all control chars and non-ASCII chars are disallowed + for (int i = 0x20; i < 0x7F; i++) + { + if (allowedBmpCodePoints.IsCharAllowed((char)i)) + { + AsBytes[i & 0xF] |= (byte)(1 << (i >> 4)); + } + } + } + } + + /// + /// A bitmap which represents the 64-bit pre-escaped form of the ASCII code points. + /// A pre-escaped code point has the form [ WW 00 FF EE DD CC BB AA ], + /// where AA - FF are the six-ASCII-byte escaped representation of the + /// code point, zero-padded at the end. The high byte of the pre-escaped form + /// is the number of non-zero bytes which make up the pre-escaped data. + /// + /// Example: If the escaped form of "@" is "%40", the pre-escaped form will be: + /// 0x30_00_00_00_00_30_34_25. Iterate over the least significant bytes one-by-one + /// to reconstruct the escaped representation, stopping when you hit a null byte. + /// + private unsafe struct AsciiPreescapedData + { + private fixed ulong Data[128]; + + internal void PopulatePreescapedData(in AllowedBmpCodePointsBitmap allowedCodePointsBmp, ScalarEscaperBase innerEncoder) + { + this = default; // clear all existing data + + Span tempBuffer = stackalloc char[8] { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; + for (int i = 0; i < 128; i++) + { + ulong thisPreescapedData; + int encodedCharCount; + + Rune rune = new Rune(i); // guaranteed to succeed + if (!Rune.IsControl(rune) && allowedCodePointsBmp.IsCharAllowed((char)i)) + { + thisPreescapedData = (uint)i; // char maps to itself + encodedCharCount = 1; + } + else + { + encodedCharCount = innerEncoder.EncodeUtf16(rune, tempBuffer.Slice(0, 6)); + Debug.Assert(encodedCharCount > 0 && encodedCharCount <= 6, "Inner encoder returned bad length."); + + thisPreescapedData = 0; + tempBuffer.Slice(encodedCharCount).Clear(); + for (int j = encodedCharCount - 1; j >= 0; j--) + { + uint thisChar = tempBuffer[j]; + Debug.Assert(thisChar <= 0x7F, "Inner encoder returned non-ASCII data."); + thisPreescapedData = (thisPreescapedData << 8) | thisChar; + } + } + + Data[i] = thisPreescapedData | ((ulong)(uint)encodedCharCount << 56); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly bool TryGetPreescapedData(uint codePoint, out ulong preescapedData) + { + if (codePoint <= 0x7F) + { + preescapedData = Data[codePoint]; + return true; + } + else + { + preescapedData = default; + return false; + } + } + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.Ssse3.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.Ssse3.cs new file mode 100644 index 0000000000000..012e1ae169ac2 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.Ssse3.cs @@ -0,0 +1,229 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Numerics; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace System.Text.Encodings.Web +{ + internal sealed partial class OptimizedInboxTextEncoder + { + private unsafe nuint GetIndexOfFirstByteToEncodeSsse3(byte* pData, nuint lengthInBytes) + { + Debug.Assert(Ssse3.IsSupported); + Debug.Assert(BitConverter.IsLittleEndian); + + Vector128 vecZero = Vector128.Zero; + Vector128 vec0x7 = Vector128.Create((byte)0x7); + Vector128 vecPowersOfTwo = Vector128.Create(1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0); + Vector128 allowedCodePoints = _allowedAsciiCodePoints.AsVector; + int pmovmskb; + + nuint i = 0; + if (lengthInBytes >= 16) + { + nuint lastLegalIterationFor16CharRead = lengthInBytes & unchecked((nuint)(nint)~0xF); + + do + { + // Read 16 bytes at a time into a single 128-bit vector. + + Vector128 packed = Sse2.LoadVector128(pData + i); // unaligned read + + // Each element of the packed vector corresponds to a byte of untrusted source data. It will + // have the format [ ..., 0xYZ, ... ]. We use the low nibble of each byte to index into + // the 'allowedCodePoints' vector, and we use the high nibble of each byte to select a bit + // from the corresponding element in the 'allowedCodePoints' vector. + // + // Example: let packed := [ ..., 0x6D ('m'), ... ] + // The final 'result' vector will contain a non-zero value in the corresponding space iff the + // 0xD element in the 'allowedCodePoints' vector has its 1 << 0x6 bit set. + // + // We rely on the fact that the pshufb operation will turn each non-ASCII byte (high bit set) + // into 0x00 in the resulting 'shuffled' vector. That results in the corresponding element + // in the 'result' vector also being 0x00, meaning that escaping is required. + + var allowedCodePointsShuffled = Ssse3.Shuffle(allowedCodePoints, packed); + var vecPowersOfTwoShuffled = Ssse3.Shuffle(vecPowersOfTwo, Sse2.And(Sse2.ShiftRightLogical(packed.AsUInt32(), 4).AsByte(), vec0x7)); + var result = Sse2.And(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + + // Now, each element of 'result' contains a non-zero value if the corresponding element in + // 'packed' is allowed; and it contains a zero value if the corresponding element in 'packed' + // is disallowed. We'll compare 'result' against an all-zero vector to normalize 0x00 -> 0xFF + // and (anything other than 0x00) -> 0x00. Then 'pmovmskb' will have its nth bit set iff + // the nth entry in 'packed' requires escaping. An all-zero pmovmskb means no escaping is required. + + pmovmskb = Sse2.MoveMask(Sse2.CompareEqual(result, vecZero)); + if ((pmovmskb & 0xFFFF) != 0) + { + goto MaskContainsDataWhichRequiresEscaping; + } + } while ((i += 16) < lastLegalIterationFor16CharRead); + } + + if ((lengthInBytes & 8) != 0) + { + // Read 8 bytes at a time into a single 128-bit vector. + // Same logic as the 16-byte case, but we only care about the low byte of the final pmovmskb value. + // Everything except the low byte of pmovksmb contains garbage and must be discarded. + + var packed = Sse2.LoadScalarVector128((/* unaligned */ ulong*)(pData + i)).AsByte(); + var allowedCodePointsShuffled = Ssse3.Shuffle(allowedCodePoints, packed); + var vecPowersOfTwoShuffled = Ssse3.Shuffle(vecPowersOfTwo, Sse2.And(Sse2.ShiftRightLogical(packed.AsUInt32(), 4).AsByte(), vec0x7)); + var result = Sse2.And(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + pmovmskb = Sse2.MoveMask(Sse2.CompareEqual(result, vecZero)); + if ((byte)pmovmskb != 0) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 8; + } + + if ((lengthInBytes & 4) != 0) + { + // Read 4 bytes at a time into a single 128-bit vector. + // Same logic as the 16-byte case, but we only care about the low nibble of the final pmovmskb value. + // Everything except the low nibble of pmovksmb contains garbage and must be discarded. + + var packed = Sse2.LoadScalarVector128((/* unaligned */ uint*)(pData + i)).AsByte(); + var allowedCodePointsShuffled = Ssse3.Shuffle(allowedCodePoints, packed); + var vecPowersOfTwoShuffled = Ssse3.Shuffle(vecPowersOfTwo, Sse2.And(Sse2.ShiftRightLogical(packed.AsUInt32(), 4).AsByte(), vec0x7)); + var result = Sse2.And(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + pmovmskb = Sse2.MoveMask(Sse2.CompareEqual(result, vecZero)); + if ((pmovmskb & 0xF) != 0) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 4; + } + + // Beyond this point, vectorization isn't worthwhile. Just do a normal loop. + + if ((lengthInBytes & 3) != 0) + { + Debug.Assert(lengthInBytes - i <= 3); + + do + { + if (!_allowedAsciiCodePoints.IsAllowedAsciiCodePoint(pData[i])) { break; } + } while (++i != lengthInBytes); + } + + Return: + + return i; + + MaskContainsDataWhichRequiresEscaping: + + Debug.Assert(pmovmskb != 0); + i += (uint)BitOperations.TrailingZeroCount(pmovmskb); // location of lowest set bit is where we must begin escaping + goto Return; + } + + private unsafe nuint GetIndexOfFirstCharToEncodeSsse3(char* pData, nuint lengthInChars) + { + // See GetIndexOfFirstByteToEncodeSsse3 for the central logic behind this method. + // The main difference here is that we need to pack WORDs to BYTEs before performing + // the main vectorized logic. It doesn't matter if we use signed or unsigned saturation + // while packing, as saturation will convert out-of-range (non-ASCII char) WORDs to + // 0x00 or 0x7F..0xFF, all of which are forbidden by the encoder. + + Debug.Assert(Ssse3.IsSupported); + Debug.Assert(BitConverter.IsLittleEndian); + + Vector128 vecZero = Vector128.Zero; + Vector128 vec0x7 = Vector128.Create((byte)0x7); + Vector128 vecPowersOfTwo = Vector128.Create(1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0, 0, 0, 0, 0); + Vector128 allowedCodePoints = _allowedAsciiCodePoints.AsVector; + int pmovmskb; + + nuint i = 0; + if (lengthInChars >= 16) + { + nuint lastLegalIterationFor16CharRead = lengthInChars & unchecked((nuint)(nint)~0xF); + + do + { + // Read 16 chars at a time into 2x 128-bit vectors, then pack into a single 128-bit vector. + + var packed = Sse2.PackUnsignedSaturate( + Sse2.LoadVector128((/* unaligned */ short*)(pData + i)), + Sse2.LoadVector128((/* unaligned */ short*)(pData + 8 + i))); + var allowedCodePointsShuffled = Ssse3.Shuffle(allowedCodePoints, packed); + var vecPowersOfTwoShuffled = Ssse3.Shuffle(vecPowersOfTwo, Sse2.And(Sse2.ShiftRightLogical(packed.AsUInt32(), 4).AsByte(), vec0x7)); + var result = Sse2.And(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + pmovmskb = Sse2.MoveMask(Sse2.CompareEqual(result, vecZero)); + if ((pmovmskb & 0xFFFF) != 0) + { + goto MaskContainsDataWhichRequiresEscaping; + } + } while ((i += 16) < lastLegalIterationFor16CharRead); + } + + if ((lengthInChars & 8) != 0) + { + // Read 8 chars at a time into a single 128-bit vector, then pack into low 8 bytes. + + var packed = Sse2.PackUnsignedSaturate( + Sse2.LoadVector128((/* unaligned */ short*)(pData + i)), + vecZero.AsInt16()); + var allowedCodePointsShuffled = Ssse3.Shuffle(allowedCodePoints, packed); + var vecPowersOfTwoShuffled = Ssse3.Shuffle(vecPowersOfTwo, Sse2.And(Sse2.ShiftRightLogical(packed.AsUInt32(), 4).AsByte(), vec0x7)); + var result = Sse2.And(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + pmovmskb = Sse2.MoveMask(Sse2.CompareEqual(result, vecZero)); + if ((byte)pmovmskb != 0) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 8; + } + + if ((lengthInChars & 4) != 0) + { + // Read 4 chars at a time into a single 128-bit vector, then pack into low 4 bytes. + // Everything except the low nibble of pmovksmb contains garbage and must be discarded. + + var packed = Sse2.PackUnsignedSaturate( + Sse2.LoadScalarVector128((/* unaligned */ ulong*)(pData + i)).AsInt16(), + vecZero.AsInt16()); + var allowedCodePointsShuffled = Ssse3.Shuffle(allowedCodePoints, packed); + var vecPowersOfTwoShuffled = Ssse3.Shuffle(vecPowersOfTwo, Sse2.And(Sse2.ShiftRightLogical(packed.AsUInt32(), 4).AsByte(), vec0x7)); + var result = Sse2.And(allowedCodePointsShuffled, vecPowersOfTwoShuffled); + pmovmskb = Sse2.MoveMask(Sse2.CompareEqual(result, vecZero)); + if ((pmovmskb & 0xF) != 0) + { + goto MaskContainsDataWhichRequiresEscaping; + } + + i += 4; + } + + // Beyond this point, vectorization isn't worthwhile. Just do a normal loop. + + if ((lengthInChars & 3) != 0) + { + Debug.Assert(lengthInChars - i <= 3); + + do + { + if (!_allowedAsciiCodePoints.IsAllowedAsciiCodePoint(pData[i])) { break; } + } while (++i != lengthInChars); + } + + Return: + + return i; + + MaskContainsDataWhichRequiresEscaping: + + Debug.Assert(pmovmskb != 0); + i += (uint)BitOperations.TrailingZeroCount(pmovmskb); // location of lowest set bit is where we must begin escaping + goto Return; + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.cs new file mode 100644 index 0000000000000..8fc8213376d0f --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/OptimizedInboxTextEncoder.cs @@ -0,0 +1,500 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +#if NETCOREAPP +using System.Runtime.Intrinsics.X86; +#endif + +#if NET5_0_OR_GREATER +using System.Runtime.Intrinsics.Arm; +#endif + +namespace System.Text.Encodings.Web +{ + /// + /// Allows efficient escaping for the library's built-in types (HTML, URL, JS). + /// Assumes the following: + /// (a) All C0 and C1 code points are disallowed. + /// (b) Escaping 1 ASCII input character results in no more than 6 output characters. + /// (c) All Unicode scalar values may be represented in escaped form. + /// (d) The escaped form of any Unicode scalar value consists of only ASCII characters. + /// + internal sealed partial class OptimizedInboxTextEncoder + { + private readonly AllowedAsciiCodePoints _allowedAsciiCodePoints; + private readonly AsciiPreescapedData _asciiPreescapedData; + private readonly AllowedBmpCodePointsBitmap _allowedBmpCodePoints; + private readonly ScalarEscaperBase _scalarEscaper; + + internal OptimizedInboxTextEncoder( + ScalarEscaperBase scalarEscaper, + in AllowedBmpCodePointsBitmap allowedCodePointsBmp, + bool forbidHtmlSensitiveCharacters = true, + ReadOnlySpan extraCharactersToEscape = default) + { + Debug.Assert(scalarEscaper != null); + + _scalarEscaper = scalarEscaper; + _allowedBmpCodePoints = allowedCodePointsBmp; + +#if DEBUG && !NETCOREAPP3_1 + // Debug-only assertion to validate that we're no longer using the input + // argument once the field value has been assigned. All accesses to the bitmap + // should now go through our instance field. In debug mode, if any code violates + // this, it'll cause a null ref within this ctor. + allowedCodePointsBmp = ref Unsafe.NullRef(); +#endif + + // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed + // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp). + _allowedBmpCodePoints.ForbidUndefinedCharacters(); + + // Most encoders should forbid characters that are special in HTML, even if they're not + // HTML encoders themselves. This is defense-in-depth for scenarios where somebody encodes + // a JavaScript string or a URL, then places it straight into an HTML document without + // accounting for any required outer envelope (HTML) escaping. + if (forbidHtmlSensitiveCharacters) + { + _allowedBmpCodePoints.ForbidHtmlCharacters(); + } + + foreach (char ch in extraCharactersToEscape) + { + _allowedBmpCodePoints.ForbidChar(ch); + } + + // Now that disallowed characters have been filtered out, we're free to populate + // the ASCII maps and pre-escaped data caches. + _asciiPreescapedData.PopulatePreescapedData(_allowedBmpCodePoints, scalarEscaper); + _allowedAsciiCodePoints.PopulateAllowedCodePoints(_allowedBmpCodePoints); + } + + [Obsolete("Greenfield code shouldn't call this. It should only be used by the TextEncoder adapter.")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe int FindFirstCharacterToEncode(char* text, int textLength) + { + return GetIndexOfFirstCharToEncode(new ReadOnlySpan(text, textLength)); // performs bounds checking + } + + [Obsolete("Greenfield code shouldn't call this. It should only be used by the TextEncoder adapter.")] + public unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) + { + Span destination = new Span(buffer, bufferLength); + + if (_allowedBmpCodePoints.IsCodePointAllowed((uint)unicodeScalar)) + { + // The bitmap should only allow BMP non-surrogate code points. + UnicodeDebug.AssertIsBmpCodePoint((uint)unicodeScalar); + UnicodeDebug.AssertIsValidScalar((uint)unicodeScalar); + if (!destination.IsEmpty) + { + destination[0] = (char)unicodeScalar; // reflect as-is + numberOfCharactersWritten = 1; + return true; + } + } + else + { + int innerCharsWritten = _scalarEscaper.EncodeUtf16(new Rune(unicodeScalar), destination); + Debug.Assert(innerCharsWritten <= bufferLength, "Mustn't overflow the buffer."); + Debug.Assert(innerCharsWritten != 0, "Inner escaper succeeded with 0-char output?"); + if (innerCharsWritten >= 0) + { + numberOfCharactersWritten = innerCharsWritten; + return true; + } + } + + // If we reached this point, we ran out of space in the destination. + numberOfCharactersWritten = 0; + return false; + } + + public OperationStatus Encode(ReadOnlySpan source, Span destination, out int charsConsumed, out int charsWritten, bool isFinalBlock) + { + _AssertThisNotNull(); // hoist "this != null" check out of hot loop below + + int srcIdx = 0; + int dstIdx = 0; + + while (true) + { + if (!SpanUtility.IsValidIndex(source, srcIdx)) + { + break; // EOF + } + + char thisChar = source[srcIdx]; + if (!_asciiPreescapedData.TryGetPreescapedData(thisChar, out ulong preescapedEntry)) + { + goto NotAscii; // forward jump predicted not taken + } + + if (!SpanUtility.IsValidIndex(destination, dstIdx)) + { + goto DestTooSmall; // forward jump predicted not taken + } + + destination[dstIdx] = (char)(byte)preescapedEntry; + if (((uint)preescapedEntry & 0xFF00) == 0) + { + dstIdx++; // predicted taken - only had to write a single char + srcIdx++; + continue; + } + + // At this point, we're writing a multi-char output for a single-char input. + // Copy over as many chars as we can. + + preescapedEntry >>= 8; + int dstIdxTemp = dstIdx + 1; + do + { + if (!SpanUtility.IsValidIndex(destination, dstIdxTemp)) + { + goto DestTooSmall; // forward jump predicted not taken + } + + destination[dstIdxTemp++] = (char)(byte)preescapedEntry; + } while ((byte)(preescapedEntry >>= 8) != 0); + + dstIdx = dstIdxTemp; + srcIdx++; + continue; + + NotAscii: + + if (!Rune.TryCreate(thisChar, out Rune scalarValue)) + { + int srcIdxTemp = srcIdx + 1; + if (SpanUtility.IsValidIndex(source, srcIdxTemp)) + { + if (Rune.TryCreate(thisChar, source[srcIdxTemp], out scalarValue)) + { + goto CheckWhetherScalarValueAllowed; // successfully extracted scalar value + } + } + else if (!isFinalBlock && char.IsHighSurrogate(thisChar)) + { + goto NeedMoreData; // ended with a high surrogate, and caller said they'd provide more data + } + + scalarValue = Rune.ReplacementChar; // fallback char + goto MustEncodeNonAscii; + } + + CheckWhetherScalarValueAllowed: + + if (IsScalarValueAllowed(scalarValue)) + { + if (!scalarValue.TryEncodeToUtf16(destination.Slice(dstIdx), out int utf16CodeUnitCount)) + { + goto DestTooSmall; + } + + dstIdx += utf16CodeUnitCount; + srcIdx += utf16CodeUnitCount; + continue; + } + + MustEncodeNonAscii: + + // At this point, we know we need to encode. + + int charsWrittenJustNow = _scalarEscaper.EncodeUtf16(scalarValue, destination.Slice(dstIdx)); + if (charsWrittenJustNow < 0) + { + goto DestTooSmall; + } + + dstIdx += charsWrittenJustNow; + srcIdx += scalarValue.Utf16SequenceLength; + } + + // And at this point, we're done! + + OperationStatus retVal = OperationStatus.Done; + + CommonReturn: + charsConsumed = srcIdx; + charsWritten = dstIdx; + return retVal; + + DestTooSmall: + retVal = OperationStatus.DestinationTooSmall; + goto CommonReturn; + + NeedMoreData: + retVal = OperationStatus.NeedMoreData; + goto CommonReturn; + } + + public OperationStatus EncodeUtf8(ReadOnlySpan source, Span destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) + { + _AssertThisNotNull(); // hoist "this != null" check out of hot loop below + + int srcIdx = 0; + int dstIdx = 0; + + while (true) + { + if (!SpanUtility.IsValidIndex(source, srcIdx)) + { + break; // EOF + } + + uint thisByte = source[srcIdx]; + if (!_asciiPreescapedData.TryGetPreescapedData(thisByte, out ulong preescapedEntry)) + { + goto NotAscii; // forward jump predicted not taken + } + + // The common case is that the destination is large enough to hold 8 bytes of output, + // so let's write the entire pre-escaped entry to it. In reality we're only writing up + // to 6 bytes of output, so we'll only bump dstIdx by the number of useful bytes we + // wrote. + + if (SpanUtility.TryWriteUInt64LittleEndian(destination, dstIdx, preescapedEntry)) + { + dstIdx += (int)(preescapedEntry >> 56); // predicted taken + srcIdx++; + continue; + } + + // We don't have enough space to hold a single QWORD copy, so let's write byte-by-byte + // and see if we have enough room. + + int dstIdxTemp = dstIdx; + do + { + if (!SpanUtility.IsValidIndex(destination, dstIdxTemp)) + { + goto DestTooSmall; // forward jump predicted not taken + } + + destination[dstIdxTemp++] = (byte)preescapedEntry; + } while ((byte)(preescapedEntry >>= 8) != 0); + + dstIdx = dstIdxTemp; + srcIdx++; + continue; + + NotAscii: + + OperationStatus runeDecodeStatus = Rune.DecodeFromUtf8(source.Slice(srcIdx), out Rune scalarValue, out int bytesConsumedJustNow); + if (runeDecodeStatus != OperationStatus.Done) + { + if (!isFinalBlock && runeDecodeStatus == OperationStatus.NeedMoreData) + { + goto NeedMoreData; // source ends in the middle of a multi-byte sequence + } + + Debug.Assert(scalarValue == Rune.ReplacementChar); // DecodeFromUtfXX should've set replacement character on failure + goto MustEncodeNonAscii; // bad UTF-8 data seen + } + + if (IsScalarValueAllowed(scalarValue)) + { + if (!scalarValue.TryEncodeToUtf8(destination.Slice(dstIdx), out int utf8CodeUnitCount)) + { + goto DestTooSmall; + } + dstIdx += utf8CodeUnitCount; + srcIdx += utf8CodeUnitCount; + continue; + } + + MustEncodeNonAscii: + + // At this point, we know we need to encode. + + int bytesWrittenJustNow = _scalarEscaper.EncodeUtf8(scalarValue, destination.Slice(dstIdx)); + if (bytesWrittenJustNow < 0) + { + goto DestTooSmall; + } + + dstIdx += bytesWrittenJustNow; + srcIdx += bytesConsumedJustNow; + } + + // And at this point, we're done! + + OperationStatus retVal = OperationStatus.Done; + + CommonReturn: + bytesConsumed = srcIdx; + bytesWritten = dstIdx; + return retVal; + + DestTooSmall: + retVal = OperationStatus.DestinationTooSmall; + goto CommonReturn; + + NeedMoreData: + retVal = OperationStatus.NeedMoreData; + goto CommonReturn; + } + + public int GetIndexOfFirstByteToEncode(ReadOnlySpan data) + { + // First, try calling the SIMD-enabled version. + // The SIMD-enabled version handles only ASCII characters. + + int dataOriginalLength = data.Length; + +#if NETCOREAPP + if (Ssse3.IsSupported +#if NET5_0_OR_GREATER + || (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian) +#endif + ) + { + int asciiBytesSkipped; + unsafe + { + fixed (byte* pData = data) + { + nuint asciiBytesSkippedNInt; +#if NET5_0_OR_GREATER + if (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian) + { + asciiBytesSkippedNInt = GetIndexOfFirstByteToEncodeAdvSimd64(pData, (uint)dataOriginalLength); + } + else +#endif + { + Debug.Assert(Ssse3.IsSupported, "#ifdef was ill-formed."); + asciiBytesSkippedNInt = GetIndexOfFirstByteToEncodeSsse3(pData, (uint)dataOriginalLength); + } + Debug.Assert(0 <= asciiBytesSkippedNInt && asciiBytesSkippedNInt <= (uint)dataOriginalLength); + asciiBytesSkipped = (int)asciiBytesSkippedNInt; + } + } + + if (!SpanUtility.IsValidIndex(data, asciiBytesSkipped)) + { + Debug.Assert(asciiBytesSkipped == data.Length); + return -1; // all data consumed + } + + // Quick check: We know some data remains in the buffer. If the first byte is an ASCII + // byte, that means it already failed the vectorized logic, and there's no need to run + // down the slower "decode scalar-by-scalar" code path. In that case we'll exit now. + + if (UnicodeUtility.IsAsciiCodePoint(data[asciiBytesSkipped])) + { + return asciiBytesSkipped; + } + + data = data.Slice((int)asciiBytesSkipped); + Debug.Assert(!data.IsEmpty); + } +#endif + + // If there's any leftover data, try consuming it now. + + while (!data.IsEmpty) + { + OperationStatus opStatus = Rune.DecodeFromUtf8(data, out Rune scalarValue, out int bytesConsumed); + if (opStatus != OperationStatus.Done) { break; } // bad data found, must escape + if (bytesConsumed >= 4) { break; } // found supplementary code point, must escape + + UnicodeDebug.AssertIsBmpCodePoint((uint)scalarValue.Value); + if (!_allowedBmpCodePoints.IsCharAllowed((char)scalarValue.Value)) { break; } // disallowed code point + data = data.Slice(bytesConsumed); + } + + return (data.IsEmpty) ? -1 : dataOriginalLength - data.Length; + } + + public unsafe int GetIndexOfFirstCharToEncode(ReadOnlySpan data) + { + fixed (char* pData = data) + { + nuint lengthInChars = (uint)data.Length; + + // First, try calling the SIMD-enabled version. + // The SIMD-enabled version handles only ASCII characters. + + nuint idx = 0; +#if NETCOREAPP + if (Ssse3.IsSupported) + { + idx = GetIndexOfFirstCharToEncodeSsse3(pData, lengthInChars); + } +#if NET5_0_OR_GREATER + else if (AdvSimd.Arm64.IsSupported && BitConverter.IsLittleEndian) + { + idx = GetIndexOfFirstCharToEncodeAdvSimd64(pData, lengthInChars); + } +#endif + Debug.Assert(0 <= idx && idx <= lengthInChars); +#endif + + // If there's any leftover data, try consuming it now. + + if (idx < lengthInChars) + { + _AssertThisNotNull(); // hoist "this != null" check out of hot loop below + + // unroll the loop 8x + nint loopIter = 0; + for (; lengthInChars - idx >= 8; idx += 8) + { + loopIter = -1; + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx + (nuint)(++loopIter)])) { goto BrokeInUnrolledLoop; } + } + + for (; idx < lengthInChars; idx++) + { + if (!_allowedBmpCodePoints.IsCharAllowed(pData[idx])) { break; } + } + + goto Return; + + BrokeInUnrolledLoop: + idx += (nuint)loopIter; + } + + Return: + + Debug.Assert(0 <= idx && idx <= lengthInChars); + int idx32 = (int)idx; + if (idx32 == (int)lengthInChars) + { + idx32 = -1; + } + return idx32; + } + } + + /// + /// Given a scalar value, returns a value stating whether that value is present + /// in this encoder's allow list. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsScalarValueAllowed(Rune value) + { + return _allowedBmpCodePoints.IsCodePointAllowed((uint)value.Value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void _AssertThisNotNull() + { + // Used for hoisting "'this' is not null" assertions outside hot loops. + if (GetType() == typeof(OptimizedInboxTextEncoder)) { /* intentionally left blank */ } + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/ScalarEscaperBase.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/ScalarEscaperBase.cs new file mode 100644 index 0000000000000..24eae1446f2f8 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/ScalarEscaperBase.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Text.Encodings.Web +{ + /// + /// A class that can escape a scalar value and write either UTF-16 or UTF-8 format. + /// + internal abstract class ScalarEscaperBase + { + internal abstract int EncodeUtf16(Rune value, Span destination); + internal abstract int EncodeUtf8(Rune value, Span destination); + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/SpanUtility.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/SpanUtility.cs new file mode 100644 index 0000000000000..62dedbea3572c --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/SpanUtility.cs @@ -0,0 +1,259 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Text.Encodings.Web +{ + /// + /// Contains helpers for manipulating spans so that we can keep unsafe code out of the common path. + /// + internal static class SpanUtility + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsValidIndex(ReadOnlySpan span, int index) + { + return ((uint)index < (uint)span.Length) ? true : false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsValidIndex(Span span, int index) + { + return ((uint)index < (uint)span.Length) ? true : false; + } + + /// + /// Tries writing four bytes to the span. If success, returns true. If the span is not large + /// enough to hold four bytes, leaves the span unchanged and returns false. + /// + /// + /// Parameters are intended to be constant values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteBytes(Span span, byte a, byte b, byte c, byte d) + { + if (span.Length >= 4) + { + uint abcd32; + if (BitConverter.IsLittleEndian) + { + abcd32 = ((uint)d << 24) | ((uint)c << 16) | ((uint)b << 8) | a; + } + else + { + abcd32 = ((uint)a << 24) | ((uint)b << 16) | ((uint)c << 8) | d; + } + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(span), abcd32); + return true; + } + else + { + return false; + } + } + + /// + /// Tries writing five bytes to the span. If success, returns true. If the span is not large + /// enough to hold five bytes, leaves the span unchanged and returns false. + /// + /// + /// Parameters are intended to be constant values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteBytes(Span span, byte a, byte b, byte c, byte d, byte e) + { + if (span.Length >= 5) + { + uint abcd32; + if (BitConverter.IsLittleEndian) + { + abcd32 = ((uint)d << 24) | ((uint)c << 16) | ((uint)b << 8) | a; + } + else + { + abcd32 = ((uint)a << 24) | ((uint)b << 16) | ((uint)c << 8) | d; + } + ref byte rDest = ref MemoryMarshal.GetReference(span); + Unsafe.WriteUnaligned(ref rDest, abcd32); + Unsafe.Add(ref rDest, 4) = e; + return true; + } + else + { + return false; + } + } + + /// + /// Tries writing six bytes to the span. If success, returns true. If the span is not large + /// enough to hold six bytes, leaves the span unchanged and returns false. + /// + /// + /// Parameters are intended to be constant values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteBytes(Span span, byte a, byte b, byte c, byte d, byte e, byte f) + { + if (span.Length >= 6) + { + uint abcd32; + uint ef16; + if (BitConverter.IsLittleEndian) + { + abcd32 = ((uint)d << 24) | ((uint)c << 16) | ((uint)b << 8) | a; + ef16 = ((uint)f << 8) | e; + } + else + { + abcd32 = ((uint)a << 24) | ((uint)b << 16) | ((uint)c << 8) | d; + ef16 = ((uint)e << 8) | f; + } + ref byte rDest = ref MemoryMarshal.GetReference(span); + Unsafe.WriteUnaligned(ref rDest, abcd32); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref rDest, 4), (ushort)ef16); + return true; + } + else + { + return false; + } + } + + /// + /// Tries writing four chars to the span. If success, returns true. If the span is not large + /// enough to hold four chars, leaves the span unchanged and returns false. + /// + /// + /// Parameters are intended to be constant values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteChars(Span span, char a, char b, char c, char d) + { + if (span.Length >= 4) + { + ulong abcd64; + if (BitConverter.IsLittleEndian) + { + abcd64 = ((ulong)d << 48) | ((ulong)c << 32) | ((ulong)b << 16) | a; + } + else + { + abcd64 = ((ulong)a << 48) | ((ulong)b << 32) | ((ulong)c << 16) | d; + } + Unsafe.WriteUnaligned(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), abcd64); + return true; + } + else + { + return false; + } + } + + /// + /// Tries writing five chars to the span. If success, returns true. If the span is not large + /// enough to hold five chars, leaves the span unchanged and returns false. + /// + /// + /// Parameters are intended to be constant values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteChars(Span span, char a, char b, char c, char d, char e) + { + if (span.Length >= 5) + { + ulong abcd64; + if (BitConverter.IsLittleEndian) + { + abcd64 = ((ulong)d << 48) | ((ulong)c << 32) | ((ulong)b << 16) | a; + } + else + { + abcd64 = ((ulong)a << 48) | ((ulong)b << 32) | ((ulong)c << 16) | d; + } + ref char rDest = ref MemoryMarshal.GetReference(span); + Unsafe.WriteUnaligned(ref Unsafe.As(ref rDest), abcd64); + Unsafe.Add(ref rDest, 4) = e; + return true; + } + else + { + return false; + } + } + + /// + /// Tries writing six chars to the span. If success, returns true. If the span is not large + /// enough to hold six chars, leaves the span unchanged and returns false. + /// + /// + /// Parameters are intended to be constant values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteChars(Span span, char a, char b, char c, char d, char e, char f) + { + if (span.Length >= 6) + { + ulong abcd64; + uint ef32; + if (BitConverter.IsLittleEndian) + { + abcd64 = ((ulong)d << 48) | ((ulong)c << 32) | ((ulong)b << 16) | a; + ef32 = ((uint)f << 16) | e; + } + else + { + abcd64 = ((ulong)a << 48) | ((ulong)b << 32) | ((ulong)c << 16) | d; + ef32 = ((uint)e << 16) | f; + } + ref byte rDest = ref Unsafe.As(ref MemoryMarshal.GetReference(span)); + Unsafe.WriteUnaligned(ref rDest, abcd64); + Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref rDest, (nint)sizeof(ulong)), ef32); + return true; + } + else + { + return false; + } + } + + /// + /// Tries writing a 64-bit value as little endian to the span. If success, returns true. If + /// the span is not large enough to hold 8 bytes, leaves the span unchanged and returns false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryWriteUInt64LittleEndian(Span span, int offset, ulong value) + { + if (AreValidIndexAndLength(span.Length, offset, sizeof(ulong))) + { + if (!BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Unsafe.WriteUnaligned(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), (nint)(uint)offset), value); + return true; + } + else + { + return false; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool AreValidIndexAndLength(int spanRealLength, int requestedOffset, int requestedLength) + { + // Logic here is copied from Span.Slice. + if (IntPtr.Size == 4) + { + if ((uint)requestedOffset > (uint)spanRealLength) { return false; } + if ((uint)requestedLength > (uint)(spanRealLength - requestedOffset)) { return false; } + } + else + { + if ((ulong)(uint)spanRealLength < (ulong)(uint)requestedOffset + (ulong)(uint)requestedLength) { return false; } + } + return true; + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/Sse2Helper.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/Sse2Helper.cs deleted file mode 100644 index 7d0239540ddc4..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/Sse2Helper.cs +++ /dev/null @@ -1,121 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace System.Text.Encodings.Web -{ - internal static class Sse2Helper - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(Vector128 sourceValue) - { - Debug.Assert(Sse2.IsSupported); - - // Anything in the control characters range, and anything above short.MaxValue but less than or equal char.MaxValue - // That's because anything between 32768 and 65535 (inclusive) will overflow and become negative. - Vector128 mask = Sse2.CompareLessThan(sourceValue, s_spaceMaskInt16); - - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_quotationMarkMaskInt16)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_reverseSolidusMaskInt16)); - - // Anything above the ASCII range, and also including the leftover control character in the ASCII range - 0x7F - // When this method is called with only ASCII data, 0x7F is the only value that would meet this comparison. - // However, when called from "Default", the source could contain characters outside the ASCII range. - mask = Sse2.Or(mask, Sse2.CompareGreaterThan(sourceValue, s_tildeMaskInt16)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(Vector128 sourceValue) - { - Debug.Assert(Sse2.IsSupported); - - // Anything in the control characters range (except 0x7F), and anything above sbyte.MaxValue but less than or equal byte.MaxValue - // That's because anything between 128 and 255 (inclusive) will overflow and become negative. - Vector128 mask = Sse2.CompareLessThan(sourceValue, s_spaceMaskSByte); - - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_quotationMarkMaskSByte)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_reverseSolidusMaskSByte)); - - // Leftover control character in the ASCII range - 0x7F - // Since we are dealing with sbytes, 0x7F is the only value that would meet this comparison. - mask = Sse2.Or(mask, Sse2.CompareGreaterThan(sourceValue, s_tildeMaskSByte)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin(Vector128 sourceValue) - { - Debug.Assert(Sse2.IsSupported); - - Vector128 mask = CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(sourceValue); - - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_ampersandMaskSByte)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_apostropheMaskSByte)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_plusSignMaskSByte)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_lessThanSignMaskSByte)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_greaterThanSignMaskSByte)); - mask = Sse2.Or(mask, Sse2.CompareEqual(sourceValue, s_graveAccentMaskSByte)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateAsciiMask(Vector128 sourceValue) - { - Debug.Assert(Sse2.IsSupported); - - // Anything above short.MaxValue but less than or equal char.MaxValue - // That's because anything between 32768 and 65535 (inclusive) will overflow and become negative. - Vector128 mask = Sse2.CompareLessThan(sourceValue, s_nullMaskInt16); - - // Anything above the ASCII range - mask = Sse2.Or(mask, Sse2.CompareGreaterThan(sourceValue, s_maxAsciiCharacterMaskInt16)); - - return mask; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsNonAsciiByte(Vector128 value) - { - Debug.Assert(Sse2.IsSupported); - int mask = Sse2.MoveMask(value); - return mask != 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetIndexOfFirstNonAsciiByte(Vector128 value) - { - Debug.Assert(Sse2.IsSupported); - int mask = Sse2.MoveMask(value); - int index = BitOperations.TrailingZeroCount(mask); - Debug.Assert((mask != 0) ? index < 16 : index >= 16); - return index; - } - - private static readonly Vector128 s_nullMaskInt16 = Vector128.Zero; - private static readonly Vector128 s_spaceMaskInt16 = Vector128.Create((short)' '); - private static readonly Vector128 s_quotationMarkMaskInt16 = Vector128.Create((short)'"'); - private static readonly Vector128 s_reverseSolidusMaskInt16 = Vector128.Create((short)'\\'); - private static readonly Vector128 s_tildeMaskInt16 = Vector128.Create((short)'~'); - private static readonly Vector128 s_maxAsciiCharacterMaskInt16 = Vector128.Create((short)0x7F); // Delete control character - - private static readonly Vector128 s_spaceMaskSByte = Vector128.Create((sbyte)' '); - private static readonly Vector128 s_quotationMarkMaskSByte = Vector128.Create((sbyte)'"'); - private static readonly Vector128 s_ampersandMaskSByte = Vector128.Create((sbyte)'&'); - private static readonly Vector128 s_apostropheMaskSByte = Vector128.Create((sbyte)'\''); - private static readonly Vector128 s_plusSignMaskSByte = Vector128.Create((sbyte)'+'); - private static readonly Vector128 s_lessThanSignMaskSByte = Vector128.Create((sbyte)'<'); - private static readonly Vector128 s_greaterThanSignMaskSByte = Vector128.Create((sbyte)'>'); - private static readonly Vector128 s_reverseSolidusMaskSByte = Vector128.Create((sbyte)'\\'); - private static readonly Vector128 s_graveAccentMaskSByte = Vector128.Create((sbyte)'`'); - private static readonly Vector128 s_tildeMaskSByte = Vector128.Create((sbyte)'~'); - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/Ssse3Helper.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/Ssse3Helper.cs deleted file mode 100644 index 08d4b90d2b792..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/Ssse3Helper.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace System.Text.Encodings.Web -{ - internal static class Ssse3Helper - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin(Vector128 sourceValue) - => CreateEscapingMask(sourceValue, s_bitMaskLookupBasicLatin, s_bitPosLookup, s_nibbleMaskSByte, s_nullMaskSByte); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 CreateEscapingMask( - Vector128 sourceValue, - Vector128 bitMaskLookup, - Vector128 bitPosLookup, - Vector128 nibbleMaskSByte, - Vector128 nullMaskSByte) - { - // To check if an input byte needs to be escaped or not, we use a bitmask-lookup. - // Therefore we split the input byte into the low- and high-nibble, which will get - // the row-/column-index in the bit-mask. - // The bitmask-lookup looks like (here for example s_bitMaskLookupBasicLatin): - // high-nibble - // low-nibble 0 1 2 3 4 5 6 7 8 9 A B C D E F - // 0 1 1 0 0 0 0 1 0 1 1 1 1 1 1 1 1 - // 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 2 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 3 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 4 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 5 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 6 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 7 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 8 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // 9 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // A 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // B 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 - // C 1 1 0 1 0 1 0 0 1 1 1 1 1 1 1 1 - // D 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 - // E 1 1 0 1 0 0 0 0 1 1 1 1 1 1 1 1 - // F 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 - // - // where 1 denotes the need for escaping, while 0 means no escaping needed. - // For high-nibbles in the range 8..F every input needs to be escaped, so we - // can omit them in the bit-mask, thus only high-nibbles in the range 0..7 need - // to be considered, hence the entries in the bit-mask can be of type byte. - // - // In the bitmask-lookup for each row (= low-nibble) a bit-mask for the - // high-nibbles (= columns) is created. - - Debug.Assert(Ssse3.IsSupported); - - Vector128 highNibbles = Sse2.And(Sse2.ShiftRightLogical(sourceValue.AsInt32(), 4).AsSByte(), nibbleMaskSByte); - Vector128 lowNibbles = Sse2.And(sourceValue, nibbleMaskSByte); - - Vector128 bitMask = Ssse3.Shuffle(bitMaskLookup, lowNibbles); - Vector128 bitPositions = Ssse3.Shuffle(bitPosLookup, highNibbles); - - Vector128 mask = Sse2.And(bitPositions, bitMask); - - mask = Sse2.CompareEqual(nullMaskSByte, Sse2.CompareEqual(nullMaskSByte, mask)); - return mask; - } - - internal static readonly Vector128 s_nibbleMaskSByte = Vector128.Create((sbyte)0xF); - internal static readonly Vector128 s_nullMaskSByte = Vector128.Zero; - - // See comment above in method CreateEscapingMask_DefaultJavaScriptEncoderBasicLatin - // for description of the bit-mask. - private static readonly Vector128 s_bitMaskLookupBasicLatin = Vector128.Create( - 0b_01000011, // low-nibble 0 - 0b_00000011, // low-nibble 1 - 0b_00000111, // low-nibble 2 - 0b_00000011, // low-nibble 3 - 0b_00000011, // low-nibble 4 - 0b_00000011, // low-nibble 5 - 0b_00000111, // low-nibble 6 - 0b_00000111, // low-nibble 7 - 0b_00000011, // low-nibble 8 - 0b_00000011, // low-nibble 9 - 0b_00000011, // low-nibble A - 0b_00000111, // low-nibble B - 0b_00101011, // low-nibble C - 0b_00000011, // low-nibble D - 0b_00001011, // low-nibble E - 0b_10000011 // low-nibble F - ).AsSByte(); - - // To check if a bit in a bitmask from the Bitmask is set, in a sequential code - // we would do ((1 << bitIndex) & bitmask) != 0 - // As there is no hardware instrinic for such a shift, we use a lookup that - // stores the shifted bitpositions. - // So (1 << bitIndex) becomes BitPosLook[bitIndex], which is simd-friendly. - // - // A bitmask from the Bitmask (above) is created only for values 0..7 (one byte), - // so to avoid a explicit check for values outside 0..7, i.e. - // high nibbles 8..F, we use a bitpos that always results in escaping. - internal static readonly Vector128 s_bitPosLookup = Vector128.Create( - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, // high-nibble 0..7 - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // high-nibble 8..F - ).AsSByte(); - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoder.cs index 6fb8526e42dca..a7142b188cb02 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoder.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoder.cs @@ -4,17 +4,12 @@ using System.Buffers; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Unicode; -#if NETCOREAPP -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using System.Runtime.Intrinsics.Arm; -#endif - namespace System.Text.Encodings.Web { /// @@ -26,27 +21,7 @@ namespace System.Text.Encodings.Web /// public abstract class TextEncoder { - // Fast cache for Ascii - private readonly byte[][] _asciiEscape = new byte[0x80][]; - - private volatile bool _isAsciiCacheInitialized; - private AsciiNeedsEscapingData _asciiNeedsEscaping; - -#if NETCOREAPP - private Vector128 _bitMaskLookupAsciiNeedsEscaping; -#endif - - // Keep a reference to Array.Empty as this is used as a singleton for comparisons - // and there is no guarantee that Array.Empty() will always be the same instance. - private static readonly byte[] s_noEscape = Array.Empty(); - - // The following pragma disables a warning complaining about non-CLS compliant members being abstract, - // and wants me to mark the type as non-CLS compliant. - // It is true that this type cannot be extended by all CLS compliant languages. - // Having said that, if I marked the type as non-CLS all methods that take it as parameter will now have to be marked CLSCompliant(false), - // yet consumption of concrete encoders is totally CLS compliant, - // as it?s mainly to be done by calling helper methods in TextEncoderExtensions class, - // and so I think the warning is a bit too aggressive. + private const int EncodeStartingOutputBufferSize = 1024; // bytes or chars, depending /// /// Encodes a Unicode scalar into a buffer. @@ -59,11 +34,63 @@ public abstract class TextEncoder /// This method is seldom called directly. One of the TextEncoder.Encode overloads should be used instead. /// Implementations of need to be thread safe and stateless. /// -#pragma warning disable 3011 [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public unsafe abstract bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe bool TryEncodeUnicodeScalar(uint unicodeScalar, Span buffer, out int charsWritten) + { + fixed (char* pBuffer = &MemoryMarshal.GetReference(buffer)) + { + return TryEncodeUnicodeScalar((int)unicodeScalar, pBuffer, buffer.Length, out charsWritten); + } + } + + private bool TryEncodeUnicodeScalarUtf8(uint unicodeScalar, Span utf16ScratchBuffer, Span utf8Destination, out int bytesWritten) + { + if (!TryEncodeUnicodeScalar(unicodeScalar, utf16ScratchBuffer, out int charsWritten)) + { + // We really don't expect any encoder to exceed 24 escaped chars per input scalar. + // If this happens, throw an exception and we can figure out if we want to support it + // in the future. + ThrowArgumentException_MaxOutputCharsPerInputChar(); + } + + // Transcode chars -> bytes one at a time. + + utf16ScratchBuffer = utf16ScratchBuffer.Slice(0, charsWritten); + int dstIdx = 0; + + while (!utf16ScratchBuffer.IsEmpty) + { + if (Rune.DecodeFromUtf16(utf16ScratchBuffer, out Rune nextScalarValue, out int scalarUtf16CodeUnitCount) != OperationStatus.Done) + { + // Wrote bad UTF-16 data, we cannot transcode to UTF-8. + ThrowArgumentException_MaxOutputCharsPerInputChar(); + } + + uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)nextScalarValue.Value); + do + { + if (SpanUtility.IsValidIndex(utf8Destination, dstIdx)) + { + utf8Destination[dstIdx++] = (byte)utf8lsb; + } + else + { + bytesWritten = 0; // ran out of space in the destination + return false; + } + } while ((utf8lsb >>= 8) != 0); + + utf16ScratchBuffer = utf16ScratchBuffer.Slice(scalarUtf16CodeUnitCount); + } + + bytesWritten = dstIdx; + return true; + } + // all subclasses have the same implementation of this method. // but this cannot be made virtual, because it will cause a virtual call to Encodes, and it destroys perf, i.e. makes common scenario 2x slower @@ -77,7 +104,6 @@ public abstract class TextEncoder [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public unsafe abstract int FindFirstCharacterToEncode(char* text, int textLength); -#pragma warning restore /// /// Determines if a given Unicode scalar will be encoded. @@ -102,159 +128,60 @@ public abstract class TextEncoder /// Encoded string. public virtual string Encode(string value) { - if (value == null) + if (value is null) { - throw new ArgumentNullException(nameof(value)); + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); } - unsafe + int indexOfFirstCharToEncode = FindFirstCharacterToEncode(value.AsSpan()); + if (indexOfFirstCharToEncode < 0) { - fixed (char* valuePointer = value) - { - int firstCharacterToEncode = FindFirstCharacterToEncode(valuePointer, value.Length); - - if (firstCharacterToEncode == -1) - { - return value; - } - - int bufferSize = MaxOutputCharactersPerInputCharacter * value.Length; - - string result; - if (bufferSize < 1024) - { - char* wholebuffer = stackalloc char[bufferSize]; - OperationStatus status = EncodeIntoBuffer(wholebuffer, bufferSize, valuePointer, value.Length, out int _, out int totalWritten, firstCharacterToEncode); - if (status != OperationStatus.Done) - { - ThrowArgumentException_MaxOutputCharsPerInputChar(); - } - - result = new string(wholebuffer, 0, totalWritten); - } - else - { - char[] wholebuffer = new char[bufferSize]; - fixed (char* buffer = &wholebuffer[0]) - { - OperationStatus status = EncodeIntoBuffer(buffer, bufferSize, valuePointer, value.Length, out int _, out int totalWritten, firstCharacterToEncode); - if (status != OperationStatus.Done) - { - ThrowArgumentException_MaxOutputCharsPerInputChar(); - } + return value; // shortcut: there's no work to perform + } - result = new string(wholebuffer, 0, totalWritten); - } - } + // We optimize for the data having no "requires encoding" chars, so keep the + // real encoding logic out of the fast path. - return result; - } - } + return EncodeToNewString(value.AsSpan(), indexOfFirstCharToEncode); } - private unsafe OperationStatus EncodeIntoBuffer( - char* buffer, - int bufferLength, - char* value, - int valueLength, - out int charsConsumed, - out int charsWritten, - int firstCharacterToEncode, - bool isFinalBlock = true) + private string EncodeToNewString(ReadOnlySpan value, int indexOfFirstCharToEncode) { - Debug.Assert(value != null); - Debug.Assert(firstCharacterToEncode >= 0); - - char* originalBuffer = buffer; - charsWritten = 0; - - if (firstCharacterToEncode > 0) - { - Debug.Assert(firstCharacterToEncode <= valueLength); - Buffer.MemoryCopy(source: value, - destination: buffer, - destinationSizeInBytes: sizeof(char) * bufferLength, - sourceBytesToCopy: sizeof(char) * firstCharacterToEncode); - - charsWritten += firstCharacterToEncode; - bufferLength -= firstCharacterToEncode; - buffer += firstCharacterToEncode; - } - - int valueIndex = firstCharacterToEncode; - - char firstChar = value[valueIndex]; - char secondChar = firstChar; - bool wasSurrogatePair = false; - - // this loop processes character pairs (in case they are surrogates). - // there is an if block below to process single last character. - int secondCharIndex; - for (secondCharIndex = valueIndex + 1; secondCharIndex < valueLength; secondCharIndex++) - { - if (!wasSurrogatePair) - { - firstChar = secondChar; - } - else - { - firstChar = value[secondCharIndex - 1]; - } - - secondChar = value[secondCharIndex]; - - if (!WillEncode(firstChar)) - { - wasSurrogatePair = false; - *buffer = firstChar; - buffer++; - bufferLength--; - charsWritten++; - } - else - { - int nextScalar = UnicodeHelpers.GetScalarValueFromUtf16(firstChar, secondChar, out wasSurrogatePair, out bool _); - if (!TryEncodeUnicodeScalar(nextScalar, buffer, bufferLength, out int charsWrittenThisTime)) - { - charsConsumed = (int)(originalBuffer - buffer); - return OperationStatus.DestinationTooSmall; - } + ReadOnlySpan remainingInput = value.Slice(indexOfFirstCharToEncode); + ValueStringBuilder stringBuilder = new ValueStringBuilder(stackalloc char[EncodeStartingOutputBufferSize]); - if (wasSurrogatePair) - { - secondCharIndex++; - } +#if !NETCOREAPP + // Can't call string.Concat later in the method, so memcpy now. + stringBuilder.Append(value.Slice(0, indexOfFirstCharToEncode)); +#endif - buffer += charsWrittenThisTime; - bufferLength -= charsWrittenThisTime; - charsWritten += charsWrittenThisTime; - } - } + // On each iteration of the main loop, we'll make sure we have at least this many chars left in the + // destination buffer. This should prevent us from making very chatty calls where we only make progress + // one char at a time. + int minBufferBumpEachIteration = Math.Max(MaxOutputCharactersPerInputCharacter, EncodeStartingOutputBufferSize); - if (secondCharIndex == valueLength) + do { - firstChar = value[valueLength - 1]; - int nextScalar = UnicodeHelpers.GetScalarValueFromUtf16(firstChar, null, out wasSurrogatePair, out bool needMoreData); - if (!isFinalBlock && needMoreData) - { - Debug.Assert(wasSurrogatePair == false); - charsConsumed = (int)(buffer - originalBuffer); - return OperationStatus.NeedMoreData; - } - - if (!TryEncodeUnicodeScalar(nextScalar, buffer, bufferLength, out int charsWrittenThisTime)) + // AppendSpan mutates the VSB length to include the newly-added span. This potentially overallocates. + Span destBuffer = stringBuilder.AppendSpan(Math.Max(remainingInput.Length, minBufferBumpEachIteration)); + EncodeCore(remainingInput, destBuffer, out int charsConsumedJustNow, out int charsWrittenJustNow, isFinalBlock: true); + if (charsWrittenJustNow == 0 || (uint)charsWrittenJustNow > (uint)destBuffer.Length) { - charsConsumed = (int)(buffer - originalBuffer); - return OperationStatus.DestinationTooSmall; + ThrowArgumentException_MaxOutputCharsPerInputChar(); // couldn't make forward progress or returned bogus data } + remainingInput = remainingInput.Slice(charsConsumedJustNow); + // It's likely we didn't populate the entire span. If this is the case, adjust the VSB length + // to reflect that there's unused buffer at the end of the VSB instance. + stringBuilder.Length -= destBuffer.Length - charsWrittenJustNow; + } while (!remainingInput.IsEmpty); - buffer += charsWrittenThisTime; - bufferLength -= charsWrittenThisTime; - charsWritten += charsWrittenThisTime; - } - - charsConsumed = valueLength; - return OperationStatus.Done; +#if NETCOREAPP + string retVal = string.Concat(value.Slice(0, indexOfFirstCharToEncode), stringBuilder.AsSpan()); + stringBuilder.Dispose(); + return retVal; +#else + return stringBuilder.ToString(); +#endif } /// @@ -286,37 +213,18 @@ public virtual void Encode(TextWriter output, string value, int startIndex, int } ValidateRanges(startIndex, characterCount, actualInputLength: value.Length); - unsafe + int indexOfFirstCharToEncode = FindFirstCharacterToEncode(value.AsSpan(startIndex, characterCount)); + if (indexOfFirstCharToEncode < 0) { - fixed (char* valuePointer = value) - { - char* substring = valuePointer + startIndex; - int firstIndexToEncode = FindFirstCharacterToEncode(substring, characterCount); - - if (firstIndexToEncode == -1) // nothing to encode; - { - if (startIndex == 0 && characterCount == value.Length) // write whole string - { - output.Write(value); - return; - } - for (int i = 0; i < characterCount; i++) // write substring - { - output.Write(*substring); - substring++; - } - return; - } + indexOfFirstCharToEncode = characterCount; + } - // write prefix, then encode - for (int i = 0; i < firstIndexToEncode; i++) - { - output.Write(*substring); - substring++; - } + // memcpy all characters that don't require encoding, then encode any remaining chars - EncodeCore(output, substring, characterCount - firstIndexToEncode); - } + output.WritePartialString(value, startIndex, indexOfFirstCharToEncode); + if (indexOfFirstCharToEncode != characterCount) + { + EncodeCore(output, value.AsSpan(startIndex + indexOfFirstCharToEncode, characterCount - indexOfFirstCharToEncode)); } } @@ -339,37 +247,16 @@ public virtual void Encode(TextWriter output, char[] value, int startIndex, int } ValidateRanges(startIndex, characterCount, actualInputLength: value.Length); - unsafe + int indexOfFirstCharToEncode = FindFirstCharacterToEncode(value.AsSpan(startIndex, characterCount)); + if (indexOfFirstCharToEncode < 0) { - fixed (char* valuePointer = value) - { - char* substring = valuePointer + startIndex; - int firstIndexToEncode = FindFirstCharacterToEncode(substring, characterCount); - - if (firstIndexToEncode == -1) // nothing to encode; - { - if (startIndex == 0 && characterCount == value.Length) // write whole string - { - output.Write(value); - return; - } - for (int i = 0; i < characterCount; i++) // write substring - { - output.Write(*substring); - substring++; - } - return; - } - - // write prefix, then encode - for (int i = 0; i < firstIndexToEncode; i++) - { - output.Write(*substring); - substring++; - } + indexOfFirstCharToEncode = characterCount; + } + output.Write(value, startIndex, indexOfFirstCharToEncode); - EncodeCore(output, substring, characterCount - firstIndexToEncode); - } + if (indexOfFirstCharToEncode != characterCount) + { + EncodeCore(output, value.AsSpan(startIndex + indexOfFirstCharToEncode, characterCount - indexOfFirstCharToEncode)); } } @@ -385,184 +272,122 @@ public virtual void Encode(TextWriter output, char[] value, int startIndex, int /// if there is no further source data that needs to be encoded. /// An describing the result of the encoding operation. /// The buffers and must not overlap. - public unsafe virtual OperationStatus EncodeUtf8( + public virtual OperationStatus EncodeUtf8( ReadOnlySpan utf8Source, Span utf8Destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { - int originalUtf8SourceLength = utf8Source.Length; - int originalUtf8DestinationLength = utf8Destination.Length; - - const int TempUtf16CharBufferLength = 24; // arbitrarily chosen, but sufficient for any reasonable implementation - char* pTempCharBuffer = stackalloc char[TempUtf16CharBufferLength]; - - const int TempUtf8ByteBufferLength = TempUtf16CharBufferLength * 3 /* max UTF-8 output code units per UTF-16 input code unit */; - byte* pTempUtf8Buffer = stackalloc byte[TempUtf8ByteBufferLength]; - - uint nextScalarValue; - int utf8BytesConsumedForScalar = 0; - int nonEscapedByteCount = 0; - OperationStatus opStatus = OperationStatus.Done; - - while (!utf8Source.IsEmpty) + // The Encode method is intended to be called in a loop, potentially where the source buffer + // is much larger than the destination buffer. We don't want to walk the entire source buffer + // on each invocation of this method, so we'll slice the source buffer to be no larger than + // the destination buffer to avoid performing unnecessary work. The potential exists for us to + // split the source in the middle of a UTF-8 multi-byte sequence. If this happens, + // FindFirstCharacterToEncodeUtf8 will report the split bytes as "needs encoding", we'll fall + // back down the slow path, and the slow path will handle the scenario appropriately. + + ReadOnlySpan sourceSearchSpace = utf8Source; + if (utf8Destination.Length < utf8Source.Length) { - // For performance, read until we require escaping. - do - { - nextScalarValue = utf8Source[nonEscapedByteCount]; - if (UnicodeUtility.IsAsciiCodePoint(nextScalarValue)) - { - // Check Ascii cache. - byte[]? encodedBytes = GetAsciiEncoding((byte)nextScalarValue); + sourceSearchSpace = utf8Source.Slice(0, utf8Destination.Length); + } - if (ReferenceEquals(encodedBytes, s_noEscape)) - { - if (++nonEscapedByteCount <= utf8Destination.Length) - { - // Source data can be copied as-is. - continue; - } - - --nonEscapedByteCount; - opStatus = OperationStatus.DestinationTooSmall; - break; - } + int idxOfFirstByteToEncode = FindFirstCharacterToEncodeUtf8(sourceSearchSpace); + if (idxOfFirstByteToEncode < 0) + { + idxOfFirstByteToEncode = sourceSearchSpace.Length; + } - if (encodedBytes == null) - { - // We need to escape and update the cache, so break out of this loop. - opStatus = OperationStatus.Done; - utf8BytesConsumedForScalar = 1; - break; - } + utf8Source.Slice(0, idxOfFirstByteToEncode).CopyTo(utf8Destination); // memcpy data that doesn't need to be encoded + if (idxOfFirstByteToEncode == utf8Source.Length) + { + bytesConsumed = utf8Source.Length; + bytesWritten = utf8Source.Length; + return OperationStatus.Done; // memcopied all bytes, nothing more to do + } - // For performance, handle the non-escaped bytes and encoding here instead of breaking out of the loop. - if (nonEscapedByteCount > 0) - { - // We previously verified the destination size. - Debug.Assert(nonEscapedByteCount <= utf8Destination.Length); + // If we got to this point, we couldn't memcpy the entire source buffer into the destination. + // Either the destination was too short or we found data that needs to be encoded. - utf8Source.Slice(0, nonEscapedByteCount).CopyTo(utf8Destination); - utf8Source = utf8Source.Slice(nonEscapedByteCount); - utf8Destination = utf8Destination.Slice(nonEscapedByteCount); - nonEscapedByteCount = 0; - } + OperationStatus status = EncodeUtf8Core(utf8Source.Slice(idxOfFirstByteToEncode), utf8Destination.Slice(idxOfFirstByteToEncode), out int innerBytesConsumed, out int innerBytesWritten, isFinalBlock); + bytesConsumed = idxOfFirstByteToEncode + innerBytesConsumed; + bytesWritten = idxOfFirstByteToEncode + innerBytesWritten; + return status; + } - if (!((ReadOnlySpan)encodedBytes).TryCopyTo(utf8Destination)) - { - opStatus = OperationStatus.DestinationTooSmall; - break; - } + // skips the call to FindFirstCharacterToEncodeUtf8 + private protected virtual OperationStatus EncodeUtf8Core( + ReadOnlySpan utf8Source, + Span utf8Destination, + out int bytesConsumed, + out int bytesWritten, + bool isFinalBlock) + { + int originalUtf8SourceLength = utf8Source.Length; + int originalUtf8DestinationLength = utf8Destination.Length; - utf8Destination = utf8Destination.Slice(encodedBytes.Length); - utf8Source = utf8Source.Slice(1); - continue; - } + const int TempUtf16CharBufferLength = 24; // arbitrarily chosen, but sufficient for any reasonable implementation + Span utf16ScratchBuffer = stackalloc char[TempUtf16CharBufferLength]; - // Code path for non-Ascii. - opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Source.Slice(nonEscapedByteCount), out nextScalarValue, out utf8BytesConsumedForScalar); - if (opStatus == OperationStatus.Done) + while (!utf8Source.IsEmpty) + { + OperationStatus opStatus = Rune.DecodeFromUtf8(utf8Source, out Rune scalarValue, out int bytesConsumedJustNow); + if (opStatus != OperationStatus.Done) + { + if (!isFinalBlock && opStatus == OperationStatus.NeedMoreData) { - if (!WillEncode((int)nextScalarValue)) - { - nonEscapedByteCount += utf8BytesConsumedForScalar; - if (nonEscapedByteCount <= utf8Destination.Length) - { - // Source data can be copied as-is. - continue; - } - - nonEscapedByteCount -= utf8BytesConsumedForScalar; - opStatus = OperationStatus.DestinationTooSmall; - } + goto NeedMoreData; } - // We need to escape. - break; - } while (nonEscapedByteCount < utf8Source.Length); - - if (nonEscapedByteCount > 0) - { - // We previously verified the destination size. - Debug.Assert(nonEscapedByteCount <= utf8Destination.Length); - - utf8Source.Slice(0, nonEscapedByteCount).CopyTo(utf8Destination); - utf8Source = utf8Source.Slice(nonEscapedByteCount); - utf8Destination = utf8Destination.Slice(nonEscapedByteCount); - nonEscapedByteCount = 0; + Debug.Assert(scalarValue == Rune.ReplacementChar); // DecodeFromUtf8 should've performed substitution + goto MustEncode; } - if (utf8Source.IsEmpty) - { - goto Done; - } - - // This code path is hit for ill-formed input data (where decoding has replaced it with U+FFFD) - // and for well-formed input data that must be escaped. - - if (opStatus != OperationStatus.Done) // Optimize happy path. + if (!WillEncode(scalarValue.Value)) { - if (opStatus == OperationStatus.NeedMoreData) + uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)scalarValue.Value); + int dstIdxTemp = 0; + do { - if (!isFinalBlock) + if ((uint)dstIdxTemp >= (uint)utf8Destination.Length) { - bytesConsumed = originalUtf8SourceLength - utf8Source.Length; - bytesWritten = originalUtf8DestinationLength - utf8Destination.Length; - return OperationStatus.NeedMoreData; + goto DestinationTooSmall; } - // else treat this as a normal invalid subsequence. - } - else if (opStatus == OperationStatus.DestinationTooSmall) - { - goto ReturnDestinationTooSmall; - } + utf8Destination[dstIdxTemp++] = (byte)utf8lsb; + } while ((utf8lsb >>= 8) != 0); + utf8Source = utf8Source.Slice(bytesConsumedJustNow); + utf8Destination = utf8Destination.Slice(dstIdxTemp); + continue; } - if (TryEncodeUnicodeScalar((int)nextScalarValue, pTempCharBuffer, TempUtf16CharBufferLength, out int charsWrittenJustNow)) - { - // Now that we have it as UTF-16, transcode it to UTF-8. - // Need to copy it to a temporary buffer first, otherwise GetBytes might throw an exception - // due to lack of output space. - - int transcodedByteCountThisIteration = Encoding.UTF8.GetBytes(pTempCharBuffer, charsWrittenJustNow, pTempUtf8Buffer, TempUtf8ByteBufferLength); - ReadOnlySpan transcodedUtf8BytesThisIteration = new ReadOnlySpan(pTempUtf8Buffer, transcodedByteCountThisIteration); - - // Update cache for Ascii - if (UnicodeUtility.IsAsciiCodePoint(nextScalarValue)) - { - _asciiEscape[nextScalarValue] = transcodedUtf8BytesThisIteration.ToArray(); - } - - if (!transcodedUtf8BytesThisIteration.TryCopyTo(utf8Destination)) - { - goto ReturnDestinationTooSmall; - } + MustEncode: - utf8Destination = utf8Destination.Slice(transcodedByteCountThisIteration); - } - else + if (!TryEncodeUnicodeScalarUtf8((uint)scalarValue.Value, utf16ScratchBuffer, utf8Destination, out int bytesWrittenJustNow)) { - // We really don't expect this to fail. If that happens we'll report an error to our caller. - bytesConsumed = originalUtf8SourceLength - utf8Source.Length; - bytesWritten = originalUtf8DestinationLength - utf8Destination.Length; - return OperationStatus.InvalidData; + goto DestinationTooSmall; } - utf8Source = utf8Source.Slice(utf8BytesConsumedForScalar); + utf8Source = utf8Source.Slice(bytesConsumedJustNow); + utf8Destination = utf8Destination.Slice(bytesWrittenJustNow); } - Done: - // Input buffer has been fully processed! - bytesConsumed = originalUtf8SourceLength; - bytesWritten = originalUtf8DestinationLength - utf8Destination.Length; - return OperationStatus.Done; + // And we're finished! + + OperationStatus retVal = OperationStatus.Done; - ReturnDestinationTooSmall: + ReturnCommon: bytesConsumed = originalUtf8SourceLength - utf8Source.Length; bytesWritten = originalUtf8DestinationLength - utf8Destination.Length; - return OperationStatus.DestinationTooSmall; + return retVal; + + NeedMoreData: + retVal = OperationStatus.NeedMoreData; + goto ReturnCommon; + + DestinationTooSmall: + retVal = OperationStatus.DestinationTooSmall; + goto ReturnCommon; } /// @@ -584,103 +409,135 @@ public virtual OperationStatus Encode( out int charsWritten, bool isFinalBlock = true) { - unsafe + // The Encode method is intended to be called in a loop, potentially where the source buffer + // is much larger than the destination buffer. We don't want to walk the entire source buffer + // on each invocation of this method, so we'll slice the source buffer to be no larger than + // the destination buffer to avoid performing unnecessary work. The potential exists for us to + // split the source in the middle of a UTF-16 surrogate pair. If this happens, + // FindFirstCharacterToEncode will report the split surrogate as "needs encoding", we'll fall + // back down the slow path, and the slow path will handle the surrogate appropriately. + + ReadOnlySpan sourceSearchSpace = source; + if (destination.Length < source.Length) { - fixed (char* sourcePtr = source) - { - int firstCharacterToEncode; - if (source.IsEmpty || (firstCharacterToEncode = FindFirstCharacterToEncode(sourcePtr, source.Length)) == -1) - { - if (source.TryCopyTo(destination)) - { - charsConsumed = source.Length; - charsWritten = source.Length; - return OperationStatus.Done; - } + sourceSearchSpace = source.Slice(0, destination.Length); + } - charsConsumed = 0; - charsWritten = 0; - return OperationStatus.DestinationTooSmall; - } - else if (destination.IsEmpty) - { - // Guards against passing a null destinationPtr to EncodeIntoBuffer (pinning an empty Span will return a null pointer). - charsConsumed = 0; - charsWritten = 0; - return OperationStatus.DestinationTooSmall; - } + int idxOfFirstCharToEncode = FindFirstCharacterToEncode(sourceSearchSpace); + if (idxOfFirstCharToEncode < 0) + { + idxOfFirstCharToEncode = sourceSearchSpace.Length; + } - fixed (char* destinationPtr = destination) - { - return EncodeIntoBuffer(destinationPtr, destination.Length, sourcePtr, source.Length, out charsConsumed, out charsWritten, firstCharacterToEncode, isFinalBlock); - } - } + source.Slice(0, idxOfFirstCharToEncode).CopyTo(destination); // memcpy data that doesn't need to be encoded + if (idxOfFirstCharToEncode == source.Length) + { + charsConsumed = source.Length; + charsWritten = source.Length; + return OperationStatus.Done; // memcopied all chars, nothing more to do } - } - private unsafe void EncodeCore(TextWriter output, char* value, int valueLength) - { - Debug.Assert(value != null && output != null); - Debug.Assert(valueLength >= 0); + // If we got to this point, we couldn't memcpy the entire source buffer into the destination. + // Either the destination was too short or we found data that needs to be encoded. - int bufferLength = MaxOutputCharactersPerInputCharacter; - char* buffer = stackalloc char[bufferLength]; + OperationStatus status = EncodeCore(source.Slice(idxOfFirstCharToEncode), destination.Slice(idxOfFirstCharToEncode), out int innerCharsConsumed, out int innerCharsWritten, isFinalBlock); + charsConsumed = idxOfFirstCharToEncode + innerCharsConsumed; + charsWritten = idxOfFirstCharToEncode + innerCharsWritten; + return status; + } - char firstChar = *value; - char secondChar = firstChar; - bool wasSurrogatePair = false; - int charsWritten; + // skips the call to FindFirstCharacterToEncode + private protected virtual OperationStatus EncodeCore(ReadOnlySpan source, Span destination, out int charsConsumed, out int charsWritten, bool isFinalBlock) + { + int originalSourceLength = source.Length; + int originalDestinationLength = destination.Length; - // this loop processes character pairs (in case they are surrogates). - // there is an if block below to process single last character. - int secondCharIndex; - for (secondCharIndex = 1; secondCharIndex < valueLength; secondCharIndex++) + while (!source.IsEmpty) { - if (!wasSurrogatePair) - { - firstChar = secondChar; - } - else + OperationStatus status = Rune.DecodeFromUtf16(source, out Rune scalarValue, out int charsConsumedJustNow); + if (status != OperationStatus.Done) { - firstChar = value[secondCharIndex - 1]; - } - secondChar = value[secondCharIndex]; + if (!isFinalBlock && status == OperationStatus.NeedMoreData) + { + goto NeedMoreData; + } - if (!WillEncode(firstChar)) - { - wasSurrogatePair = false; - output.Write(firstChar); + Debug.Assert(scalarValue == Rune.ReplacementChar); // should be replacement char + goto MustEncode; } - else + + if (!WillEncode(scalarValue.Value)) { - int nextScalar = UnicodeHelpers.GetScalarValueFromUtf16(firstChar, secondChar, out wasSurrogatePair, out bool _); - if (!TryEncodeUnicodeScalar(nextScalar, buffer, bufferLength, out charsWritten)) + if (!scalarValue.TryEncodeToUtf16(destination, out _)) { - ThrowArgumentException_MaxOutputCharsPerInputChar(); + goto DestinationTooSmall; } - Write(output, buffer, charsWritten); + source = source.Slice(charsConsumedJustNow); + destination = destination.Slice(charsConsumedJustNow); // reflecting input directly to the output, same # of chars written + continue; + } - if (wasSurrogatePair) - { - secondCharIndex++; - } + MustEncode: + + if (!TryEncodeUnicodeScalar((uint)scalarValue.Value, destination, out int charsWrittenJustNow)) + { + goto DestinationTooSmall; } + + source = source.Slice(charsConsumedJustNow); + destination = destination.Slice(charsWrittenJustNow); } - if (!wasSurrogatePair || (secondCharIndex == valueLength)) + // And we're finished! + + OperationStatus retVal = OperationStatus.Done; + + ReturnCommon: + charsConsumed = originalSourceLength - source.Length; + charsWritten = originalDestinationLength - destination.Length; + return retVal; + + NeedMoreData: + retVal = OperationStatus.NeedMoreData; + goto ReturnCommon; + + DestinationTooSmall: + retVal = OperationStatus.DestinationTooSmall; + goto ReturnCommon; + } + + // skips call to FindFirstCharacterToEncode + private void EncodeCore(TextWriter output, ReadOnlySpan value) + { + Debug.Assert(output != null); + Debug.Assert(!value.IsEmpty, "Caller should've special-cased 'no encoding needed'."); + + // On each iteration of the main loop, we'll make sure we have at least this many chars left in the + // destination buffer. This should prevent us from making very chatty calls where we only make progress + // one char at a time. + int minBufferBumpEachIteration = Math.Max(MaxOutputCharactersPerInputCharacter, EncodeStartingOutputBufferSize); + char[] rentedArray = ArrayPool.Shared.Rent(Math.Max(value.Length, minBufferBumpEachIteration)); + Span scratchBuffer = rentedArray; + + do { - firstChar = value[valueLength - 1]; - int nextScalar = UnicodeHelpers.GetScalarValueFromUtf16(firstChar, null, out wasSurrogatePair, out bool _); - if (!TryEncodeUnicodeScalar(nextScalar, buffer, bufferLength, out charsWritten)) + EncodeCore(value, scratchBuffer, out int charsConsumedJustNow, out int charsWrittenJustNow, isFinalBlock: true); + if (charsWrittenJustNow == 0 || (uint)charsWrittenJustNow > (uint)scratchBuffer.Length) { - ThrowArgumentException_MaxOutputCharsPerInputChar(); + ThrowArgumentException_MaxOutputCharsPerInputChar(); // couldn't make forward progress or returned bogus data } - Write(output, buffer, charsWritten); - } + + output.Write(rentedArray, 0, charsWrittenJustNow); // write char[], not Span, for best compat & performance + value = value.Slice(charsConsumedJustNow); + } while (!value.IsEmpty); + + ArrayPool.Shared.Return(rentedArray); } - private unsafe int FindFirstCharacterToEncode(ReadOnlySpan text) + private protected virtual unsafe int FindFirstCharacterToEncode(ReadOnlySpan text) { + // Default implementation calls the unsafe overload + fixed (char* pText = &MemoryMarshal.GetReference(text)) { return FindFirstCharacterToEncode(pText, text.Length); @@ -697,205 +554,21 @@ private unsafe int FindFirstCharacterToEncode(ReadOnlySpan text) /// current encoder instance, or -1 if no data in requires escaping. /// [EditorBrowsable(EditorBrowsableState.Never)] - public virtual unsafe int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) + public virtual int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) { - if (!_isAsciiCacheInitialized) - { - InitializeAsciiCache(); - } + int utf8TextOriginalLength = utf8Text.Length; - // Loop through the input text, terminating when we see ill-formed UTF-8 or when we decode a scalar value - // that must be encoded. If we see either of these things then we'll return its index in the original - // input sequence. If we consume the entire text without seeing either of these, return -1 to indicate - // that the text can be copied as-is without escaping. - - fixed (byte* ptr = utf8Text) + while (!utf8Text.IsEmpty) { - int idx = 0; - -#if NETCOREAPP - if ((Sse2.IsSupported || AdvSimd.Arm64.IsSupported) && utf8Text.Length - 16 >= idx) - { - // Hoist these outside the loop, as the JIT won't do it. - Vector128 bitMaskLookupAsciiNeedsEscaping = _bitMaskLookupAsciiNeedsEscaping; - Vector128 bitPosLookup = Ssse3Helper.s_bitPosLookup; - Vector128 nibbleMaskSByte = Ssse3Helper.s_nibbleMaskSByte; - Vector128 nullMaskSByte = Ssse3Helper.s_nullMaskSByte; - - sbyte* startingAddress = (sbyte*)ptr; - do - { - Debug.Assert(startingAddress >= ptr && startingAddress <= (ptr + utf8Text.Length - 16)); - - // Load the next 16 bytes. - Vector128 sourceValue; - bool containsNonAsciiBytes; - - // Check for ASCII text. Any byte that's not in the ASCII range will already be negative when - // casted to signed byte. - if (Sse2.IsSupported) - { - sourceValue = Sse2.LoadVector128(startingAddress); - containsNonAsciiBytes = Sse2Helper.ContainsNonAsciiByte(sourceValue); - } - else if (AdvSimd.Arm64.IsSupported) - { - sourceValue = AdvSimd.LoadVector128(startingAddress); - containsNonAsciiBytes = AdvSimdHelper.ContainsNonAsciiByte(sourceValue); - } - else - { - throw new PlatformNotSupportedException(); - } - - if (!containsNonAsciiBytes) - { - // All of the following 16 bytes is ASCII. - // TODO AdvSimd: optimization maybe achievable using VectorTableLookup and/or VectorTableLookupExtension - - if (Ssse3.IsSupported) - { - Vector128 mask = Ssse3Helper.CreateEscapingMask(sourceValue, bitMaskLookupAsciiNeedsEscaping, bitPosLookup, nibbleMaskSByte, nullMaskSByte); - int index = Sse2Helper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - - if (index < 16) - { - idx += index; - goto Return; - } - } - else - { - byte* p = (byte*)startingAddress; - if (DoesAsciiNeedEncoding(p[0])) goto Return; - if (DoesAsciiNeedEncoding(p[1])) goto Return1; - if (DoesAsciiNeedEncoding(p[2])) goto Return2; - if (DoesAsciiNeedEncoding(p[3])) goto Return3; - if (DoesAsciiNeedEncoding(p[4])) goto Return4; - if (DoesAsciiNeedEncoding(p[5])) goto Return5; - if (DoesAsciiNeedEncoding(p[6])) goto Return6; - if (DoesAsciiNeedEncoding(p[7])) goto Return7; - if (DoesAsciiNeedEncoding(p[8])) goto Return8; - if (DoesAsciiNeedEncoding(p[9])) goto Return9; - if (DoesAsciiNeedEncoding(p[10])) goto Return10; - if (DoesAsciiNeedEncoding(p[11])) goto Return11; - if (DoesAsciiNeedEncoding(p[12])) goto Return12; - if (DoesAsciiNeedEncoding(p[13])) goto Return13; - if (DoesAsciiNeedEncoding(p[14])) goto Return14; - if (DoesAsciiNeedEncoding(p[15])) goto Return15; - } - - idx += 16; - } - else - { - // At least one of the following 16 bytes is non-ASCII. - - int processNextSixteen = idx + 16; - Debug.Assert(processNextSixteen <= utf8Text.Length); - - while (idx < processNextSixteen) - { - Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length)); - - if (UnicodeUtility.IsAsciiCodePoint(ptr[idx])) - { - if (DoesAsciiNeedEncoding(ptr[idx])) - { - goto Return; - } - idx++; - } - else - { - OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar); - - Debug.Assert(nextScalarValue <= int.MaxValue); - if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue)) - { - goto Return; - } - - Debug.Assert(opStatus == OperationStatus.Done); - idx += utf8BytesConsumedForScalar; - } - } - } - startingAddress = (sbyte*)ptr + idx; - } - while (utf8Text.Length - 16 >= idx); - - // Process the remaining bytes. - Debug.Assert(utf8Text.Length - idx < 16); - } -#endif - - while (idx < utf8Text.Length) + OperationStatus opStatus = Rune.DecodeFromUtf8(utf8Text, out Rune scalarValue, out int bytesConsumed); + if (opStatus != OperationStatus.Done || WillEncode(scalarValue.Value)) { - Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length)); - - if (UnicodeUtility.IsAsciiCodePoint(ptr[idx])) - { - if (DoesAsciiNeedEncoding(ptr[idx])) - { - goto Return; - } - idx++; - } - else - { - OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar); - - Debug.Assert(nextScalarValue <= int.MaxValue); - if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue)) - { - goto Return; - } - - Debug.Assert(opStatus == OperationStatus.Done); - idx += utf8BytesConsumedForScalar; - } + break; } - Debug.Assert(idx == utf8Text.Length); - - idx = -1; // All bytes are allowed. - goto Return; - -#if NETCOREAPP - Return15: - return idx + 15; - Return14: - return idx + 14; - Return13: - return idx + 13; - Return12: - return idx + 12; - Return11: - return idx + 11; - Return10: - return idx + 10; - Return9: - return idx + 9; - Return8: - return idx + 8; - Return7: - return idx + 7; - Return6: - return idx + 6; - Return5: - return idx + 5; - Return4: - return idx + 4; - Return3: - return idx + 3; - Return2: - return idx + 2; - Return1: - return idx + 1; -#endif - Return: - return idx; + utf8Text = utf8Text.Slice(bytesConsumed); } + + return (utf8Text.IsEmpty) ? -1 : utf8TextOriginalLength - utf8Text.Length; } internal static bool TryCopyCharacters(string source, Span destination, out int numberOfCharactersWritten) @@ -943,80 +616,7 @@ private static void ValidateRanges(int startIndex, int characterCount, int actua } } - private static unsafe void Write(TextWriter output, char* input, int inputLength) - { - Debug.Assert(output != null && input != null && inputLength >= 0); - - while (inputLength-- > 0) - { - output.Write(*input); - input++; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private byte[]? GetAsciiEncoding(byte value) - { - byte[] encoding = _asciiEscape[value]; - if (encoding == null) - { - if (!WillEncode(value)) - { - encoding = s_noEscape; - _asciiEscape[value] = encoding; - } - } - return encoding; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private unsafe void InitializeAsciiCache() - { -#if NETCOREAPP - if (Ssse3.IsSupported) - { - Vector128 vector = Vector128.Zero; - sbyte* tmp = (sbyte*)&vector; - - for (int i = 0; i < 0x80; i++) - { - bool willEncode = WillEncode(i); - _asciiNeedsEscaping.Data[i] = willEncode; - - if (willEncode) - { - int highNibble = i >> 4; - int lowNibble = i & 0xF; - - tmp[lowNibble] |= (sbyte)(1 << highNibble); - } - } - - _bitMaskLookupAsciiNeedsEscaping = vector; - return; - } -#endif - for (int i = 0; i < 0x80; i++) - { - _asciiNeedsEscaping.Data[i] = WillEncode(i); - } - - _isAsciiCacheInitialized = true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe bool DoesAsciiNeedEncoding(uint value) - { - Debug.Assert(value <= 0x7F); - - return _asciiNeedsEscaping.Data[value]; - } - - private unsafe struct AsciiNeedsEscapingData - { - public fixed bool Data[0x80]; - } - + [DoesNotReturn] private static void ThrowArgumentException_MaxOutputCharsPerInputChar() { throw new ArgumentException(SR.TextEncoderDoesNotImplementMaxOutputCharsPerInputChar); diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoderSettings.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoderSettings.cs index 3260456d7c782..d02c04c3ceb8a 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoderSettings.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/TextEncoderSettings.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Text.Internal; +using System.Runtime.CompilerServices; using System.Text.Unicode; namespace System.Text.Encodings.Web @@ -12,14 +12,13 @@ namespace System.Text.Encodings.Web /// public class TextEncoderSettings { - private readonly AllowedCharactersBitmap _allowedCharactersBitmap; + private AllowedBmpCodePointsBitmap _allowedCodePointsBitmap; /// /// Instantiates an empty filter (allows no code points through by default). /// public TextEncoderSettings() { - _allowedCharactersBitmap = AllowedCharactersBitmap.CreateNew(); } /// @@ -32,8 +31,7 @@ public TextEncoderSettings(TextEncoderSettings other) throw new ArgumentNullException(nameof(other)); } - _allowedCharactersBitmap = AllowedCharactersBitmap.CreateNew(); - AllowCodePoints(other.GetAllowedCodePoints()); + _allowedCodePointsBitmap = other.GetAllowedCodePointsBitmap(); // copy byval } /// @@ -46,7 +44,6 @@ public TextEncoderSettings(params UnicodeRange[] allowedRanges) { throw new ArgumentNullException(nameof(allowedRanges)); } - _allowedCharactersBitmap = AllowedCharactersBitmap.CreateNew(); AllowRanges(allowedRanges); } @@ -55,7 +52,7 @@ public TextEncoderSettings(params UnicodeRange[] allowedRanges) /// public virtual void AllowCharacter(char character) { - _allowedCharactersBitmap.AllowCharacter(character); + _allowedCodePointsBitmap.AllowChar(character); } /// @@ -70,7 +67,7 @@ public virtual void AllowCharacters(params char[] characters) for (int i = 0; i < characters.Length; i++) { - _allowedCharactersBitmap.AllowCharacter(characters[i]); + _allowedCodePointsBitmap.AllowChar(characters[i]); } } @@ -87,10 +84,9 @@ public virtual void AllowCodePoints(IEnumerable codePoints) foreach (var allowedCodePoint in codePoints) { // If the code point can't be represented as a BMP character, skip it. - char codePointAsChar = (char)allowedCodePoint; - if (allowedCodePoint == codePointAsChar) + if (UnicodeUtility.IsBmpCodePoint((uint)allowedCodePoint)) { - _allowedCharactersBitmap.AllowCharacter(codePointAsChar); + _allowedCodePointsBitmap.AllowChar((char)allowedCodePoint); } } } @@ -109,7 +105,9 @@ public virtual void AllowRange(UnicodeRange range) int rangeSize = range.Length; for (int i = 0; i < rangeSize; i++) { - _allowedCharactersBitmap.AllowCharacter((char)(firstCodePoint + i)); + int codePoint = firstCodePoint + i; + UnicodeDebug.AssertIsBmpCodePoint((uint)codePoint); // UnicodeRange only supports BMP + _allowedCodePointsBitmap.AllowChar((char)codePoint); } } @@ -134,7 +132,7 @@ public virtual void AllowRanges(params UnicodeRange[] ranges) /// public virtual void Clear() { - _allowedCharactersBitmap.Clear(); + _allowedCodePointsBitmap = default; } /// @@ -142,7 +140,7 @@ public virtual void Clear() /// public virtual void ForbidCharacter(char character) { - _allowedCharactersBitmap.ForbidCharacter(character); + _allowedCodePointsBitmap.ForbidChar(character); } /// @@ -157,7 +155,7 @@ public virtual void ForbidCharacters(params char[] characters) for (int i = 0; i < characters.Length; i++) { - _allowedCharactersBitmap.ForbidCharacter(characters[i]); + _allowedCodePointsBitmap.ForbidChar(characters[i]); } } @@ -175,7 +173,9 @@ public virtual void ForbidRange(UnicodeRange range) int rangeSize = range.Length; for (int i = 0; i < rangeSize; i++) { - _allowedCharactersBitmap.ForbidCharacter((char)(firstCodePoint + i)); + int codePoint = firstCodePoint + i; + UnicodeDebug.AssertIsBmpCodePoint((uint)codePoint); // UnicodeRange only supports BMP + _allowedCodePointsBitmap.ForbidChar((char)codePoint); } } @@ -196,25 +196,42 @@ public virtual void ForbidRanges(params UnicodeRange[] ranges) } /// - /// Retrieves the bitmap of allowed characters from this settings object. - /// The returned bitmap is a clone of the original bitmap to avoid unintentional modification. + /// Gets an enumeration of all allowed code points. /// - internal AllowedCharactersBitmap GetAllowedCharacters() + public virtual IEnumerable GetAllowedCodePoints() { - return _allowedCharactersBitmap.Clone(); + for (int i = 0; i <= char.MaxValue; i++) + { + if (_allowedCodePointsBitmap.IsCharAllowed((char)i)) + { + yield return i; + } + } } /// - /// Gets an enumeration of all allowed code points. + /// Retrieves the bitmap of allowed characters from this settings object. + /// The data is returned readonly byref. /// - public virtual IEnumerable GetAllowedCodePoints() + internal ref readonly AllowedBmpCodePointsBitmap GetAllowedCodePointsBitmap() { - for (int i = 0; i < 0x10000; i++) + if (GetType() == typeof(TextEncoderSettings)) { - if (_allowedCharactersBitmap.IsCharacterAllowed((char)i)) + return ref _allowedCodePointsBitmap; + } + else + { + // Somebody may have overridden GetAllowedCodePoints, and we need to honor that. + // Fabricate a new bitmap and populate it from the virtual overrides. + StrongBox newBitmap = new StrongBox(); + foreach (int allowedCodePoint in GetAllowedCodePoints()) { - yield return i; + if ((uint)allowedCodePoint <= char.MaxValue) + { + newBitmap.Value.AllowChar((char)allowedCodePoint); + } } + return ref newBitmap.Value; } } } diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/ThrowHelper.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/ThrowHelper.cs new file mode 100644 index 0000000000000..c1f89d4ac1905 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/ThrowHelper.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Text.Encodings.Web +{ + internal static class ThrowHelper + { + [DoesNotReturn] + internal static void ThrowArgumentNullException(ExceptionArgument argument) + { + throw new ArgumentNullException(GetArgumentName(argument)); + } + + [DoesNotReturn] + internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) + { + throw new ArgumentOutOfRangeException(GetArgumentName(argument)); + } + + private static string GetArgumentName(ExceptionArgument argument) + { + Debug.Assert(Enum.IsDefined(typeof(ExceptionArgument), argument), + "The enum value is not defined, please check the ExceptionArgument Enum."); + + return argument.ToString(); + } + } + + internal enum ExceptionArgument + { + value, + } +} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UnsafeRelaxedJavaScriptEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UnsafeRelaxedJavaScriptEncoder.cs deleted file mode 100644 index 489f0d1137fa4..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UnsafeRelaxedJavaScriptEncoder.cs +++ /dev/null @@ -1,369 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Buffers; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text.Internal; -using System.Text.Unicode; - -#if NETCOREAPP -using System.Numerics; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using System.Runtime.Intrinsics.Arm; -#endif - -namespace System.Text.Encodings.Web -{ - internal sealed class UnsafeRelaxedJavaScriptEncoder : JavaScriptEncoder - { - private readonly AllowedCharactersBitmap _allowedCharacters; - - internal static readonly UnsafeRelaxedJavaScriptEncoder s_singleton = new UnsafeRelaxedJavaScriptEncoder(); - - private UnsafeRelaxedJavaScriptEncoder() - { - var filter = new TextEncoderSettings(UnicodeRanges.All); - - _allowedCharacters = filter.GetAllowedCharacters(); - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - _allowedCharacters.ForbidUndefinedCharacters(); - - // '"' (U+0022 QUOTATION MARK) must always be escaped in Javascript / ECMAScript / JSON. - _allowedCharacters.ForbidCharacter('\"'); // can be used to escape attributes - - // '\' (U+005C REVERSE SOLIDUS) must always be escaped in Javascript / ECMAScript / JSON. - // '/' (U+002F SOLIDUS) is not Javascript / ECMAScript / JSON-sensitive so doesn't need to be escaped. - _allowedCharacters.ForbidCharacter('\\'); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool WillEncode(int unicodeScalar) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) - { - return true; - } - - Debug.Assert(unicodeScalar >= char.MinValue && unicodeScalar <= char.MaxValue); - - return !_allowedCharacters.IsUnicodeScalarAllowed(unicodeScalar); - } - - public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) - { - if (text == null) - { - throw new ArgumentNullException(nameof(text)); - } - - int idx = 0; - -#if NETCOREAPP - if (Sse2.IsSupported || AdvSimd.Arm64.IsSupported) - { - short* startingAddress = (short*)text; - while (textLength - 8 >= idx) - { - Debug.Assert(startingAddress >= text && startingAddress <= (text + textLength - 8)); - - // Load the next 8 characters. - Vector128 sourceValue; - Vector128 mask; - bool containsNonAsciiChars; - - if (Sse2.IsSupported) - { - sourceValue = Sse2.LoadVector128(startingAddress); - mask = Sse2Helper.CreateAsciiMask(sourceValue); - containsNonAsciiChars = Sse2Helper.ContainsNonAsciiByte(mask.AsSByte()); - } - else if (AdvSimd.Arm64.IsSupported) - { - sourceValue = AdvSimd.LoadVector128(startingAddress); - mask = AdvSimdHelper.CreateAsciiMask(sourceValue); - containsNonAsciiChars = AdvSimdHelper.ContainsNonAsciiByte(mask.AsSByte()); - } - else - { - throw new PlatformNotSupportedException(); - } - - if (containsNonAsciiChars) - { - // At least one of the following 8 characters is non-ASCII. - int processNextEight = idx + 8; - Debug.Assert(processNextEight <= textLength); - for (; idx < processNextEight; idx++) - { - Debug.Assert((text + idx) <= (text + textLength)); - if (!_allowedCharacters.IsCharacterAllowed(*(text + idx))) - { - goto Return; - } - } - startingAddress += 8; - } - else - { - int index; - - // Check if any of the 8 characters need to be escaped. - if (Sse2.IsSupported) - { - mask = Sse2Helper.CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(sourceValue); - index = Sse2Helper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - } - else if (AdvSimd.Arm64.IsSupported) - { - mask = AdvSimdHelper.CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(sourceValue); - index = AdvSimdHelper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - } - else - { - throw new PlatformNotSupportedException(); - } - - // If index >= 16, that means none of the 8 characters needed to be escaped. - if (index < 16) - { - // Found at least one character that needs to be escaped, figure out the index of - // the first one found that needed to be escaped within the 8 characters. - Debug.Assert(index % 2 == 0); - idx += index >> 1; - goto Return; - } - idx += 8; - startingAddress += 8; - } - } - - // Process the remaining characters. - Debug.Assert(textLength - idx < 8); - } -#endif - - for (; idx < textLength; idx++) - { - Debug.Assert((text + idx) <= (text + textLength)); - if (!_allowedCharacters.IsCharacterAllowed(*(text + idx))) - { - goto Return; - } - } - - idx = -1; // All characters are allowed. - - Return: - return idx; - } - - public override unsafe int FindFirstCharacterToEncodeUtf8(ReadOnlySpan utf8Text) - { - fixed (byte* ptr = utf8Text) - { - int idx = 0; - -#if NETCOREAPP - if (Sse2.IsSupported || AdvSimd.Arm64.IsSupported) - { - sbyte* startingAddress = (sbyte*)ptr; - while (utf8Text.Length - 16 >= idx) - { - Debug.Assert(startingAddress >= ptr && startingAddress <= (ptr + utf8Text.Length - 16)); - - // Load the next 16 bytes. - Vector128 sourceValue; - bool containsNonAsciiBytes; - - // Check for ASCII text. Any byte that's not in the ASCII range will already be negative when - // casted to signed byte. - if (Sse2.IsSupported) - { - sourceValue = Sse2.LoadVector128(startingAddress); - containsNonAsciiBytes = Sse2Helper.ContainsNonAsciiByte(sourceValue); - } - else - { - sourceValue = AdvSimd.LoadVector128(startingAddress); - containsNonAsciiBytes = AdvSimdHelper.ContainsNonAsciiByte(sourceValue); - } - - if (containsNonAsciiBytes) - { - // At least one of the following 16 bytes is non-ASCII. - - int processNextSixteen = idx + 16; - Debug.Assert(processNextSixteen <= utf8Text.Length); - - while (idx < processNextSixteen) - { - Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length)); - - if (UnicodeUtility.IsAsciiCodePoint(ptr[idx])) - { - if (!_allowedCharacters.IsUnicodeScalarAllowed(ptr[idx])) - { - goto Return; - } - idx++; - } - else - { - OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar); - - Debug.Assert(nextScalarValue <= int.MaxValue); - if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue)) - { - goto Return; - } - - Debug.Assert(opStatus == OperationStatus.Done); - idx += utf8BytesConsumedForScalar; - } - } - startingAddress = (sbyte*)ptr + idx; - } - else - { - // Check if any of the 16 bytes need to be escaped. - int index; - - if (Sse2.IsSupported) - { - Vector128 mask = Sse2Helper.CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(sourceValue); - index = Sse2Helper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - } - else - { - Vector128 mask = AdvSimdHelper.CreateEscapingMask_UnsafeRelaxedJavaScriptEncoder(sourceValue); - index = AdvSimdHelper.GetIndexOfFirstNonAsciiByte(mask.AsByte()); - } - - // If index >= 16, that means none of the 16 bytes needed to be escaped. - if (index < 16) - { - // Found at least one byte that needs to be escaped, figure out the index of - // the first one found that needed to be escaped within the 16 bytes. - idx += index; - goto Return; - } - idx += 16; - startingAddress += 16; - } - } - - // Process the remaining bytes. - Debug.Assert(utf8Text.Length - idx < 16); - } -#endif - - while (idx < utf8Text.Length) - { - Debug.Assert((ptr + idx) <= (ptr + utf8Text.Length)); - - if (UnicodeUtility.IsAsciiCodePoint(ptr[idx])) - { - if (!_allowedCharacters.IsUnicodeScalarAllowed(ptr[idx])) - { - goto Return; - } - idx++; - } - else - { - OperationStatus opStatus = UnicodeHelpers.DecodeScalarValueFromUtf8(utf8Text.Slice(idx), out uint nextScalarValue, out int utf8BytesConsumedForScalar); - - Debug.Assert(nextScalarValue <= int.MaxValue); - if (opStatus != OperationStatus.Done || WillEncode((int)nextScalarValue)) - { - goto Return; - } - - Debug.Assert(opStatus == OperationStatus.Done); - idx += utf8BytesConsumedForScalar; - } - } - Debug.Assert(idx == utf8Text.Length); - - idx = -1; // All bytes are allowed. - - Return: - return idx; - } - } - - // The worst case encoding is 6 output chars per input char: [input] U+FFFF -> [output] "\uFFFF" - // We don't need to worry about astral code points since they're represented as encoded - // surrogate pairs in the output. - public override int MaxOutputCharactersPerInputCharacter => 12; // "\uFFFF\uFFFF" is the longest encoded form - - private const string s_b = "\\b"; - private const string s_t = "\\t"; - private const string s_n = "\\n"; - private const string s_f = "\\f"; - private const string s_r = "\\r"; - private const string s_back = "\\\\"; - private const string s_doubleQuote = "\\\""; - - // Writes a scalar value as a JavaScript-escaped character (or sequence of characters). - // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9 - // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 - // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf - public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not. - // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/" - // (in ECMA-262 this character is a NonEscape character); however, we - // don't encode SOLIDUS by default unless the caller has provided an - // explicit bitmap which does not contain it. In this case we'll assume - // that the caller didn't want a SOLIDUS written to the output at all, - // so it should be written using "\u002F" encoding. - // HTML-specific characters (including apostrophe and quotes) will - // be written out as numeric entities for defense-in-depth. - // See UnicodeEncoderBase ctor comments for more info. - - Span destination = new Span(buffer, bufferLength); - if (!WillEncode(unicodeScalar)) - { - return TryWriteScalarAsChar(unicodeScalar, destination, out numberOfCharactersWritten); - } - - string toCopy; - switch (unicodeScalar) - { - case '\"': - toCopy = s_doubleQuote; - break; - case '\b': - toCopy = s_b; - break; - case '\t': - toCopy = s_t; - break; - case '\n': - toCopy = s_n; - break; - case '\f': - toCopy = s_f; - break; - case '\r': - toCopy = s_r; - break; - case '\\': - toCopy = s_back; - break; - default: - return JavaScriptEncoderHelper.TryWriteEncodedScalarAsNumericEntity(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten); - } - return TryCopyCharacters(toCopy, destination, out numberOfCharactersWritten); - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UrlEncoder.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UrlEncoder.cs index 9f6675aa329d8..dba6b96432436 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UrlEncoder.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Encodings/Web/UrlEncoder.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Text.Internal; using System.Text.Unicode; namespace System.Text.Encodings.Web @@ -16,10 +13,7 @@ public abstract class UrlEncoder : TextEncoder /// /// Returns a default built-in instance of . /// - public static UrlEncoder Default - { - get { return DefaultUrlEncoder.Singleton; } - } + public static UrlEncoder Default => DefaultUrlEncoder.BasicLatinSingleton; /// /// Creates a new instance of UrlEncoder with provided settings. @@ -39,149 +33,7 @@ public static UrlEncoder Create(TextEncoderSettings settings) /// Some characters in might still get encoded, i.e. this parameter is just telling the encoder what ranges it is allowed to not encode, not what characters it must not encode. public static UrlEncoder Create(params UnicodeRange[] allowedRanges) { - return new DefaultUrlEncoder(allowedRanges); - } - } - - internal sealed class DefaultUrlEncoder : UrlEncoder - { - private readonly AllowedCharactersBitmap _allowedCharacters; - - internal static readonly DefaultUrlEncoder Singleton = new DefaultUrlEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); - - // We perform UTF8 conversion of input, which means that the worst case is - // 12 output chars per input surrogate char: [input] U+FFFF U+FFFF -> [output] "%XX%YY%ZZ%WW". - public override int MaxOutputCharactersPerInputCharacter - { - get { return 12; } - } - - public DefaultUrlEncoder(TextEncoderSettings filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - _allowedCharacters = filter.GetAllowedCharacters(); - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - _allowedCharacters.ForbidUndefinedCharacters(); - - // Forbid characters that are special in HTML. - // Even though this is a not HTML encoder, - // it's unfortunately common for developers to - // forget to HTML-encode a string once it has been URL-encoded, - // so this offers extra protection. - HtmlEncoderHelper.ForbidHtmlCharacters(_allowedCharacters); - - // Per RFC 3987, Sec. 2.2, we want encodings that are safe for - // four particular components: 'isegment', 'ipath-noscheme', - // 'iquery', and 'ifragment'. The relevant definitions are below. - // - // ipath-noscheme = isegment-nz-nc *( "/" isegment ) - // - // isegment = *ipchar - // - // isegment-nz-nc = 1*( iunreserved / pct-encoded / sub-delims - // / "@" ) - // ; non-zero-length segment without any colon ":" - // - // ipchar = iunreserved / pct-encoded / sub-delims / ":" - // / "@" - // - // iquery = *( ipchar / iprivate / "/" / "?" ) - // - // ifragment = *( ipchar / "/" / "?" ) - // - // iunreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar - // - // ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF - // / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD - // / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD - // / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD - // / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD - // / %xD0000-DFFFD / %xE1000-EFFFD - // - // pct-encoded = "%" HEXDIG HEXDIG - // - // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - // / "*" / "+" / "," / ";" / "=" - // - // The only common characters between these four components are the - // intersection of 'isegment-nz-nc' and 'ipchar', which is really - // just 'isegment-nz-nc' (colons forbidden). - // - // From this list, the base encoder already forbids "&", "'", "+", - // and we'll additionally forbid "=" since it has special meaning - // in x-www-form-urlencoded representations. - // - // This means that the full list of allowed characters from the - // Basic Latin set is: - // ALPHA / DIGIT / "-" / "." / "_" / "~" / "!" / "$" / "(" / ")" / "*" / "," / ";" / "@" - - const string forbiddenChars = @" #%/:=?[\]^`{|}"; // chars from Basic Latin which aren't already disallowed by the base encoder - foreach (char character in forbiddenChars) - { - _allowedCharacters.ForbidCharacter(character); - } - - // Specials (U+FFF0 .. U+FFFF) are forbidden by the definition of 'ucschar' above - for (int i = 0; i < 16; i++) - { - _allowedCharacters.ForbidCharacter((char)(0xFFF0 | i)); - } - } - - public DefaultUrlEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool WillEncode(int unicodeScalar) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) return true; - return !_allowedCharacters.IsUnicodeScalarAllowed(unicodeScalar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe override int FindFirstCharacterToEncode(char* text, int textLength) - { - if (text == null) - { - throw new ArgumentNullException(nameof(text)); - } - return _allowedCharacters.FindFirstCharacterToEncode(text, textLength); - } - - public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - if (buffer == null) - { - throw new ArgumentNullException(nameof(buffer)); - } - - if (!WillEncode(unicodeScalar)) { return TryWriteScalarAsChar(unicodeScalar, new Span(buffer, bufferLength), out numberOfCharactersWritten); } - - numberOfCharactersWritten = 0; - - uint asUtf8 = unchecked((uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)unicodeScalar)); - do - { - if (numberOfCharactersWritten + 3 > bufferLength) - { - numberOfCharactersWritten = 0; - return false; - } - - *buffer = '%'; buffer++; - *buffer = HexConverter.ToCharUpper((int)asUtf8 >> 4); buffer++; - *buffer = HexConverter.ToCharUpper((int)asUtf8); buffer++; - - numberOfCharactersWritten += 3; - } - while ((asUtf8 >>= 8) != 0); - return true; + return new DefaultUrlEncoder(new TextEncoderSettings(allowedRanges)); } } } diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Internal/AllowedCharactersBitmap.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Internal/AllowedCharactersBitmap.cs deleted file mode 100644 index b435bc7829b30..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Internal/AllowedCharactersBitmap.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Text.Unicode; - -namespace System.Text.Internal -{ - internal readonly struct AllowedCharactersBitmap - { - private const int ALLOWED_CHARS_BITMAP_LENGTH = 0x10000 / (8 * sizeof(uint)); - private readonly uint[] _allowedCharacters; - - // should be called in place of the default ctor - public static AllowedCharactersBitmap CreateNew() - { - return new AllowedCharactersBitmap(new uint[ALLOWED_CHARS_BITMAP_LENGTH]); - } - - private AllowedCharactersBitmap(uint[] allowedCharacters) - { - if (allowedCharacters == null) - { - throw new ArgumentNullException(nameof(allowedCharacters)); - } - _allowedCharacters = allowedCharacters; - } - - // Marks a character as allowed (can be returned unencoded) - public void AllowCharacter(char character) - { - int codePoint = character; - int index = codePoint >> 5; - int offset = codePoint & 0x1F; - _allowedCharacters[index] |= 0x1U << offset; - } - - // Marks a character as forbidden (must be returned encoded) - public void ForbidCharacter(char character) - { - int codePoint = character; - int index = codePoint >> 5; - int offset = codePoint & 0x1F; - _allowedCharacters[index] &= ~(0x1U << offset); - } - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - public void ForbidUndefinedCharacters() - { - ReadOnlySpan definedCharactersBitmap = UnicodeHelpers.GetDefinedCharacterBitmap(); - Debug.Assert(definedCharactersBitmap.Length == _allowedCharacters.Length); - for (int i = 0; i < _allowedCharacters.Length; i++) - { - _allowedCharacters[i] &= definedCharactersBitmap[i]; - } - } - - // Marks all characters as forbidden (must be returned encoded) - public void Clear() - { - Array.Clear(_allowedCharacters, 0, _allowedCharacters.Length); - } - - // Creates a deep copy of this bitmap - public AllowedCharactersBitmap Clone() - { - return new AllowedCharactersBitmap((uint[])_allowedCharacters.Clone()); - } - - // Determines whether the given character can be returned unencoded. - public bool IsCharacterAllowed(char character) - { - return IsUnicodeScalarAllowed(character); - } - - // Determines whether the given character can be returned unencoded. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsUnicodeScalarAllowed(int unicodeScalar) - { - Debug.Assert(unicodeScalar < 0x10000); - int index = unicodeScalar >> 5; - int offset = unicodeScalar & 0x1F; - return (_allowedCharacters[index] & (0x1U << offset)) != 0; - } - - public unsafe int FindFirstCharacterToEncode(char* text, int textLength) - { - int i = 0; - - while (i <= textLength - 8) - { - if (!IsCharacterAllowed(text[i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i])) - { - goto Return; - } - i++; - } - - while (i <= textLength - 4) - { - if (!IsCharacterAllowed(text[i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i]) - || !IsCharacterAllowed(text[++i])) - { - goto Return; - } - i++; - } - - while (i < textLength) - { - if (!IsCharacterAllowed(text[i])) - { - goto Return; - } - i++; - } - - i = -1; - - Return: - return i; - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/src/System/Text/Unicode/UnicodeHelpers.cs b/src/libraries/System.Text.Encodings.Web/src/System/Text/Unicode/UnicodeHelpers.cs index 26ae7ec55d495..8b7c4a54cb774 100644 --- a/src/libraries/System.Text.Encodings.Web/src/System/Text/Unicode/UnicodeHelpers.cs +++ b/src/libraries/System.Text.Encodings.Web/src/System/Text/Unicode/UnicodeHelpers.cs @@ -1,385 +1,39 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; -using System.Buffers.Binary; using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System.Text.Unicode { /// /// Contains helpers for dealing with Unicode code points. /// - internal static unsafe partial class UnicodeHelpers + internal static partial class UnicodeHelpers { - /// - /// Used for invalid Unicode sequences or other unrepresentable values. - /// - private const char UNICODE_REPLACEMENT_CHAR = '\uFFFD'; - /// /// The last code point defined by the Unicode specification. /// internal const int UNICODE_LAST_CODEPOINT = 0x10FFFF; - // This field is only used on big-endian architectures. We don't - // bother computing it on little-endian architectures. - private static readonly uint[]? _definedCharacterBitmapBigEndian = (BitConverter.IsLittleEndian) ? null : CreateDefinedCharacterBitmapMachineEndian(); - - private static uint[] CreateDefinedCharacterBitmapMachineEndian() - { - Debug.Assert(!BitConverter.IsLittleEndian); - - // We need to convert little-endian to machine-endian. - - ReadOnlySpan remainingBitmap = DefinedCharsBitmapSpan; - uint[] bigEndianData = new uint[remainingBitmap.Length / sizeof(uint)]; - - for (int i = 0; i < bigEndianData.Length; i++) - { - bigEndianData[i] = BinaryPrimitives.ReadUInt32LittleEndian(remainingBitmap); - remainingBitmap = remainingBitmap.Slice(sizeof(uint)); - } - - return bigEndianData; - } - /// - /// A copy of the logic in Rune.DecodeFromUtf8. + /// Returns a bitmap of all BMP code points as a series of little-endian 32-bit values. + /// On other-endian architectures, caller must convert each 32-bit integer to native endianness + /// before using the data. /// - public static OperationStatus DecodeScalarValueFromUtf8(ReadOnlySpan source, out uint result, out int bytesConsumed) - { - const char ReplacementChar = '\uFFFD'; - - // This method follows the Unicode Standard's recommendation for detecting - // the maximal subpart of an ill-formed subsequence. See The Unicode Standard, - // Ch. 3.9 for more details. In summary, when reporting an invalid subsequence, - // it tries to consume as many code units as possible as long as those code - // units constitute the beginning of a longer well-formed subsequence per Table 3-7. - - int index = 0; - - // Try reading input[0]. - - if ((uint)index >= (uint)source.Length) - { - goto NeedsMoreData; - } - - uint tempValue = source[index]; - if (!UnicodeUtility.IsAsciiCodePoint(tempValue)) - { - goto NotAscii; - } - - Finish: - - bytesConsumed = index + 1; - Debug.Assert(1 <= bytesConsumed && bytesConsumed <= 4); // Valid subsequences are always length [1..4] - result = tempValue; - return OperationStatus.Done; - - NotAscii: - - // Per Table 3-7, the beginning of a multibyte sequence must be a code unit in - // the range [C2..F4]. If it's outside of that range, it's either a standalone - // continuation byte, or it's an overlong two-byte sequence, or it's an out-of-range - // four-byte sequence. - - if (!UnicodeUtility.IsInRangeInclusive(tempValue, 0xC2, 0xF4)) - { - goto FirstByteInvalid; - } - - tempValue = (tempValue - 0xC2) << 6; - - // Try reading input[1]. - - index++; - if ((uint)index >= (uint)source.Length) - { - goto NeedsMoreData; - } - - // Continuation bytes are of the form [10xxxxxx], which means that their two's - // complement representation is in the range [-65..-128]. This allows us to - // perform a single comparison to see if a byte is a continuation byte. - - int thisByteSignExtended = (sbyte)source[index]; - if (thisByteSignExtended >= -64) - { - goto Invalid; - } - - tempValue += (uint)thisByteSignExtended; - tempValue += 0x80; // remove the continuation byte marker - tempValue += (0xC2 - 0xC0) << 6; // remove the leading byte marker - - if (tempValue < 0x0800) - { - Debug.Assert(UnicodeUtility.IsInRangeInclusive(tempValue, 0x0080, 0x07FF)); - goto Finish; // this is a valid 2-byte sequence - } - - // This appears to be a 3- or 4-byte sequence. Since per Table 3-7 we now have - // enough information (from just two code units) to detect overlong or surrogate - // sequences, we need to perform these checks now. - - if (!UnicodeUtility.IsInRangeInclusive(tempValue, ((0xE0 - 0xC0) << 6) + (0xA0 - 0x80), ((0xF4 - 0xC0) << 6) + (0x8F - 0x80))) - { - // The first two bytes were not in the range [[E0 A0]..[F4 8F]]. - // This is an overlong 3-byte sequence or an out-of-range 4-byte sequence. - goto Invalid; - } - - if (UnicodeUtility.IsInRangeInclusive(tempValue, ((0xED - 0xC0) << 6) + (0xA0 - 0x80), ((0xED - 0xC0) << 6) + (0xBF - 0x80))) - { - // This is a UTF-16 surrogate code point, which is invalid in UTF-8. - goto Invalid; - } - - if (UnicodeUtility.IsInRangeInclusive(tempValue, ((0xF0 - 0xC0) << 6) + (0x80 - 0x80), ((0xF0 - 0xC0) << 6) + (0x8F - 0x80))) - { - // This is an overlong 4-byte sequence. - goto Invalid; - } - - // The first two bytes were just fine. We don't need to perform any other checks - // on the remaining bytes other than to see that they're valid continuation bytes. - - // Try reading input[2]. - - index++; - if ((uint)index >= (uint)source.Length) - { - goto NeedsMoreData; - } - - thisByteSignExtended = (sbyte)source[index]; - if (thisByteSignExtended >= -64) - { - goto Invalid; // this byte is not a UTF-8 continuation byte - } - - tempValue <<= 6; - tempValue += (uint)thisByteSignExtended; - tempValue += 0x80; // remove the continuation byte marker - tempValue -= (0xE0 - 0xC0) << 12; // remove the leading byte marker - - if (tempValue <= 0xFFFF) - { - Debug.Assert(UnicodeUtility.IsInRangeInclusive(tempValue, 0x0800, 0xFFFF)); - goto Finish; // this is a valid 3-byte sequence - } - - // Try reading input[3]. - - index++; - if ((uint)index >= (uint)source.Length) - { - goto NeedsMoreData; - } - - thisByteSignExtended = (sbyte)source[index]; - if (thisByteSignExtended >= -64) - { - goto Invalid; // this byte is not a UTF-8 continuation byte - } - - tempValue <<= 6; - tempValue += (uint)thisByteSignExtended; - tempValue += 0x80; // remove the continuation byte marker - tempValue -= (0xF0 - 0xE0) << 18; // remove the leading byte marker - - UnicodeDebug.AssertIsValidSupplementaryPlaneScalar(tempValue); - goto Finish; // this is a valid 4-byte sequence + internal static ReadOnlySpan GetDefinedBmpCodePointsBitmapLittleEndian() => DefinedCharsBitmapSpan; - FirstByteInvalid: - - index = 1; // Invalid subsequences are always at least length 1. - - Invalid: - - Debug.Assert(1 <= index && index <= 3); // Invalid subsequences are always length 1..3 - bytesConsumed = index; - result = ReplacementChar; - return OperationStatus.InvalidData; - - NeedsMoreData: - - Debug.Assert(0 <= index && index <= 3); // Incomplete subsequences are always length 0..3 - bytesConsumed = index; - result = ReplacementChar; - return OperationStatus.NeedMoreData; - } - - /// - /// Returns a bitmap of all characters which are defined per the checked-in version - /// of the Unicode specification. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ReadOnlySpan GetDefinedCharacterBitmap() - { - if (BitConverter.IsLittleEndian) - { - // Underlying data is a series of 32-bit little-endian values and is guaranteed - // properly aligned by the compiler, so we know this is a valid cast byte -> uint. - - return MemoryMarshal.Cast(DefinedCharsBitmapSpan); - } - else - { - // Static compiled data was little-endian; we had to create a big-endian - // representation at runtime. - - return _definedCharacterBitmapBigEndian; - } - } - - /// - /// Given a UTF-16 character stream, reads the next scalar value from the stream. - /// Set 'endOfString' to true if 'pChar' points to the last character in the stream. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int GetScalarValueFromUtf16(char first, char? second, out bool wasSurrogatePair, out bool needsMoreData) - { - if (!char.IsSurrogate(first)) - { - wasSurrogatePair = false; - needsMoreData = false; - return first; - } - - return GetScalarValueFromUtf16Slow(first, second, out wasSurrogatePair, out needsMoreData); - } - - private static int GetScalarValueFromUtf16Slow(char first, char? second, out bool wasSurrogatePair, out bool needMoreData) - { -#if DEBUG - if (!char.IsSurrogate(first)) - { - Debug.Assert(false, "This case should've been handled by the fast path."); - wasSurrogatePair = false; - needMoreData = false; - return first; - } -#endif - if (char.IsHighSurrogate(first)) - { - if (second != null) - { - if (char.IsLowSurrogate(second.Value)) - { - // valid surrogate pair - extract codepoint - wasSurrogatePair = true; - needMoreData = false; - return GetScalarValueFromUtf16SurrogatePair(first, second.Value); - } - else - { - // unmatched surrogate - substitute - wasSurrogatePair = false; - needMoreData = false; - return UNICODE_REPLACEMENT_CHAR; - } - } - else - { - // unmatched surrogate - substitute - wasSurrogatePair = false; - needMoreData = true; // Last character was high surrogate; we need more data. - return UNICODE_REPLACEMENT_CHAR; - } - } - else - { - // unmatched surrogate - substitute - Debug.Assert(char.IsLowSurrogate(first)); - wasSurrogatePair = false; - needMoreData = false; - return UNICODE_REPLACEMENT_CHAR; - } - } - - /// - /// Given a UTF-16 character stream, reads the next scalar value from the stream. - /// Set 'endOfString' to true if 'pChar' points to the last character in the stream. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int GetScalarValueFromUtf16(char* pChar, bool endOfString) - { - // This method is marked as AggressiveInlining to handle the common case of a non-surrogate - // character. The surrogate case is handled in the slower fallback code path. - char thisChar = *pChar; - return (char.IsSurrogate(thisChar)) ? GetScalarValueFromUtf16Slow(pChar, endOfString) : thisChar; - } - - private static int GetScalarValueFromUtf16Slow(char* pChar, bool endOfString) - { - char firstChar = pChar[0]; - - if (!char.IsSurrogate(firstChar)) - { - Debug.Assert(false, "This case should've been handled by the fast path."); - return firstChar; - } - else if (char.IsHighSurrogate(firstChar)) - { - if (endOfString) - { - // unmatched surrogate - substitute - return UNICODE_REPLACEMENT_CHAR; - } - else - { - char secondChar = pChar[1]; - if (char.IsLowSurrogate(secondChar)) - { - // valid surrogate pair - extract codepoint - return GetScalarValueFromUtf16SurrogatePair(firstChar, secondChar); - } - else - { - // unmatched surrogate - substitute - return UNICODE_REPLACEMENT_CHAR; - } - } - } - else - { - // unmatched surrogate - substitute - Debug.Assert(char.IsLowSurrogate(firstChar)); - return UNICODE_REPLACEMENT_CHAR; - } - } - - private static int GetScalarValueFromUtf16SurrogatePair(char highSurrogate, char lowSurrogate) - { - Debug.Assert(char.IsHighSurrogate(highSurrogate)); - Debug.Assert(char.IsLowSurrogate(lowSurrogate)); - - // See https://www.unicode.org/versions/Unicode6.2.0/ch03.pdf, Table 3.5 for the - // details of this conversion. We don't use Char.ConvertToUtf32 because its exception - // handling shows up on the hot path, and our caller has already sanitized the inputs. - return (lowSurrogate & 0x3ff) | (((highSurrogate & 0x3ff) + (1 << 6)) << 10); - } - - internal static void GetUtf16SurrogatePairFromAstralScalarValue(int scalar, out char highSurrogate, out char lowSurrogate) + internal static void GetUtf16SurrogatePairFromAstralScalarValue(uint scalar, out char highSurrogate, out char lowSurrogate) { Debug.Assert(0x10000 <= scalar && scalar <= UNICODE_LAST_CODEPOINT); - // See https://www.unicode.org/versions/Unicode6.2.0/ch03.pdf, Table 3.5 for the - // details of this conversion. We don't use Char.ConvertFromUtf32 because its exception - // handling shows up on the hot path, it allocates temporary strings (which we don't want), - // and our caller has already sanitized the inputs. + UnicodeDebug.AssertIsValidSupplementaryPlaneScalar(scalar); + + // This calculation comes from the Unicode specification, Table 3-5. - int x = scalar & 0xFFFF; - int u = scalar >> 16; - int w = u - 1; - highSurrogate = (char)(0xD800 | (w << 6) | (x >> 10)); - lowSurrogate = (char)(0xDC00 | (x & 0x3FF)); + highSurrogate = (char)((scalar + ((0xD800u - 0x40u) << 10)) >> 10); + lowSurrogate = (char)((scalar & 0x3FFu) + 0xDC00u); } /// @@ -425,21 +79,6 @@ internal static int GetUtf8RepresentationForScalarValue(uint scalar) } } - /// - /// Returns a value stating whether a character is defined per the checked-in version - /// of the Unicode specification. Certain classes of characters (control chars, - /// private use, surrogates, some whitespace) are considered "undefined" for - /// our purposes. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsCharacterDefined(char c) - { - uint codePoint = (uint)c; - int index = (int)(codePoint >> 5); - int offset = (int)(codePoint & 0x1FU); - return ((GetDefinedCharacterBitmap()[index] >> offset) & 0x1U) != 0; - } - /// /// Determines whether the given scalar value is in the supplementary plane and thus /// requires 2 characters to be represented in UTF-16 (as a surrogate pair). @@ -449,24 +88,5 @@ internal static bool IsSupplementaryCodePoint(int scalar) { return ((scalar & ~((int)char.MaxValue)) != 0); } - - /// - /// Returns iff is a UTF-8 continuation byte; - /// i.e., has binary representation 10xxxxxx, where x is any bit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsUtf8ContinuationByte(in byte value) - { - // This API takes its input as a readonly ref so that the JIT can emit "cmp ModRM" statements - // directly rather than bounce a temporary through a register. That is, we want the JIT to be - // able to emit a single "cmp byte ptr [data], C0h" statement if we're querying a memory location - // to see if it's a continuation byte. Data that's already enregistered will go through the - // normal "cmp reg, C0h" code paths, perhaps with some extra unnecessary "movzx" instructions. - // - // The below check takes advantage of the two's complement representation of negative numbers. - // [ 0b1000_0000, 0b1011_1111 ] is [ -127 (sbyte.MinValue), -65 ] - - return ((sbyte)value < -64); - } } } diff --git a/src/libraries/System.Text.Encodings.Web/tests/AllowedAsciiCodePointsTests.cs b/src/libraries/System.Text.Encodings.Web/tests/AllowedAsciiCodePointsTests.cs new file mode 100644 index 0000000000000..6d06ed4149830 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/AllowedAsciiCodePointsTests.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class AllowedAsciiCodePointsTests + { + [Fact] + public void AllowedAsciiCodePointsTestBattery() + { + OptimizedInboxTextEncoder._RunAllowedAsciiCodePointsTestBattery(); + } + } +} + +namespace System.Text.Encodings.Web +{ + internal partial class OptimizedInboxTextEncoder + { + internal static void _RunAllowedAsciiCodePointsTestBattery() + { + // Arrange + // Allow only characters that are multiples of 3 or 7. + + static bool IsValueAllowed(int value) => ((value % 3) == 0) || ((value % 7) == 0); + + var bitmap = new AllowedBmpCodePointsBitmap(); + for (int i = 0; i < 1024; i++) // include C0 controls & characters beyond ASCII range + { + if (IsValueAllowed(i)) { bitmap.AllowChar((char)i); } + } + + // Act + + using BoundedMemory boundedMemory = BoundedMemory.Allocate(1); // use BoundedMemory to detect out-of-bound accesses + ref var allowedAsciiCodePoints = ref boundedMemory.Span[0]; + + allowedAsciiCodePoints.PopulateAllowedCodePoints(bitmap); + boundedMemory.MakeReadonly(); + + // Assert + // Note: We test negative inputs as well to exercise edge cases in memory accesses + + for (int i = -1024; i < 1024; i++) + { + bool expected = UnicodeUtility.IsAsciiCodePoint((uint)i) && !char.IsControl((char)i) && IsValueAllowed(i); + bool actual = allowedAsciiCodePoints.IsAllowedAsciiCodePoint((uint)i); + Assert.Equal(expected, actual); + } + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/AllowedBmpCodePointsBitmapTests.cs b/src/libraries/System.Text.Encodings.Web/tests/AllowedBmpCodePointsBitmapTests.cs new file mode 100644 index 0000000000000..c1a46aff9d109 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/AllowedBmpCodePointsBitmapTests.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Text.Unicode; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class AllowedBmpCodePointsBitmapTests + { + [Fact] + public void Ctor_EmptyByDefault() + { + // Act + var bitmap = new AllowedBmpCodePointsBitmap(); + + // Assert + for (int i = 0; i <= char.MaxValue; i++) + { + Assert.False(bitmap.IsCharAllowed((char)i)); + } + } + + [Fact] + public void Allow_Forbid_ZigZag() + { + // Arrange - we'll use BoundedMemory in this test to guard against + // out-of-bounds accesses on the bitmap instance. + using var boundedMem = BoundedMemory.Allocate(1); + boundedMem.Span.Clear(); + ref var bitmap = ref boundedMem.Span[0]; + + // Act + // The only chars which are allowed are those whose code points are multiples of 3 or 7 + // who aren't also multiples of 5. Exception: multiples of 35 are allowed. + for (int i = 0; i <= char.MaxValue; i += 3) + { + bitmap.AllowChar((char)i); + } + for (int i = 0; i <= char.MaxValue; i += 5) + { + bitmap.ForbidChar((char)i); + } + for (int i = 0; i <= char.MaxValue; i += 7) + { + bitmap.AllowChar((char)i); + } + + // Assert + for (int i = 0; i <= char.MaxValue; i++) + { + bool isAllowed = false; + if (i % 3 == 0) { isAllowed = true; } + if (i % 5 == 0) { isAllowed = false; } + if (i % 7 == 0) { isAllowed = true; } + Assert.Equal(isAllowed, bitmap.IsCharAllowed((char)i)); + Assert.Equal(isAllowed, bitmap.IsCodePointAllowed((uint)i)); + } + } + + [Fact] + public void CopyByVal_MakesDeepCopy() + { + // Arrange + var originalBitmap = new AllowedBmpCodePointsBitmap(); + originalBitmap.AllowChar('x'); + + // Act + var clonedBitmap = originalBitmap; // struct byval copy + clonedBitmap.AllowChar('y'); + + // Assert + Assert.True(originalBitmap.IsCharAllowed('x')); + Assert.False(originalBitmap.IsCharAllowed('y')); + Assert.True(clonedBitmap.IsCharAllowed('x')); + Assert.True(clonedBitmap.IsCharAllowed('y')); + } + + [Fact] + public void ForbidUndefinedCharacters_RemovesUndefinedChars() + { + // Arrange + // We only allow odd-numbered characters in this test so that + // we can validate that we properly merged the two bitmaps together + // rather than simply overwriting the target. + var bitmap = new AllowedBmpCodePointsBitmap(); + for (int i = 1; i <= char.MaxValue; i += 2) + { + bitmap.AllowChar((char)i); + } + + // Act + bitmap.ForbidUndefinedCharacters(); + + // Assert + for (int i = 0; i <= char.MaxValue; i++) + { + if (i % 2 == 0) + { + Assert.False(bitmap.IsCharAllowed((char)i)); // these chars were never allowed in the original description + } + else + { + Assert.Equal(UnicodeTestHelpers.IsCharacterDefined((char)i), bitmap.IsCharAllowed((char)i)); + } + } + } + + [Fact] + public void IsCodePointAllowed_NonBmpCodePoints_ReturnsFalse() + { + // Arrange - we'll use BoundedMemory in this test to guard against + // out-of-bounds accesses on the bitmap instance. + using var boundedMem = BoundedMemory.Allocate(1); + ref var bitmap = ref boundedMem.Span[0]; + + Assert.False(bitmap.IsCodePointAllowed(0x10000)); // start of supplementary plane + Assert.False(bitmap.IsCodePointAllowed(0x10FFFF)); // end of supplementary plane + Assert.False(bitmap.IsCodePointAllowed(0x110000)); + Assert.False(bitmap.IsCodePointAllowed(uint.MaxValue)); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/AllowedCharsBitmapTests.cs b/src/libraries/System.Text.Encodings.Web/tests/AllowedCharsBitmapTests.cs deleted file mode 100644 index 8e8b426c87c61..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/AllowedCharsBitmapTests.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Text.Internal; -using System.Text.Unicode; -using Xunit; - -namespace System.Text.Encodings.Web.Tests -{ - public class AllowedCharsBitmapTests - { - [Fact] - public void Ctor_EmptyByDefault() - { - // Act - var bitmap = AllowedCharactersBitmap.CreateNew(); - - // Assert - for (int i = 0; i <= char.MaxValue; i++) - { - Assert.False(bitmap.IsCharacterAllowed((char)i)); - } - } - - [Fact] - public void Allow_Forbid_ZigZag() - { - // Arrange - var bitmap = AllowedCharactersBitmap.CreateNew(); - - // Act - // The only chars which are allowed are those whose code points are multiples of 3 or 7 - // who aren't also multiples of 5. Exception: multiples of 35 are allowed. - for (int i = 0; i <= char.MaxValue; i += 3) - { - bitmap.AllowCharacter((char)i); - } - for (int i = 0; i <= char.MaxValue; i += 5) - { - bitmap.ForbidCharacter((char)i); - } - for (int i = 0; i <= char.MaxValue; i += 7) - { - bitmap.AllowCharacter((char)i); - } - - // Assert - for (int i = 0; i <= char.MaxValue; i++) - { - bool isAllowed = false; - if (i % 3 == 0) { isAllowed = true; } - if (i % 5 == 0) { isAllowed = false; } - if (i % 7 == 0) { isAllowed = true; } - Assert.Equal(isAllowed, bitmap.IsCharacterAllowed((char)i)); - } - } - - [Fact] - public void Clear_ForbidsEverything() - { - // Arrange - var bitmap = AllowedCharactersBitmap.CreateNew(); - for (int i = 1; i <= char.MaxValue; i++) - { - bitmap.AllowCharacter((char)i); - } - - // Act - bitmap.Clear(); - - // Assert - for (int i = 0; i <= char.MaxValue; i++) - { - Assert.False(bitmap.IsCharacterAllowed((char)i)); - } - } - - [Fact] - public void Clone_MakesDeepCopy() - { - // Arrange - var originalBitmap = AllowedCharactersBitmap.CreateNew(); - originalBitmap.AllowCharacter('x'); - - // Act - var clonedBitmap = originalBitmap.Clone(); - clonedBitmap.AllowCharacter('y'); - - // Assert - Assert.True(originalBitmap.IsCharacterAllowed('x')); - Assert.False(originalBitmap.IsCharacterAllowed('y')); - Assert.True(clonedBitmap.IsCharacterAllowed('x')); - Assert.True(clonedBitmap.IsCharacterAllowed('y')); - } - - [Fact] - public void ForbidUndefinedCharacters_RemovesUndefinedChars() - { - // Arrange - // We only allow odd-numbered characters in this test so that - // we can validate that we properly merged the two bitmaps together - // rather than simply overwriting the target. - var bitmap = AllowedCharactersBitmap.CreateNew(); - for (int i = 1; i <= char.MaxValue; i += 2) - { - bitmap.AllowCharacter((char)i); - } - - // Act - bitmap.ForbidUndefinedCharacters(); - - // Assert - for (int i = 0; i <= char.MaxValue; i++) - { - if (i % 2 == 0) - { - Assert.False(bitmap.IsCharacterAllowed((char)i)); // these chars were never allowed in the original description - } - else - { - Assert.Equal(UnicodeHelpers.IsCharacterDefined((char)i), bitmap.IsCharacterAllowed((char)i)); - } - } - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/AsciiByteMapTests.cs b/src/libraries/System.Text.Encodings.Web/tests/AsciiByteMapTests.cs new file mode 100644 index 0000000000000..3b05e4c20ab4a --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/AsciiByteMapTests.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class AsciiByteMapTests + { + [Fact] + public void Ctor_EmptyByDefault() + { + // Act + var byteMap = new AsciiByteMap(); + + // Assert + for (int i = 0; i < 128; i++) + { + Assert.False(byteMap.TryLookup(new Rune(i), out _)); + } + } + + [Fact] + public void MapEntries_ZigZag() + { + // Arrange - we'll use BoundedMemory in this test to guard against + // out-of-bounds accesses on the byte map instance. + using var boundedMem = BoundedMemory.Allocate(1); + boundedMem.Span.Clear(); + ref var byteMap = ref boundedMem.Span[0]; + + // Act + // All chars which are multiples of 3 or 7 will be mapped to their one's complement inverse. + for (int i = 0; i < 128; i += 3) + { + byteMap.InsertAsciiChar((char)i, (byte)(~i)); + } + for (int i = 0; i < 128; i += 7) + { + byteMap.InsertAsciiChar((char)i, (byte)(~i)); + } + + // Assert + for (int i = 0; i < 128; i++) + { + if ((i % 3) == 0 || (i % 7) == 0) + { + byte expectedValue = (byte)(~i); // maps to its inverse + Assert.True(byteMap.TryLookup(new Rune(i), out byte actualValue)); + Assert.Equal(expectedValue, actualValue); + } + else + { + Assert.False(byteMap.TryLookup(new Rune(i), out _)); + } + } + } + + [Fact] + public void TryLookup_NonAsciiCodePoints_ReturnsFalse() + { + // Arrange - we'll use BoundedMemory in this test to guard against + // out-of-bounds accesses on the bitmap instance. + using var boundedMem = BoundedMemory.Allocate(1); + ref var byteMap = ref boundedMem.Span[0]; + + Assert.False(byteMap.TryLookup(new Rune(128), out _)); // start of non-ASCII + Assert.False(byteMap.TryLookup(new Rune(0xFFFF), out _)); // end of BMP + Assert.False(byteMap.TryLookup(new Rune(0x10000), out _)); // start of supplementary planes + Assert.False(byteMap.TryLookup(new Rune(0x10FFFF), out _)); // end of supplementary planes + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/AsciiPreescapedDataTests.cs b/src/libraries/System.Text.Encodings.Web/tests/AsciiPreescapedDataTests.cs new file mode 100644 index 0000000000000..cf298eb7e20f3 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/AsciiPreescapedDataTests.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class AsciiPreescapedDataTests + { + [Fact] + public void AllowedAsciiCodePointsTestBattery() + { + OptimizedInboxTextEncoder._RunAsciiPreescapedDataTestBattery(); + } + } +} + +namespace System.Text.Encodings.Web +{ + internal partial class OptimizedInboxTextEncoder + { + private static readonly ulong[] _expected = new ulong[] + { + 0x01_00_00_00_00_00_00_41, // "A......1" (where . = 0x00) + 0x02_00_00_00_00_00_62_61, // "ab.....2" + 0x03_00_00_00_00_45_44_43, // "CDE....3" + 0x04_00_00_00_66_65_64_63, // "cdef...4" + 0x05_00_00_4B_4A_49_48_47, // "GHIJK..5" + 0x06_00_6C_6B_6A_69_68_67, // "ghijkl.6" + }; + + internal static void _RunAsciiPreescapedDataTestBattery() + { + // Arrange + // Allow only characters that are *not* multiples of 7 (relatively prime to 6, ensuring every index of our test array is hit) + + static bool IsValueAllowed(int value) => (value % 7) != 0; + + var bitmap = new AllowedBmpCodePointsBitmap(); + for (int i = 0; i < 1024; i++) // include C0 controls & characters beyond ASCII range + { + if (IsValueAllowed(i)) { bitmap.AllowChar((char)i); } + } + + using BoundedMemory boundedMemory = BoundedMemory.Allocate(1); // use BoundedMemory to detect out-of-bound accesses + ref var preescapedData = ref boundedMemory.Span[0]; + preescapedData.PopulatePreescapedData(bitmap, new TestEscaper()); + boundedMemory.MakeReadonly(); + + // Assert + // Try ASCII chars first + + ulong preescapedEntry; + for (int i = 0; i < 128; i++) + { + ulong iterExpected; + bool mustEscape = char.IsControl((char)i) || !IsValueAllowed(i); + if (mustEscape) + { + iterExpected = _expected[(uint)i % _expected.Length]; // char must be escaped, look up which value we're expecting + } + else + { + iterExpected = (0x01ul << 56) + (uint)i; // 0x01_00_00_00_00_00_00_XX, meaning char can go unescaped + } + Assert.True(preescapedData.TryGetPreescapedData((uint)i, out preescapedEntry), "All ASCII code points must return true."); + Assert.Equal(iterExpected, preescapedEntry); + } + + // Some known test cases + + Assert.True(preescapedData.TryGetPreescapedData('L' /* = 76 dec, not multiple of 7, allowed */, out preescapedEntry)); + Assert.Equal(0x01_00_00_00_00_00_00_4Cul /* "1......L" */, preescapedEntry); + Assert.True(preescapedData.TryGetPreescapedData('M' /* = 77 dec, multiple of 7, disallowed (use index 77 % 6 = 5) */, out preescapedEntry)); + Assert.Equal(0x06_00_6C_6B_6A_69_68_67ul /* "ghijkl.6" */, preescapedEntry); + Assert.True(preescapedData.TryGetPreescapedData('N' /* = 78 dec, not multiple of 7, allowed */, out preescapedEntry)); + Assert.Equal(0x01_00_00_00_00_00_00_4Eul /* "N......1" */, preescapedEntry); + + // And try some non-ASCII edge cases, all of which must return false + + Assert.False(preescapedData.TryGetPreescapedData(128, out _)); + Assert.False(preescapedData.TryGetPreescapedData(256, out _)); + Assert.False(preescapedData.TryGetPreescapedData(char.MaxValue, out _)); + Assert.False(preescapedData.TryGetPreescapedData(char.MaxValue + 1, out _)); + Assert.False(preescapedData.TryGetPreescapedData(int.MaxValue, out _)); + Assert.False(preescapedData.TryGetPreescapedData((uint)int.MaxValue + 1, out _)); + Assert.False(preescapedData.TryGetPreescapedData(uint.MaxValue, out _)); + } + + private class TestEscaper : ScalarEscaperBase + { + // tests the different lengths from 0 - 6 + private static readonly string[] _encodings = new string[] + { + "A", + "ab", + "CDE", + "cdef", + "GHIJK", + "ghijkl", + }; + + internal override int EncodeUtf16(Rune value, Span destination) + { + string encoding = _encodings[value.Value % _encodings.Length]; + encoding.AsSpan().CopyTo(destination); + return encoding.Length; + } + + internal override int EncodeUtf8(Rune value, Span destination) => throw new NotImplementedException(); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/ConfigurableScalarTextEncoder.cs b/src/libraries/System.Text.Encodings.Web/tests/ConfigurableScalarTextEncoder.cs index e592f25c1441e..0f948fcff2405 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/ConfigurableScalarTextEncoder.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/ConfigurableScalarTextEncoder.cs @@ -17,9 +17,59 @@ public ConfigurableScalarTextEncoder(Predicate isScalarAllowed) _isScalarAllowed = isScalarAllowed; } - public override int MaxOutputCharactersPerInputCharacter => throw new NotImplementedException(); + public override int MaxOutputCharactersPerInputCharacter => 8; // "[10FFFF]".Length - public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) => throw new NotImplementedException(); + public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) + => FindFirstCharacterToEncode(new ReadOnlySpan(text, textLength)); + + private int FindFirstCharacterToEncode(ReadOnlySpan span) + { + int originalLength = span.Length; + + while (!span.IsEmpty) + { + if (!TryGetNextScalarValue(span, out int scalarValue) || !_isScalarAllowed(scalarValue)) + { + return originalLength - span.Length; // couldn't extract scalar or failed predicate + } + + span = span.Slice(UnicodeUtility.GetUtf16SequenceLength((uint)scalarValue)); + } + + return -1; // entire span was consumed + } + + private static bool TryGetNextScalarValue(ReadOnlySpan span, out int scalarValue) + { + if (!span.IsEmpty) + { + // non-surrogate char? + char firstChar = span[0]; + if (!char.IsSurrogate(firstChar)) + { + scalarValue = firstChar; + return true; + } + + // well-formed surrogate pair? + if (char.IsHighSurrogate(firstChar)) + { + if (span.Length > 1) + { + char secondChar = span[1]; + if (char.IsLowSurrogate(secondChar)) + { + scalarValue = char.ConvertToUtf32(firstChar, secondChar); + return true; + } + } + } + } + + // if we got to this point, span was empty or ill-formed surrogate found + scalarValue = default; + return false; + } public override bool WillEncode(int unicodeScalar) => !_isScalarAllowed(unicodeScalar); diff --git a/src/libraries/System.Text.Encodings.Web/tests/EncoderCommonTests.cs b/src/libraries/System.Text.Encodings.Web/tests/EncoderCommonTests.cs index 239a6299d407f..8169576630ae3 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/EncoderCommonTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/EncoderCommonTests.cs @@ -13,6 +13,7 @@ public class EncoderCommonTests [InlineData(5000, 3, 15000)] // haven't exceeded the 16k cap [InlineData(40000, 3, 40000)] // if we spill over the LOH, we still allocate an output buffer equivalent in length to the input buffer [InlineData(512, int.MaxValue, 16 * 1024)] // make sure we can handle numeric overflow + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void GetCapacityOfOutputStringBuilder(int numCharsToEncode, int worstCaseOutputCharsPerInputChar, int expectedResult) { Assert.Equal(expectedResult, EncoderCommon.GetCapacityOfOutputStringBuilder(numCharsToEncode, worstCaseOutputCharsPerInputChar)); diff --git a/src/libraries/System.Text.Encodings.Web/tests/EncoderExtensionsTests.cs b/src/libraries/System.Text.Encodings.Web/tests/EncoderExtensionsTests.cs index d60bfba1a7161..5f4f5ca0213c2 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/EncoderExtensionsTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/EncoderExtensionsTests.cs @@ -11,12 +11,6 @@ namespace System.Text.Encodings.Web { public class EncoderExtensionsTests { - [Fact] - public void HtmlEncode_ParameterChecks() - { - Assert.Throws(() => EncoderExtensions.HtmlEncode(null, "Hello!", new StringWriter())); - } - [Fact] public void HtmlEncode_PositiveTestCase() { @@ -60,9 +54,9 @@ public void HtmlEncode_CreateNullSettings() [Fact] - public unsafe void TryEncodeUnicodeScalar_Null_Buffer() + public unsafe void TryEncodeUnicodeScalar_NegativeLengthBuffer() { - Assert.Throws("buffer", () => HtmlEncoder.Default.TryEncodeUnicodeScalar(2, null, 1, out int _)); + Assert.Throws(() => HtmlEncoder.Default.TryEncodeUnicodeScalar(2, null, -1, out int _)); } [Fact] @@ -75,31 +69,19 @@ public unsafe void TryEncodeUnicodeScalar_InsufficientRoom() } [Fact] - public void JavaScriptStringEncode_ParameterChecks() - { - Assert.Throws(() => EncoderExtensions.JavaScriptStringEncode(null, "Hello!", new StringWriter())); - } - - [Fact] - public void JavaScriptStringEncode_PositiveTestCase() + public void JavaScriptEncode_PositiveTestCase() { // Arrange - IJavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.All); + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.All); StringWriter writer = new StringWriter(); // Act - encoder.JavaScriptStringEncode("Hello+there!", writer); + encoder.Encode(writer, "Hello+there!"); // Assert Assert.Equal(@"Hello\u002Bthere!", writer.ToString()); } - [Fact] - public void UrlEncode_ParameterChecks() - { - Assert.Throws(() => EncoderExtensions.UrlEncode(null, "Hello!", new StringWriter())); - } - [Fact] public void UrlEncode_PositiveTestCase() { diff --git a/src/libraries/System.Text.Encodings.Web/tests/Extensions.cs b/src/libraries/System.Text.Encodings.Web/tests/Extensions.cs index 4895b7540b624..682e0426ee2dd 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/Extensions.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/Extensions.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -10,6 +9,22 @@ namespace System.Text.Encodings.Web.Tests { public static class Extensions { + public unsafe static int FindFirstCharacterToEncodeUtf16(this TextEncoder encoder, ReadOnlySpan text) + { + if (text.IsEmpty) + { + char dummy = default; + return encoder.FindFirstCharacterToEncode(&dummy, 0); + } + else + { + fixed (char* pText = text) + { + return encoder.FindFirstCharacterToEncode(pText, text.Length); + } + } + } + public static string[] ReadAllLines(this TextReader reader) { return ReadAllLinesImpl(reader).ToArray(); diff --git a/src/libraries/System.Text.Encodings.Web/tests/HtmlEncoderTests.cs b/src/libraries/System.Text.Encodings.Web/tests/HtmlEncoderTests.cs index 074bb3977cd30..0621653036a48 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/HtmlEncoderTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/HtmlEncoderTests.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Globalization; using System.IO; using System.Text.Unicode; @@ -17,13 +16,14 @@ public class HtmlEncoderTests [InlineData("😂 21", "\U0001F602 21")] [InlineData("x😂y", "x\U0001F602y")] [InlineData("😂x😂y", "\U0001F602x\U0001F602y")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void TestSurrogate(string expected, string actual) { - Assert.Equal(expected, System.Text.Encodings.Web.HtmlEncoder.Default.Encode(actual)); + Assert.Equal(expected, HtmlEncoder.Default.Encode(actual)); using (var writer = new StringWriter()) { - System.Text.Encodings.Web.HtmlEncoder.Default.Encode(writer, actual); + HtmlEncoder.Default.Encode(writer, actual); Assert.Equal(expected, writer.GetStringBuilder().ToString()); } } @@ -35,47 +35,47 @@ public void Ctor_WithTextEncoderSettings() var filter = new TextEncoderSettings(); filter.AllowCharacters('a', 'b'); filter.AllowCharacters('\0', '&', '\uFFFF', 'd'); - HtmlEncoder encoder = new HtmlEncoder(filter); + HtmlEncoder encoder = HtmlEncoder.Create(filter); // Act & assert - Assert.Equal("a", encoder.HtmlEncode("a")); - Assert.Equal("b", encoder.HtmlEncode("b")); - Assert.Equal("c", encoder.HtmlEncode("c")); - Assert.Equal("d", encoder.HtmlEncode("d")); - Assert.Equal("�", encoder.HtmlEncode("\0")); // we still always encode control chars - Assert.Equal("&", encoder.HtmlEncode("&")); // we still always encode HTML-special chars - Assert.Equal("￿", encoder.HtmlEncode("\uFFFF")); // we still always encode non-chars and other forbidden chars + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal("b", encoder.Encode("b")); + Assert.Equal("c", encoder.Encode("c")); + Assert.Equal("d", encoder.Encode("d")); + Assert.Equal("�", encoder.Encode("\0")); // we still always encode control chars + Assert.Equal("&", encoder.Encode("&")); // we still always encode HTML-special chars + Assert.Equal("￿", encoder.Encode("\uFFFF")); // we still always encode non-chars and other forbidden chars } [Fact] public void Ctor_WithUnicodeRanges() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); + HtmlEncoder encoder = HtmlEncoder.Create(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); // Act & assert - Assert.Equal("a", encoder.HtmlEncode("a")); - Assert.Equal("\u00E9", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal("\u2601", encoder.HtmlEncode("\u2601" /* CLOUD */)); + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal("\u00E9", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); + Assert.Equal("\u2601", encoder.Encode("\u2601" /* CLOUD */)); } [Fact] - public void Ctor_WithNoParameters_DefaultsToBasicLatin() + public void DefaultFactory_IsBasicLatin() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(); + HtmlEncoder encoder = HtmlEncoder.Default; // Act & assert - Assert.Equal("a", encoder.HtmlEncode("a")); - Assert.Equal("é", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal("☁", encoder.HtmlEncode("\u2601" /* CLOUD */)); + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal("é", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); + Assert.Equal("☁", encoder.Encode("\u2601" /* CLOUD */)); } [Fact] public void Default_EquivalentToBasicLatin() { // Arrange - HtmlEncoder controlEncoder = new HtmlEncoder(UnicodeRanges.BasicLatin); + HtmlEncoder controlEncoder = HtmlEncoder.Create(UnicodeRanges.BasicLatin); HtmlEncoder testEncoder = HtmlEncoder.Default; // Act & assert @@ -84,7 +84,7 @@ public void Default_EquivalentToBasicLatin() if (!IsSurrogateCodePoint(i)) { string input = new string((char)i, 1); - Assert.Equal(controlEncoder.HtmlEncode(input), testEncoder.HtmlEncode(input)); + Assert.Equal(controlEncoder.Encode(input), testEncoder.Encode(input)); } } } @@ -96,13 +96,14 @@ public void Default_EquivalentToBasicLatin() [InlineData("'", "'")] [InlineData("\"", """)] [InlineData("+", "+")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string input, string expected) { // Arrange - HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); + HtmlEncoder encoder = HtmlEncoder.Create(UnicodeRanges.All); // Act - string retVal = encoder.HtmlEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); @@ -112,7 +113,7 @@ public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); + HtmlEncoder encoder = HtmlEncoder.Create(UnicodeRanges.All); // Act & assert - BMP chars for (int i = 0; i <= 0xFFFF; i++) @@ -121,7 +122,7 @@ public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() string expected; if (IsSurrogateCodePoint(i)) { - expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char + expected = "�"; // unpaired surrogate -> Unicode replacement char (escaped) } else { @@ -140,7 +141,7 @@ public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() { mustEncode = true; // control char } - else if (!UnicodeHelpers.IsCharacterDefined((char)i)) + else if (!UnicodeTestHelpers.IsCharacterDefined((char)i)) { mustEncode = true; // undefined (or otherwise disallowed) char } @@ -156,7 +157,7 @@ public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() } } - string retVal = encoder.HtmlEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } @@ -165,7 +166,7 @@ public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() { string input = char.ConvertFromUtf32(i); string expected = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i); - string retVal = encoder.HtmlEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } } @@ -174,14 +175,14 @@ public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() public void HtmlEncode_BadSurrogates_ReturnsUnicodeReplacementChar() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // allow all codepoints + HtmlEncoder encoder = HtmlEncoder.Create(UnicodeRanges.All); // allow all codepoints // "abcde" const string input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; - const string expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD𐏿e\uFFFD"; + const string expected = "a�b�c��d�𐏿e�"; // Act - string retVal = encoder.HtmlEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); @@ -191,64 +192,64 @@ public void HtmlEncode_BadSurrogates_ReturnsUnicodeReplacementChar() public void HtmlEncode_EmptyStringInput_ReturnsEmptyString() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(); + HtmlEncoder encoder = HtmlEncoder.Default; // Act & assert - Assert.Equal("", encoder.HtmlEncode("")); + Assert.Equal("", encoder.Encode("")); } [Fact] public void HtmlEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(); + HtmlEncoder encoder = HtmlEncoder.Default; string input = "Hello, there!"; // Act & assert - Assert.Same(input, encoder.HtmlEncode(input)); + Assert.Same(input, encoder.Encode(input)); } [Fact] public void HtmlEncode_NullInput_Throws() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(); - Assert.Throws(() => { encoder.HtmlEncode(null); }); + HtmlEncoder encoder = HtmlEncoder.Default; + Assert.Throws(() => { encoder.Encode(null); }); } [Fact] public void HtmlEncode_WithCharsRequiringEncodingAtBeginning() { - Assert.Equal("&Hello, there!", new HtmlEncoder().HtmlEncode("&Hello, there!")); + Assert.Equal("&Hello, there!", HtmlEncoder.Default.Encode("&Hello, there!")); } [Fact] public void HtmlEncode_WithCharsRequiringEncodingAtEnd() { - Assert.Equal("Hello, there!&", new HtmlEncoder().HtmlEncode("Hello, there!&")); + Assert.Equal("Hello, there!&", HtmlEncoder.Default.Encode("Hello, there!&")); } [Fact] public void HtmlEncode_WithCharsRequiringEncodingInMiddle() { - Assert.Equal("Hello, &there!", new HtmlEncoder().HtmlEncode("Hello, &there!")); + Assert.Equal("Hello, &there!", HtmlEncoder.Default.Encode("Hello, &there!")); } [Fact] public void HtmlEncode_WithCharsRequiringEncodingInterspersed() { - Assert.Equal("Hello, <there>!", new HtmlEncoder().HtmlEncode("Hello, !")); + Assert.Equal("Hello, <there>!", HtmlEncoder.Default.Encode("Hello, !")); } [Fact] public void HtmlEncode_CharArray() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(); + HtmlEncoder encoder = HtmlEncoder.Default; var output = new StringWriter(); // Act - encoder.HtmlEncode("Hello+world!".ToCharArray(), 3, 5, output); + encoder.Encode(output, "Hello+world!".ToCharArray(), 3, 5); // Assert Assert.Equal("lo+wo", output.ToString()); @@ -258,11 +259,11 @@ public void HtmlEncode_CharArray() public void HtmlEncode_StringSubstring() { // Arrange - HtmlEncoder encoder = new HtmlEncoder(); + HtmlEncoder encoder = HtmlEncoder.Default; var output = new StringWriter(); // Act - encoder.HtmlEncode("Hello+world!", 3, 5, output); + encoder.Encode(output, "Hello+world!", 3, 5); // Assert Assert.Equal("lo+wo", output.ToString()); @@ -276,9 +277,9 @@ public void HtmlEncode_AstralWithTextWriter() using (StreamWriter sw = new StreamWriter(ms)) { string input = "\U0010FFFF"; - System.Text.Encodings.Web.HtmlEncoder.Default.Encode(sw, input); + HtmlEncoder.Default.Encode(sw, input); } - Assert.Equal("􏿿", System.Text.Encoding.UTF8.GetString(buffer)); + Assert.Equal("􏿿", Encoding.UTF8.GetString(buffer)); } private static bool IsSurrogateCodePoint(int codePoint) diff --git a/src/libraries/System.Text.Encodings.Web/tests/IHtmlEncoder.cs b/src/libraries/System.Text.Encodings.Web/tests/IHtmlEncoder.cs deleted file mode 100644 index 0087bc6851385..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/IHtmlEncoder.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; - -namespace System.Text.Encodings.Web.Tests -{ - /// - /// Provides services for HTML-encoding input. - /// - internal interface IHtmlEncoder - { - /// - /// HTML-encodes a character array and writes the result to the supplied - /// output. - /// - /// - /// The encoded value is also appropriately encoded for inclusion inside an HTML attribute - /// as long as the attribute value is surrounded by single or double quotes. - /// - void HtmlEncode(char[] value, int startIndex, int characterCount, TextWriter output); - - /// - /// HTML-encodes a given input string. - /// - /// - /// The HTML-encoded value, or null if the input string was null. - /// - /// - /// The return value is also appropriately encoded for inclusion inside an HTML attribute - /// as long as the attribute value is surrounded by single or double quotes. - /// - string HtmlEncode(string value); - - /// - /// HTML-encodes a given input string and writes the result to the - /// supplied output. - /// - /// - /// The encoded value is also appropriately encoded for inclusion inside an HTML attribute - /// as long as the attribute value is surrounded by single or double quotes. - /// - void HtmlEncode(string value, int startIndex, int characterCount, TextWriter output); - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/IJavaScriptStringEncoder.cs b/src/libraries/System.Text.Encodings.Web/tests/IJavaScriptStringEncoder.cs deleted file mode 100644 index 6d1c3972bbda1..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/IJavaScriptStringEncoder.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; - -namespace System.Text.Encodings.Web.Tests -{ - /// - /// Provides services for JavaScript-escaping strings. - /// - internal interface IJavaScriptStringEncoder - { - /// - /// JavaScript-escapes a character array and writes the result to the - /// supplied output. - /// - /// - /// The encoded value is appropriately encoded for inclusion inside a quoted JSON string. - /// - void JavaScriptStringEncode(char[] value, int startIndex, int characterCount, TextWriter output); - - /// - /// JavaScript-escapes a given input string. - /// - /// - /// The JavaScript-escaped value, or null if the input string was null. - /// The encoded value is appropriately encoded for inclusion inside a quoted JSON string. - /// - string JavaScriptStringEncode(string value); - - /// - /// JavaScript-escapes a given input string and writes the - /// result to the supplied output. - /// - /// - /// The encoded value is appropriately encoded for inclusion inside a quoted JSON string. - /// - void JavaScriptStringEncode(string value, int startIndex, int characterCount, TextWriter output); - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/IUrlEncoder.cs b/src/libraries/System.Text.Encodings.Web/tests/IUrlEncoder.cs deleted file mode 100644 index 82a40a7ded4a2..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/IUrlEncoder.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; - -namespace System.Text.Encodings.Web.Tests -{ - /// - /// Provides services for URL-escaping strings. - /// - internal interface IUrlEncoder - { - /// - /// URL-escapes a character array and writes the result to the supplied - /// output. - /// - /// - /// The encoded value is appropriately encoded for inclusion in the segment, query, or - /// fragment portion of a URI. - /// - void UrlEncode(char[] value, int startIndex, int characterCount, TextWriter output); - - /// - /// URL-escapes a given input string. - /// - /// - /// The URL-escaped value, or null if the input string was null. - /// - /// - /// The return value is appropriately encoded for inclusion in the segment, query, or - /// fragment portion of a URI. - /// - string UrlEncode(string value); - - /// - /// URL-escapes a string and writes the result to the supplied output. - /// - /// - /// The encoded value is appropriately encoded for inclusion in the segment, query, or - /// fragment portion of a URI. - /// - void UrlEncode(string value, int startIndex, int characterCount, TextWriter output); - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/InboxEncoderCommonTests.cs b/src/libraries/System.Text.Encodings.Web/tests/InboxEncoderCommonTests.cs new file mode 100644 index 0000000000000..79905d6ff92a3 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/InboxEncoderCommonTests.cs @@ -0,0 +1,962 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Linq; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class HtmlEncoderDefaultCommonTests : InboxEncoderCommonTestBase + { + public HtmlEncoderDefaultCommonTests() + : base(HtmlEncoder.Default, allowedChar: 'a', disallowedChar: '&') + { + } + + private protected override string GetExpectedEscapedRepresentation(Rune value) + { + switch (value.Value) + { + case '<': return "<"; + case '>': return ">"; + case '&': return "&"; + case '\"': return """; + default: + return FormattableString.Invariant($"&#x{(uint)value.Value:X};"); + } + } + + [Fact] + public void EncodeUtf16_Battery() + { + string[] inputs = new string[] + { + "\n", + "<", + "a", + "\u0234", // U+0234 LATIN SMALL LETTER L WITH CURL + "\ud800", // standalone high surrogate + "\U0001F415", // U+1F415 DOG + "\udfff", // standalone low surrogate + "\uFFFF", // end of BMP range + "\U00010000", // beginning of supplementary range + "\U0010FFFF", // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + " ", + "<", + "a", + "ȴ", + "�", // replaced standalone high surrogate with replacement char + "🐕", + "�", // replaced standalone low surrogate with replacement char + "￿", + "𐀀", + "􏿿", + }; + + _RunEncodeUtf16_Battery(inputs, expectedOutputs); + } + + [Fact] + public void EncodeUtf8_Battery() + { + byte[][] inputs = new byte[][] + { + new byte[] { (byte)'\n' }, + new byte[] { (byte)'<' }, + new byte[] { (byte)'a' }, + new byte[] { 0xC8, 0xB4 }, // U+0234 LATIN SMALL LETTER L WITH CURL + new byte[] { 0xFF }, // invalid byte + new byte[] { 0xF0, 0x9F, 0x90, 0x95 }, // U+1F415 DOG + new byte[] { 0x80 }, // standalone continuation character + new byte[] { 0xC2 }, // standalone multi-byte sequence marker + new byte[] { 0xEF, 0xBF, 0xBF }, // end of BMP range + new byte[] { 0xF0, 0x90, 0x80, 0x80 }, // beginning of supplementary range + new byte[] { 0xF4, 0x8F, 0xBF, 0xBF }, // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + " ", + "<", + "a", + "ȴ", + "�", // replaced invalid byte + "🐕", + "�", // replaced standalone continuation char + "�", // replaced standalone multi-byte sequence marker + "￿", + "𐀀", + "􏿿", + }; + + _RunEncodeUtf8_Battery(inputs, expectedOutputs); + } + } + + public class JavaScriptEncoderDefaultCommonTests : InboxEncoderCommonTestBase + { + public JavaScriptEncoderDefaultCommonTests() + : base(JavaScriptEncoder.Default, allowedChar: 'a', disallowedChar: '\"') + { + } + + private protected override string GetExpectedEscapedRepresentation(Rune value) + { + switch (value.Value) + { + case '\b': return "\\b"; + case '\t': return "\\t"; + case '\n': return "\\n"; + case '\f': return "\\f"; + case '\r': return "\\r"; + case '\\': return "\\\\"; + default: + if (value.IsBmp) + { + return FormattableString.Invariant($"\\u{(uint)value.Value:X4}"); + } + else + { + Span asUtf16 = stackalloc char[2]; + bool succeeded = value.TryEncodeToUtf16(asUtf16, out int utf16CodeUnitCount); + Assert.True(succeeded); + Assert.Equal(2, utf16CodeUnitCount); + return FormattableString.Invariant($"\\u{(uint)asUtf16[0]:X4}\\u{(uint)asUtf16[1]:X4}"); + } + } + } + + [Fact] + public void EncodeUtf16_Battery() + { + string[] inputs = new string[] + { + "\n", + "a", + "\u0234", // U+0234 LATIN SMALL LETTER L WITH CURL + "\ud800", // standalone high surrogate + "\U0001F415", // U+1F415 DOG + "\udfff", // standalone low surrogate + "\uFFFF", // end of BMP range + "\U00010000", // beginning of supplementary range + "\U0010FFFF", // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + "\\n", + "a", + "\\u0234", + "\\uFFFD", // replaced standalone high surrogate with replacement char + "\\uD83D\\uDC15", + "\\uFFFD", // replaced standalone low surrogate with replacement char + "\\uFFFF", + "\\uD800\\uDC00", + "\\uDBFF\\uDFFF", + }; + + _RunEncodeUtf16_Battery(inputs, expectedOutputs); + } + + [Fact] + public void EncodeUtf8_Battery() + { + byte[][] inputs = new byte[][] + { + new byte[] { (byte)'\n' }, + new byte[] { (byte)'a' }, + new byte[] { 0xC8, 0xB4 }, // U+0234 LATIN SMALL LETTER L WITH CURL + new byte[] { 0xFF }, // invalid byte + new byte[] { 0xF0, 0x9F, 0x90, 0x95 }, // U+1F415 DOG + new byte[] { 0x80 }, // standalone continuation character + new byte[] { 0xC2 }, // standalone multi-byte sequence marker + new byte[] { 0xEF, 0xBF, 0xBF }, // end of BMP range + new byte[] { 0xF0, 0x90, 0x80, 0x80 }, // beginning of supplementary range + new byte[] { 0xF4, 0x8F, 0xBF, 0xBF }, // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + "\\n", + "a", + "\\u0234", + "\\uFFFD", // replaced invalid byte + "\\uD83D\\uDC15", + "\\uFFFD", // replaced standalone continuation char + "\\uFFFD", // replaced standalone multi-byte sequence marker + "\\uFFFF", + "\\uD800\\uDC00", + "\\uDBFF\\uDFFF", + }; + + _RunEncodeUtf8_Battery(inputs, expectedOutputs); + } + } + + public class JavaScriptEncoderRelaxedCommonTests : InboxEncoderCommonTestBase + { + public JavaScriptEncoderRelaxedCommonTests() + : base(JavaScriptEncoder.UnsafeRelaxedJsonEscaping, allowedChar: 'a', disallowedChar: '\"') + { + } + + private protected override string GetExpectedEscapedRepresentation(Rune value) + { + switch (value.Value) + { + case '\b': return "\\b"; + case '\t': return "\\t"; + case '\n': return "\\n"; + case '\f': return "\\f"; + case '\r': return "\\r"; + case '\\': return "\\\\"; + case '\"': return "\\\""; + default: + if (value.IsBmp) + { + return FormattableString.Invariant($"\\u{(uint)value.Value:X4}"); + } + else + { + Span asUtf16 = stackalloc char[2]; + bool succeeded = value.TryEncodeToUtf16(asUtf16, out int utf16CodeUnitCount); + Assert.True(succeeded); + Assert.Equal(2, utf16CodeUnitCount); + return FormattableString.Invariant($"\\u{(uint)asUtf16[0]:X4}\\u{(uint)asUtf16[1]:X4}"); + } + } + } + + [Fact] + public void EncodeUtf16_Battery() + { + string[] inputs = new string[] + { + "\n", + "a", + "\u0234", // U+0234 LATIN SMALL LETTER L WITH CURL + "\ud800", // standalone high surrogate + "\U0001F415", // U+1F415 DOG + "\udfff", // standalone low surrogate + "\uFFFF", // end of BMP range + "\U00010000", // beginning of supplementary range + "\U0010FFFF", // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + "\\n", + "a", + "\u0234", // not escaped + "\\uFFFD", // replaced standalone high surrogate with replacement char + "\\uD83D\\uDC15", + "\\uFFFD", // replaced standalone low surrogate with replacement char + "\\uFFFF", + "\\uD800\\uDC00", + "\\uDBFF\\uDFFF", + }; + + _RunEncodeUtf16_Battery(inputs, expectedOutputs); + } + + [Fact] + public void EncodeUtf8_Battery() + { + byte[][] inputs = new byte[][] + { + new byte[] { (byte)'\n' }, + new byte[] { (byte)'a' }, + new byte[] { 0xC8, 0xB4 }, // U+0234 LATIN SMALL LETTER L WITH CURL + new byte[] { 0xFF }, // invalid byte + new byte[] { 0xF0, 0x9F, 0x90, 0x95 }, // U+1F415 DOG + new byte[] { 0x80 }, // standalone continuation character + new byte[] { 0xC2 }, // standalone multi-byte sequence marker + new byte[] { 0xEF, 0xBF, 0xBF }, // end of BMP range + new byte[] { 0xF0, 0x90, 0x80, 0x80 }, // beginning of supplementary range + new byte[] { 0xF4, 0x8F, 0xBF, 0xBF }, // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + "\\n", + "a", + "\u0234", // not escaped + "\\uFFFD", // replaced invalid byte + "\\uD83D\\uDC15", + "\\uFFFD", // replaced standalone continuation char + "\\uFFFD", // replaced standalone multi-byte sequence marker + "\\uFFFF", + "\\uD800\\uDC00", + "\\uDBFF\\uDFFF", + }; + + _RunEncodeUtf8_Battery(inputs, expectedOutputs); + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedValidCharsOnly() + { + _RunGetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedValidCharsOnly('\u2663'); // U+2663 BLACK CLUB SUIT + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedSomeCharsNeedEscaping() + { + _RunGetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedSomeCharsNeedEscaping('\u2663'); // U+2663 BLACK CLUB SUIT + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf8_BmpExtendedAllValidChars() + { + _RunGetIndexOfFirstCharacterToEncodeUtf8_BmpExtendedAllValidChars('\u2663'); // U+2663 BLACK CLUB SUIT + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf8_BmpExtendedSomeCharsNeedEncoding() + { + _RunGetIndexOfFirstCharacterToEncodeUtf8_BmpExtendedSomeCharsNeedEncoding('\u2663'); // U+2663 BLACK CLUB SUIT + } + } + + public class UrlEncoderDefaultCommonTests : InboxEncoderCommonTestBase + { + public UrlEncoderDefaultCommonTests() + : base(UrlEncoder.Default, allowedChar: 'a', disallowedChar: '?') + { + } + + private protected override string GetExpectedEscapedRepresentation(Rune value) + { + Span asUtf8Bytes = stackalloc byte[4]; + Span hexEscaped = stackalloc char[12]; // worst-case 3 output chars per input UTF-8 code unit + + bool succeeded = value.TryEncodeToUtf8(asUtf8Bytes, out int utf8CodeUnitCount); + Assert.True(succeeded); + + for (int i = 0; i < utf8CodeUnitCount; i++) + { + hexEscaped[i * 3] = '%'; + HexConverter.ToCharsBuffer(asUtf8Bytes[i], hexEscaped, startingIndex: (i * 3) + 1); + } + + return hexEscaped.Slice(0, utf8CodeUnitCount * 3).ToString(); + } + + [Fact] + public void EncodeUtf16_Battery() + { + string[] inputs = new string[] + { + "\n", + "%", + "a", + "\u0234", // U+0234 LATIN SMALL LETTER L WITH CURL + "\ud800", // standalone high surrogate + "\U0001F415", // U+1F415 DOG + "\udfff", // standalone low surrogate + "\uFFFF", // end of BMP range + "\U00010000", // beginning of supplementary range + "\U0010FFFF", // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + "%0A", + "%25", + "a", + "%C8%B4", + "%EF%BF%BD", // replaced standalone high surrogate with replacement char + "%F0%9F%90%95", + "%EF%BF%BD", // replaced standalone low surrogate with replacement char + "%EF%BF%BF", + "%F0%90%80%80", + "%F4%8F%BF%BF", + }; + + _RunEncodeUtf16_Battery(inputs, expectedOutputs); + } + + [Fact] + public void EncodeUtf8_Battery() + { + byte[][] inputs = new byte[][] + { + new byte[] { (byte)'\n' }, + new byte[] { (byte)'%' }, + new byte[] { (byte)'a' }, + new byte[] { 0xC8, 0xB4 }, // U+0234 LATIN SMALL LETTER L WITH CURL + new byte[] { 0xFF }, // invalid byte + new byte[] { 0xF0, 0x9F, 0x90, 0x95 }, // U+1F415 DOG + new byte[] { 0x80 }, // standalone continuation character + new byte[] { 0xC2 }, // standalone multi-byte sequence marker + new byte[] { 0xEF, 0xBF, 0xBF }, // end of BMP range + new byte[] { 0xF0, 0x90, 0x80, 0x80 }, // beginning of supplementary range + new byte[] { 0xF4, 0x8F, 0xBF, 0xBF }, // end of supplementary range + }; + + // expected outputs correspond to the escaped form of the inputs above + string[] expectedOutputs = new string[] + { + "%0A", + "%25", + "a", + "%C8%B4", + "%EF%BF%BD", // replaced invalid byte + "%F0%9F%90%95", + "%EF%BF%BD", // replaced standalone continuation char + "%EF%BF%BD", // replaced standalone multi-byte sequence marker + "%EF%BF%BF", + "%F0%90%80%80", + "%F4%8F%BF%BF", + }; + + _RunEncodeUtf8_Battery(inputs, expectedOutputs); + } + } + + public abstract class InboxEncoderCommonTestBase : IDisposable + { + private readonly TextEncoder _encoder; + private readonly BoundedMemory _boundedBytes = BoundedMemory.Allocate(4096); + private readonly BoundedMemory _boundedChars = BoundedMemory.Allocate(4096); + + private readonly char _allowedChar; // representative allowed char for this encoder + private readonly char _disallowedChar; // representative never-allowed char for this encoder + + // U+2D2E is in the Georgian Supplement block but is not currently assigned, hence disallowed by all inbox encoders. + // U+2D2E is an interesting test case because both U+002D ('-') and U+002E ('.') are allowed by all inbox encoders, + // so using U+2D2E exercises our UTF-16 -> ASCII narrowing paths to make sure that the narrowing process doesn't + // inadvertently treat a single non-ASCII BMP char as two independent ASCII chars. IF U+2D2E is ever assigned in + // the future, this could cause unit tests to fail, but we'll deal with that problem when (if?) the time comes. + private const char BmpExtendedDisallowedChar = '\u2d2e'; + + protected InboxEncoderCommonTestBase(TextEncoder encoder, char allowedChar, char disallowedChar) + { + Assert.NotNull(encoder); + _encoder = encoder; + + Assert.True(allowedChar <= 0x7F, "Test setup failure: Allowed char should be ASCII."); + Assert.False(encoder.WillEncode(allowedChar), "Test setup failure: Encoder must say this character is allowed."); + _allowedChar = allowedChar; + + Assert.True(disallowedChar <= 0x7F, "Test setup failure: Disallowed char should be ASCII."); + Assert.True(encoder.WillEncode(disallowedChar), "Test setup failure: Encoder must say this character is disallowed."); + _disallowedChar = disallowedChar; + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf16_AllDataValid() + => _RunGetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedValidCharsOnly(_allowedChar); + + protected void _RunGetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedValidCharsOnly(char bmpAllowedChar) + { + // Loop from 96 elements all the way down to 0 elements, which tests that we're + // not overrunning our read buffers. + + var span = _boundedChars.Span; + + _boundedChars.MakeWriteable(); + span.Fill(bmpAllowedChar); // make buffer all-valid + _boundedChars.MakeReadonly(); + + for (int i = 96; i >= 0; i--) + { + Assert.Equal(-1, _encoder.FindFirstCharacterToEncodeUtf16(span.Slice(span.Length - i))); + } + + // Also check from the beginning of the buffer just in case there's some alignment weirdness + // in the SIMD-optimized code that causes us to read past where we should. + + _boundedChars.MakeWriteable(); + + for (int i = 96; i >= 0; i--) + { + span[i] = _disallowedChar; // make this char invalid (ASCII) + Assert.Equal(-1, _encoder.FindFirstCharacterToEncodeUtf16(span.Slice(0, i))); + } + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf16_SomeCharsNeedEscaping() + => _RunGetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedSomeCharsNeedEscaping(_allowedChar); + + protected void _RunGetIndexOfFirstCharacterToEncodeUtf16_BmpExtendedSomeCharsNeedEscaping(char bmpAllowedChar) + { + // Use a 31-element buffer since it will exercise all the different unrolled code paths. + + var span = _boundedChars.Span.Slice(0, 31); + + for (int i = 0; i < span.Length - 1; i++) + { + // First make this the only invalid char in the whole buffer. + // Make sure we correctly identify the index which requires escaping. + + _boundedChars.MakeWriteable(); + span.Fill(bmpAllowedChar); // make buffer all-valid + span[i] = _disallowedChar; // make this char invalid (ASCII) + _boundedChars.MakeReadonly(); + Assert.Equal(i, _encoder.FindFirstCharacterToEncodeUtf16(span)); + + _boundedChars.MakeWriteable(); + span[i] = BmpExtendedDisallowedChar; // make this char invaid (BMP extended) + _boundedChars.MakeReadonly(); + Assert.Equal(i, _encoder.FindFirstCharacterToEncodeUtf16(span)); + + // Use a bad standalone surrogate char instead of a disallowed char + // and ensure we get the same index back. + + _boundedChars.MakeWriteable(); + span[i] = '\ud800'; + _boundedChars.MakeReadonly(); + Assert.Equal(i, _encoder.FindFirstCharacterToEncodeUtf16(span)); + + // Then make sure that we correctly identify this char as the *first* + // char which requires escaping, even if the buffer contains more + // requires-escaping chars after this. + + if (i < span.Length - 2) + { + _boundedChars.MakeWriteable(); + span[i] = _disallowedChar; + span[i + 1] = _disallowedChar; + _boundedChars.MakeReadonly(); + } + Assert.Equal(i, _encoder.FindFirstCharacterToEncodeUtf16(span)); + } + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf8_AllDataValid() + { + // Loop from 96 elements all the way down to 0 elements, which tests that we're + // not overrunning our read buffers. + + var span = _boundedBytes.Span; + + _boundedBytes.MakeWriteable(); + span.Fill((byte)_allowedChar); // make buffer all-valid + _boundedBytes.MakeReadonly(); + + for (int i = 96; i >= 0; i--) + { + Assert.Equal(-1, _encoder.FindFirstCharacterToEncodeUtf8(span.Slice(span.Length - i))); + } + + // Also check from the beginning of the buffer just in case there's some alignment weirdness + // in the SIMD-optimized code that causes us to read past where we should. + + _boundedBytes.MakeWriteable(); + + for (int i = 96; i >= 0; i--) + { + span[i] = (byte)_disallowedChar; // make this char invalid + Assert.Equal(-1, _encoder.FindFirstCharacterToEncodeUtf8(span.Slice(0, i))); + } + } + + [Fact] + public void GetIndexOfFirstCharacterToEncodeUtf8_SomeCharsNeedEscaping() + { + // Use a 31-element buffer since it will exercise all the different vectorized loops. + + var span = _boundedBytes.Span.Slice(0, 31); + + for (int i = 0; i < span.Length - 1; i++) + { + // First make this the only invalid char in the whole buffer. + // Make sure we correctly identify the index which requires escaping. + + _boundedBytes.MakeWriteable(); + span.Fill((byte)_allowedChar); // make buffer all-valid + span[i] = (byte)_disallowedChar; // make this char invalid + _boundedBytes.MakeReadonly(); + Assert.Equal(i, _encoder.FindFirstCharacterToEncodeUtf8(span)); + + // Then make sure that we correctly identify this char as the *first* + // char which requires escaping, even if the buffer contains more + // requires-escaping chars after this. + + if (i < span.Length - 2) + { + _boundedBytes.MakeWriteable(); + span[i + 1] = (byte)_disallowedChar; + _boundedBytes.MakeReadonly(); + } + Assert.Equal(i, _encoder.FindFirstCharacterToEncodeUtf8(span)); + } + } + + protected void _RunGetIndexOfFirstCharacterToEncodeUtf8_BmpExtendedAllValidChars(char bmpAllowedChar) + { + Span allowedCharAsUtf8 = stackalloc byte[3]; + Assert.True(new Rune(bmpAllowedChar).TryEncodeToUtf8(allowedCharAsUtf8, out int allowedCharUtf8CodeUnitCount)); + allowedCharAsUtf8 = allowedCharAsUtf8.Slice(0, allowedCharUtf8CodeUnitCount); + + // Copy this character to the end of the buffer 12 times + + var span = _boundedBytes.Span; + span = span.Slice(span.Length - allowedCharAsUtf8.Length * 12); + + _boundedBytes.MakeWriteable(); + span.Clear(); + for (int i = 0; i < 12; i++) + { + allowedCharAsUtf8.CopyTo(span.Slice(allowedCharAsUtf8.Length * i)); + } + _boundedBytes.MakeReadonly(); + + // And now make sure we identify all chars as allowed. + + for (int i = 0; i < 12; i++) + { + Assert.Equal(-1, _encoder.FindFirstCharacterToEncodeUtf8(span.Slice(allowedCharAsUtf8.Length * i))); + } + } + + protected void _RunGetIndexOfFirstCharacterToEncodeUtf8_BmpExtendedSomeCharsNeedEncoding(char bmpAllowedChar) + { + Assert.True(bmpAllowedChar >= 0x80, "Must be a non-ASCII char."); + + Span allowedCharAsUtf8 = stackalloc byte[3]; + Assert.True(new Rune(bmpAllowedChar).TryEncodeToUtf8(allowedCharAsUtf8, out int allowedCharUtf8CodeUnitCount)); + allowedCharAsUtf8 = allowedCharAsUtf8.Slice(0, allowedCharUtf8CodeUnitCount); + + // Copy this character to the end of the buffer 12 times + + var span = _boundedBytes.Span; + span = span.Slice(span.Length - allowedCharAsUtf8.Length * 12); + + _boundedBytes.MakeWriteable(); + span.Clear(); + for (int i = 0; i < 12; i++) + { + allowedCharAsUtf8.CopyTo(span.Slice(allowedCharAsUtf8.Length * i)); + } + + // And now make sure we identify bad chars as disallowed. + // The last element in the span will be invalid, and we'll keep shrinking the span + // so that the returned index changes on each iteration. + + for (int i = 0; i < 12; i++) + { + // First, corrupt the element by making it a standalone continuation byte. + span[span.Length - allowedCharAsUtf8.Length] = 0xBF; + Assert.Equal((11 - i) * allowedCharAsUtf8.Length, _encoder.FindFirstCharacterToEncodeUtf8(span.Slice(allowedCharAsUtf8.Length * i))); + + // Then, uncorrupt the element by making it a well-formed but never-allowed code point (U+009F is a never-allowed C1 control code point) + span[span.Length - allowedCharAsUtf8.Length] = 0xC2; + span[span.Length - allowedCharAsUtf8.Length + 1] = 0x9F; + Assert.Equal((11 - i) * allowedCharAsUtf8.Length, _encoder.FindFirstCharacterToEncodeUtf8(span.Slice(allowedCharAsUtf8.Length * i))); + } + } + + [Fact] + public unsafe void TryEncodeUnicodeScalar_AllowedBmpChar() + { + _boundedChars.MakeWriteable(); + + // First, try with enough space (two chars) in the destination buffer + + var destination = _boundedChars.Span; + destination = destination.Slice(destination.Length - 2); + destination.Clear(); + + fixed (char* pBuf = &MemoryMarshal.GetReference(destination)) + { + bool succeeded = _encoder.TryEncodeUnicodeScalar(_allowedChar, pBuf, destination.Length, out int numCharsWritten); + Assert.True(succeeded); + Assert.Equal(1, numCharsWritten); + Assert.Equal(_allowedChar, destination[0]); // Should reflect char as-is + } + + // Then, try with enough space (one char) in the destination buffer + + destination.Clear(); + destination = destination.Slice(1); + + fixed (char* pBuf = &MemoryMarshal.GetReference(destination)) + { + bool succeeded = _encoder.TryEncodeUnicodeScalar(_allowedChar, pBuf, destination.Length, out int numCharsWritten); + Assert.True(succeeded); + Assert.Equal(1, numCharsWritten); + Assert.Equal(_allowedChar, destination[0]); // Should reflect char as-is + } + + // Finally, try with not enough space in the destination buffer + + destination.Clear(); + destination = destination.Slice(1); + + fixed (char* pBuf = &MemoryMarshal.GetReference(destination)) // use MemoryMarshal so as to get a valid pointer + { + bool succeeded = _encoder.TryEncodeUnicodeScalar(_allowedChar, pBuf, destination.Length, out int numCharsWritten); + Assert.False(succeeded); + Assert.Equal(0, numCharsWritten); + } + } + + [Fact] + public unsafe void TryEncodeUnicodeScalar_DisallowedBmpChar() + { + TryEncodeUnicodeScalar_DisallowedScalarCommon(new Rune(_disallowedChar)); + } + + [Fact] + public unsafe void TryEncodeUnicodeScalar_DisallowedSupplementaryChar() + { + TryEncodeUnicodeScalar_DisallowedScalarCommon(new Rune(0x1F604)); // U+1F604 SMILING FACE WITH OPEN MOUTH AND SMILING EYES + } + + private unsafe void TryEncodeUnicodeScalar_DisallowedScalarCommon(Rune value) + { + _boundedChars.MakeWriteable(); + string expectedEscaping = GetExpectedEscapedRepresentation(value); + + // First, try with enough space +1 in the destination buffer + + var destination = _boundedChars.Span; + destination = destination.Slice(destination.Length - expectedEscaping.Length - 1); + destination.Clear(); + + fixed (char* pBuf = &MemoryMarshal.GetReference(destination)) + { + bool succeeded = _encoder.TryEncodeUnicodeScalar(value.Value, pBuf, destination.Length, out int numCharsWritten); + Assert.True(succeeded); + Assert.Equal(expectedEscaping.Length, numCharsWritten); + Assert.Equal(expectedEscaping, destination.Slice(0, expectedEscaping.Length).ToString()); + } + + // Then, try with enough space +0 in the destination buffer + + destination.Clear(); + destination = destination.Slice(1); + + fixed (char* pBuf = &MemoryMarshal.GetReference(destination)) + { + bool succeeded = _encoder.TryEncodeUnicodeScalar(value.Value, pBuf, destination.Length, out int numCharsWritten); + Assert.True(succeeded); + Assert.Equal(expectedEscaping.Length, numCharsWritten); + Assert.Equal(expectedEscaping, destination.ToString()); + } + + // Finally, try with not enough space in the destination buffer + + destination.Clear(); + destination = destination.Slice(1); + + fixed (char* pBuf = &MemoryMarshal.GetReference(destination)) // use MemoryMarshal so as to get a valid pointer + { + bool succeeded = _encoder.TryEncodeUnicodeScalar(value.Value, pBuf, destination.Length, out int numCharsWritten); + Assert.False(succeeded); + Assert.Equal(0, numCharsWritten); + } + } + + protected void _RunEncodeUtf16_Battery(string[] inputs, string[] expectedOutputs) + { + string accumInput = _disallowedChar.ToString(); + string accumExpectedOutput = GetExpectedEscapedRepresentation(new Rune(_disallowedChar)); + + // First, make sure we handle the simple "can't escape a single char to the buffer" case + + OperationStatus opStatus = _encoder.Encode(accumInput.AsSpan(), new char[accumExpectedOutput.Length - 1], out int charsConsumed, out int charsWritten); + Assert.Equal(OperationStatus.DestinationTooSmall, opStatus); + Assert.Equal(0, charsConsumed); + Assert.Equal(0, charsWritten); + + // Then, escape a single char to the destination buffer. + // This skips the "find the first char to encode" fast path in TextEncoder.cs. + + char[] destination = new char[accumExpectedOutput.Length]; + opStatus = _encoder.Encode(accumInput.AsSpan(), destination, out charsConsumed, out charsWritten); + Assert.Equal(OperationStatus.Done, opStatus); + Assert.Equal(1, charsConsumed); + Assert.Equal(destination.Length, charsWritten); + Assert.Equal(accumExpectedOutput, new string(destination)); + + // Now, in a loop, append inputs to the source span and test various edge cases of + // destination too small vs. destination properly sized. + + Assert.Equal(expectedOutputs.Length, inputs.Length); + for (int i = 0; i < inputs.Length; i++) + { + accumInput += inputs[i]; + string outputToAppend = expectedOutputs[i]; + + // Test destination too small - we should make progress up until + // the very last thing we appended to the input. + + destination = new char[accumExpectedOutput.Length + outputToAppend.Length - 1]; + opStatus = _encoder.Encode(accumInput.AsSpan(), destination, out charsConsumed, out charsWritten); + Assert.Equal(OperationStatus.DestinationTooSmall, opStatus); + Assert.Equal(accumInput.Length - inputs[i].Length, charsConsumed); // should've consumed everything except the most recent appended data + Assert.Equal(accumExpectedOutput.Length, charsWritten); // should've escaped everything we consumed + Assert.Equal(accumExpectedOutput, new string(destination, 0, charsWritten)); + + // Now test destination just right - we should consume the entire buffer successfully. + + accumExpectedOutput += outputToAppend; + destination = new char[accumExpectedOutput.Length]; + opStatus = _encoder.Encode(accumInput.AsSpan(), destination, out charsConsumed, out charsWritten); + Assert.Equal(OperationStatus.Done, opStatus); + Assert.Equal(accumInput.Length, charsConsumed); + Assert.Equal(accumExpectedOutput.Length, charsWritten); + Assert.Equal(accumExpectedOutput, new string(destination)); + + // Now test destination oversized - we should consume the entire buffer successfully. + + destination = new char[accumExpectedOutput.Length + 1]; + opStatus = _encoder.Encode(accumInput.AsSpan(), destination, out charsConsumed, out charsWritten); + Assert.Equal(OperationStatus.Done, opStatus); + Assert.Equal(accumInput.Length, charsConsumed); + Assert.Equal(accumExpectedOutput.Length, charsWritten); + Assert.Equal(accumExpectedOutput, new string(destination, 0, charsWritten)); + + // Special-case: if the buffer ended with a legal supplementary scalar value, slice off + // the last low surrogate char now and ensure the escaper can handle reading partial + // surrogates, returning "Needs More Data". + + if (EndsWithValidSurrogatePair(accumInput)) + { + destination.AsSpan().Clear(); + opStatus = _encoder.Encode(accumInput.AsSpan(0, accumInput.Length - 1), destination, out charsConsumed, out charsWritten, isFinalBlock: false); + Assert.Equal(OperationStatus.NeedMoreData, opStatus); + Assert.Equal(accumInput.Length - 2, charsConsumed); + Assert.Equal(accumExpectedOutput.Length - outputToAppend.Length, charsWritten); + Assert.Equal(accumExpectedOutput.Substring(0, accumExpectedOutput.Length - outputToAppend.Length), new string(destination, 0, charsWritten)); + } + } + } + + protected void _RunEncodeUtf8_Battery(byte[][] inputs, string[] expectedOutputsAsUtf16) + { + byte[] accumInput = new byte[] { (byte)_disallowedChar }; + byte[] accumExpectedOutput = Encoding.UTF8.GetBytes(GetExpectedEscapedRepresentation(new Rune(_disallowedChar))); + byte[][] expectedOutputs = expectedOutputsAsUtf16.Select(Encoding.UTF8.GetBytes).ToArray(); + + // First, make sure we handle the simple "can't escape a single char to the buffer" case + + OperationStatus opStatus = _encoder.EncodeUtf8(accumInput, new byte[accumExpectedOutput.Length - 1], out int bytesConsumed, out int bytesWritten); + Assert.Equal(OperationStatus.DestinationTooSmall, opStatus); + Assert.Equal(0, bytesConsumed); + Assert.Equal(0, bytesWritten); + + // Then, escape a single char to the destination buffer. + // This skips the "find the first char to encode" fast path in TextEncoder.cs. + + byte[] destination = new byte[accumExpectedOutput.Length]; + opStatus = _encoder.EncodeUtf8(accumInput, destination, out bytesConsumed, out bytesWritten); + Assert.Equal(OperationStatus.Done, opStatus); + Assert.Equal(1, bytesConsumed); + Assert.Equal(destination.Length, bytesWritten); + Assert.Equal(accumExpectedOutput, destination.ToArray()); + + // Now, in a loop, append inputs to the source span and test various edge cases of + // destination too small vs. destination properly sized. + + Assert.Equal(expectedOutputs.Length, inputs.Length); + for (int i = 0; i < inputs.Length; i++) + { + accumInput = accumInput.Concat(inputs[i]).ToArray(); + byte[] outputToAppend = expectedOutputs[i]; + + // Test destination too small - we should make progress up until + // the very last thing we appended to the input. + + destination = new byte[accumExpectedOutput.Length + outputToAppend.Length - 1]; + opStatus = _encoder.EncodeUtf8(accumInput, destination, out bytesConsumed, out bytesWritten); + Assert.Equal(OperationStatus.DestinationTooSmall, opStatus); + Assert.Equal(accumInput.Length - inputs[i].Length, bytesConsumed); // should've consumed everything except the most recent appended data + Assert.Equal(accumExpectedOutput.Length, bytesWritten); // should've escaped everything we consumed + Assert.Equal(accumExpectedOutput, destination.AsSpan(0, bytesWritten).ToArray()); + + // Now test destination just right - we should consume the entire buffer successfully. + + accumExpectedOutput = accumExpectedOutput.Concat(outputToAppend).ToArray(); + destination = new byte[accumExpectedOutput.Length]; + opStatus = _encoder.EncodeUtf8(accumInput, destination, out bytesConsumed, out bytesWritten); + Assert.Equal(OperationStatus.Done, opStatus); + Assert.Equal(accumInput.Length, bytesConsumed); + Assert.Equal(accumExpectedOutput.Length, bytesWritten); + Assert.Equal(accumExpectedOutput, destination); + + // Now test destination oversized - we should consume the entire buffer successfully. + + destination = new byte[accumExpectedOutput.Length + 1]; + opStatus = _encoder.EncodeUtf8(accumInput, destination, out bytesConsumed, out bytesWritten); + Assert.Equal(OperationStatus.Done, opStatus); + Assert.Equal(accumInput.Length, bytesConsumed); + Assert.Equal(accumExpectedOutput.Length, bytesWritten); + Assert.Equal(accumExpectedOutput, destination.AsSpan(0, bytesWritten).ToArray()); + + // Special-case: if the buffer ended with a legal supplementary scalar value, slice off + // the last few bytes now and ensure the escaper can handle reading partial + // values, returning "Needs More Data". + + if (EndsWithValidMultiByteUtf8Sequence(accumInput)) + { + destination.AsSpan().Clear(); + opStatus = _encoder.EncodeUtf8(accumInput.AsSpan(0, accumInput.Length - 1), destination, out bytesConsumed, out bytesWritten, isFinalBlock: false); + Assert.Equal(OperationStatus.NeedMoreData, opStatus); + Assert.Equal(accumInput.Length - inputs[i].Length, bytesConsumed); + Assert.Equal(accumExpectedOutput.Length - outputToAppend.Length, bytesWritten); + Assert.Equal(accumExpectedOutput.AsSpan(0, accumExpectedOutput.Length - outputToAppend.Length).ToArray(), destination.AsSpan(0, bytesWritten).ToArray()); + } + } + } + + private static bool EndsWithValidSurrogatePair(string input) + { + return input.Length >= 2 + && char.IsHighSurrogate(input[input.Length - 2]) + && char.IsLowSurrogate(input[input.Length - 1]); + } + + private static bool EndsWithValidMultiByteUtf8Sequence(byte[] input) + { + for (int i = input.Length - 1; i >= 0; i--) + { + if (input[i] >= 0xC0) + { + return Rune.DecodeFromUtf8(input.AsSpan(i), out _, out int bytesConsumed) == OperationStatus.Done + && i + bytesConsumed == input.Length; + } + } + + return false; // input was empty? + } + + private protected abstract string GetExpectedEscapedRepresentation(Rune value); + + private string GetExpectedEscapedRepresentation(string value) + { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < value.Length;) + { + Rune.DecodeFromUtf16(value.AsSpan(i), out Rune nextRune, out int charsConsumed); + builder.Append(GetExpectedEscapedRepresentation(nextRune)); + i += charsConsumed; + } + return builder.ToString(); + } + + void IDisposable.Dispose() + { + _boundedBytes.Dispose(); + _boundedChars.Dispose(); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/JavaScriptStringEncoderTests.Relaxed.cs b/src/libraries/System.Text.Encodings.Web/tests/JavaScriptEncoderTests.Relaxed.cs similarity index 58% rename from src/libraries/System.Text.Encodings.Web/tests/JavaScriptStringEncoderTests.Relaxed.cs rename to src/libraries/System.Text.Encodings.Web/tests/JavaScriptEncoderTests.Relaxed.cs index b4a9f7d682f09..50073b8591773 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/JavaScriptStringEncoderTests.Relaxed.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/JavaScriptEncoderTests.Relaxed.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Globalization; using System.IO; using System.Text.Unicode; @@ -9,7 +8,7 @@ namespace System.Text.Encodings.Web.Tests { - public partial class JavaScriptStringEncoderTests + public partial class JavaScriptEncoderTests { [Fact] public void TestSurrogate_Relaxed() @@ -26,8 +25,8 @@ public void TestSurrogate_Relaxed() public void Relaxed_EquivalentToAll_WithExceptions() { // Arrange - JavaScriptStringEncoder controlEncoder = new JavaScriptStringEncoder(UnicodeRanges.All); - JavaScriptStringEncoder testEncoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder controlEncoder = JavaScriptEncoder.Create(UnicodeRanges.All); + JavaScriptEncoder testEncoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act & assert for (int i = 0; i <= char.MaxValue; i++) @@ -35,27 +34,27 @@ public void Relaxed_EquivalentToAll_WithExceptions() if (i == '"' || i == '&' || i == '<' || i == '>' || i == '+' || i == '\'' || i == '`') { string input = new string((char)i, 1); - Assert.NotEqual(controlEncoder.JavaScriptStringEncode(input), testEncoder.JavaScriptStringEncode(input)); + Assert.NotEqual(controlEncoder.Encode(input), testEncoder.Encode(input)); continue; } if (!IsSurrogateCodePoint(i)) { string input = new string((char)i, 1); - Assert.Equal(controlEncoder.JavaScriptStringEncode(input), testEncoder.JavaScriptStringEncode(input)); + Assert.Equal(controlEncoder.Encode(input), testEncoder.Encode(input)); } } } [Fact] - public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Simple_Escaping() + public void JavaScriptEncode_Relaxed_StillEncodesForbiddenChars_Simple_Escaping() { // The following two calls could be simply InlineData to the Theory below // Unfortunately, the xUnit logger fails to escape the inputs when logging the test results, // and so the suite fails despite all tests passing. // TODO: I will try to fix it in xUnit, but for now this is a workaround to enable these tests. - JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Simple("\b", @"\b"); - JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Simple("\f", @"\f"); + JavaScriptEncode_Relaxed_StillEncodesForbiddenChars_Simple("\b", @"\b"); + JavaScriptEncode_Relaxed_StillEncodesForbiddenChars_Simple("\f", @"\f"); } [Theory] @@ -64,23 +63,23 @@ public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Simple_Esc [InlineData("\n", @"\n")] [InlineData("\t", @"\t")] [InlineData("\r", @"\r")] - public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Simple(string input, string expected) + public void JavaScriptEncode_Relaxed_StillEncodesForbiddenChars_Simple(string input, string expected) { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); } [Fact] - public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Extended() + public void JavaScriptEncode_Relaxed_StillEncodesForbiddenChars_Extended() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act & assert - BMP chars for (int i = 0; i <= 0xFFFF; i++) @@ -89,7 +88,7 @@ public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Extended() string expected; if (IsSurrogateCodePoint(i)) { - expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char + expected = "\\uFFFD"; // unpaired surrogate -> Unicode replacement char } else { @@ -129,7 +128,7 @@ public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Extended() { mustEncode = true; // control char } - else if (!UnicodeHelpers.IsCharacterDefined((char)i)) + else if (!UnicodeTestHelpers.IsCharacterDefined((char)i)) { mustEncode = true; // undefined (or otherwise disallowed) char } @@ -145,7 +144,7 @@ public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Extended() } } - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } @@ -154,107 +153,107 @@ public void JavaScriptStringEncode_Relaxed_StillEncodesForbiddenChars_Extended() { string input = char.ConvertFromUtf32(i); string expected = string.Format(CultureInfo.InvariantCulture, @"\u{0:X4}\u{1:X4}", (uint)input[0], (uint)input[1]); - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } } [Fact] - public void JavaScriptStringEncode_BadSurrogates_ReturnsUnicodeReplacementChar_Relaxed() + public void JavaScriptEncode_BadSurrogates_ReturnsUnicodeReplacementChar_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; // allow all codepoints + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // allow all codepoints // "abcde" const string input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; - const string expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD\\uD800\\uDFFFe\uFFFD"; // 'D800' 'DFFF' was preserved since it's valid + const string expected = "a\\uFFFDb\\uFFFDc\\uFFFD\\uFFFDd\\uFFFD\\uD800\\uDFFFe\\uFFFD"; // 'D800' 'DFFF' was preserved since it's valid // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); } [Fact] - public void JavaScriptStringEncode_EmptyStringInput_ReturnsEmptyString_Relaxed() + public void JavaScriptEncode_EmptyStringInput_ReturnsEmptyString_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act & assert - Assert.Equal("", encoder.JavaScriptStringEncode("")); + Assert.Equal("", encoder.Encode("")); } [Fact] - public void JavaScriptStringEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance_Relaxed() + public void JavaScriptEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; string input = "Hello, there!"; // Act & assert - Assert.Same(input, encoder.JavaScriptStringEncode(input)); + Assert.Same(input, encoder.Encode(input)); } [Fact] - public void JavaScriptStringEncode_NullInput_Throws_Relaxed() + public void JavaScriptEncode_NullInput_Throws_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; - Assert.Throws(() => { encoder.JavaScriptStringEncode(null); }); + Assert.Throws(() => { encoder.Encode(null); }); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingAtBeginning_Relaxed() + public void JavaScriptEncode_WithCharsRequiringEncodingAtBeginning_Relaxed() { - Assert.Equal(@"\\Hello, there!", JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping.JavaScriptStringEncode("\\Hello, there!")); + Assert.Equal(@"\\Hello, there!", JavaScriptEncoder.UnsafeRelaxedJsonEscaping.Encode("\\Hello, there!")); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingAtEnd_Relaxed() + public void JavaScriptEncode_WithCharsRequiringEncodingAtEnd_Relaxed() { - Assert.Equal(@"Hello, there!\\", JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping.JavaScriptStringEncode("Hello, there!\\")); + Assert.Equal(@"Hello, there!\\", JavaScriptEncoder.UnsafeRelaxedJsonEscaping.Encode("Hello, there!\\")); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingInMiddle_Relaxed() + public void JavaScriptEncode_WithCharsRequiringEncodingInMiddle_Relaxed() { - Assert.Equal(@"Hello, \\there!", JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping.JavaScriptStringEncode("Hello, \\there!")); + Assert.Equal(@"Hello, \\there!", JavaScriptEncoder.UnsafeRelaxedJsonEscaping.Encode("Hello, \\there!")); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingInterspersed_Relaxed() + public void JavaScriptEncode_WithCharsRequiringEncodingInterspersed_Relaxed() { - Assert.Equal("Hello, \\\\there\\\"!", JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping.JavaScriptStringEncode("Hello, \\there\"!")); + Assert.Equal("Hello, \\\\there\\\"!", JavaScriptEncoder.UnsafeRelaxedJsonEscaping.Encode("Hello, \\there\"!")); } [Fact] - public void JavaScriptStringEncode_CharArray_Relaxed() + public void JavaScriptEncode_CharArray_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; using var output = new StringWriter(); // Act - encoder.JavaScriptStringEncode("Hello\\world!".ToCharArray(), 3, 5, output); + encoder.Encode(output, "Hello\\world!".ToCharArray(), 3, 5); // Assert Assert.Equal(@"lo\\wo", output.ToString()); } [Fact] - public void JavaScriptStringEncode_StringSubstring_Relaxed() + public void JavaScriptEncode_StringSubstring_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; using var output = new StringWriter(); // Act - encoder.JavaScriptStringEncode("Hello\\world!", 3, 5, output); + encoder.Encode(output, "Hello\\world!", 3, 5); // Assert Assert.Equal(@"lo\\wo", output.ToString()); @@ -263,13 +262,13 @@ public void JavaScriptStringEncode_StringSubstring_Relaxed() [Theory] [InlineData("\"", "\\\"")] [InlineData("'", "'")] - public void JavaScriptStringEncode_Quotes_Relaxed(string input, string expected) + public void JavaScriptEncode_Quotes_Relaxed(string input, string expected) { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); @@ -279,23 +278,23 @@ public void JavaScriptStringEncode_Quotes_Relaxed(string input, string expected) [InlineData("hello+world", "hello+world")] [InlineData("hello", "hello")] [InlineData("hello&world", "hello&world")] - public void JavaScriptStringEncode_DoesOutputHtmlSensitiveCharacters_Relaxed(string input, string expected) + public void JavaScriptEncode_DoesOutputHtmlSensitiveCharacters_Relaxed(string input, string expected) { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); } [Fact] - public void JavaScriptStringEncode_AboveAscii_Relaxed() + public void JavaScriptEncode_AboveAscii_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act & assert for (int i = 0x128; i <= 0xFFFF; i++) @@ -311,16 +310,16 @@ public void JavaScriptStringEncode_AboveAscii_Relaxed() continue; // skip undefined characters like U+0378, or spacing characters like U+2028 } - string javaScriptStringEncoded = encoder.JavaScriptStringEncode(char.ConvertFromUtf32(i)); - Assert.True(char.ConvertFromUtf32(i) == javaScriptStringEncoded, i.ToString()); + string javaScriptEncoded = encoder.Encode(char.ConvertFromUtf32(i)); + Assert.True(char.ConvertFromUtf32(i) == javaScriptEncoded, i.ToString()); } } [Fact] - public void JavaScriptStringEncode_ControlCharacters_Relaxed() + public void JavaScriptEncode_ControlCharacters_Relaxed() { // Arrange - JavaScriptStringEncoder encoder = JavaScriptStringEncoder.UnsafeRelaxedJsonEscaping; + JavaScriptEncoder encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // Act & assert for (int i = 0; i <= 0x1F; i++) @@ -330,9 +329,9 @@ public void JavaScriptStringEncode_ControlCharacters_Relaxed() { continue; } - string javaScriptStringEncoded = encoder.JavaScriptStringEncode(char.ConvertFromUtf32(i)); + string javaScriptEncoded = encoder.Encode(char.ConvertFromUtf32(i)); string expected = string.Format("\\u00{0:X2}", i); - Assert.Equal(expected, javaScriptStringEncoded); + Assert.Equal(expected, javaScriptEncoded); } } } diff --git a/src/libraries/System.Text.Encodings.Web/tests/JavaScriptStringEncoderTests.cs b/src/libraries/System.Text.Encodings.Web/tests/JavaScriptEncoderTests.cs similarity index 71% rename from src/libraries/System.Text.Encodings.Web/tests/JavaScriptStringEncoderTests.cs rename to src/libraries/System.Text.Encodings.Web/tests/JavaScriptEncoderTests.cs index e918461f79b38..161ba615ae244 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/JavaScriptStringEncoderTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/JavaScriptEncoderTests.cs @@ -1,33 +1,22 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Buffers; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; -using System.Text.Internal; using System.Text.Unicode; using Xunit; namespace System.Text.Encodings.Web.Tests { - public partial class JavaScriptStringEncoderTests + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] + public partial class JavaScriptEncoderTests { [Fact] - public unsafe void NullPtrThrows() + public void Factory_NullArgs_Throws() { - Assert.Throws(() => JavaScriptEncoder.Default.FindFirstCharacterToEncode(null, 0)); - Assert.Throws(() => JavaScriptEncoder.UnsafeRelaxedJsonEscaping.FindFirstCharacterToEncode(null, 0)); - Assert.Throws(() => JavaScriptEncoder.Create(UnicodeRanges.All).FindFirstCharacterToEncode(null, 0)); - - Assert.Throws(() => JavaScriptEncoder.Default.TryEncodeUnicodeScalar('a', null, 0, out _)); - Assert.Throws(() => JavaScriptEncoder.UnsafeRelaxedJsonEscaping.TryEncodeUnicodeScalar('a', null, 0, out _)); - Assert.Throws(() => JavaScriptEncoder.Create(UnicodeRanges.All).TryEncodeUnicodeScalar('a', null, 0, out _)); - Assert.Throws(() => JavaScriptEncoder.Create((TextEncoderSettings)null)); Assert.Throws(() => JavaScriptEncoder.Create((UnicodeRange)null)); } @@ -207,23 +196,6 @@ public static IEnumerable EscapingTestData_NonAscii new object[] { '\'', JavaScriptEncoder.UnsafeRelaxedJsonEscaping, false }, new object[] { '+', JavaScriptEncoder.UnsafeRelaxedJsonEscaping, false }, new object[] { '\uFFFD', JavaScriptEncoder.UnsafeRelaxedJsonEscaping, false }, - - new object[] { 'a', new MyCustomEncoder(UnicodeRanges.All), false }, - new object[] { '\u001F', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '\u007F', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '\u2000', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '\u00A2', new MyCustomEncoder(UnicodeRanges.All), false }, - new object[] { '\uA686', new MyCustomEncoder(UnicodeRanges.All), false }, - new object[] { '\u6C49', new MyCustomEncoder(UnicodeRanges.All), false }, - new object[] { '"', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '\\', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '<', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '>', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '&', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '`', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '\'', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '+', new MyCustomEncoder(UnicodeRanges.All), true }, - new object[] { '\uFFFD', new MyCustomEncoder(UnicodeRanges.All), false }, }; } } @@ -297,7 +269,6 @@ public static IEnumerable JavaScriptEncoders new object[] { JavaScriptEncoder.Create(UnicodeRanges.BasicLatin) }, new object[] { JavaScriptEncoder.Create(UnicodeRanges.All) }, new object[] { JavaScriptEncoder.UnsafeRelaxedJsonEscaping }, - new object[] { new MyCustomEncoder(UnicodeRanges.BasicLatin) }, }; } } @@ -352,89 +323,10 @@ public static IEnumerable InvalidEscapingTestData new object[] { '\uD801', JavaScriptEncoder.Create(UnicodeRanges.BasicLatin) }, new object[] { '\uDC01', JavaScriptEncoder.Create(UnicodeRanges.BasicLatin) }, - - new object[] { '\uD801', new MyCustomEncoder(UnicodeRanges.BasicLatin) }, - new object[] { '\uDC01', new MyCustomEncoder(UnicodeRanges.BasicLatin) }, }; } } - internal sealed class MyCustomEncoder : JavaScriptEncoder - { - private readonly AllowedCharactersBitmap _allowedCharacters; - - public MyCustomEncoder(TextEncoderSettings filter) - { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } - - _allowedCharacters = filter.GetAllowedCharacters(); - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - _allowedCharacters.ForbidUndefinedCharacters(); - - // Forbid characters that are special in HTML. - // Even though this is a not HTML encoder, - // it's unfortunately common for developers to - // forget to HTML-encode a string once it has been JS-encoded, - // so this offers extra protection. - ForbidHtmlCharacters(_allowedCharacters); - - // '\' (U+005C REVERSE SOLIDUS) must always be escaped in Javascript / ECMAScript / JSON. - // '/' (U+002F SOLIDUS) is not Javascript / ECMAScript / JSON-sensitive so doesn't need to be escaped. - _allowedCharacters.ForbidCharacter('\\'); - - // '`' (U+0060 GRAVE ACCENT) is ECMAScript-sensitive (see ECMA-262). - _allowedCharacters.ForbidCharacter('`'); - } - - internal static void ForbidHtmlCharacters(AllowedCharactersBitmap allowedCharacters) - { - allowedCharacters.ForbidCharacter('<'); - allowedCharacters.ForbidCharacter('>'); - allowedCharacters.ForbidCharacter('&'); - allowedCharacters.ForbidCharacter('\''); // can be used to escape attributes - allowedCharacters.ForbidCharacter('\"'); // can be used to escape attributes - allowedCharacters.ForbidCharacter('+'); // technically not HTML-specific, but can be used to perform UTF7-based attacks - } - - public MyCustomEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } - - public override int MaxOutputCharactersPerInputCharacter => 12; // "\uFFFF\uFFFF" is the longest encoded form - - public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - throw new NotImplementedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) - { - if (text == null) - { - throw new ArgumentNullException(nameof(text)); - } - - return _allowedCharacters.FindFirstCharacterToEncode(text, textLength); - } - - public override bool WillEncode(int unicodeScalar) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(unicodeScalar)) - { - return true; - } - - Debug.Assert(unicodeScalar >= char.MinValue && unicodeScalar <= char.MaxValue); - - return !_allowedCharacters.IsUnicodeScalarAllowed(unicodeScalar); - } - } - [Fact] public void TestSurrogate() { @@ -505,7 +397,7 @@ public void TestSurrogateBufferTooSmall() } [Fact] - public void JavaScriptStringEncoder_NonEmptySource_EmptyDest_Throws() + public void JavaScriptEncoder_NonEmptySource_EmptyDest_Throws() { OperationStatus status = System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode( "\U0001f4a9".AsSpan(), destination: null, out int _, out int _, isFinalBlock: true); @@ -514,7 +406,7 @@ public void JavaScriptStringEncoder_NonEmptySource_EmptyDest_Throws() } [Fact] - public void JavaScriptStringEncoder_EmptySource_EmptyDest() + public void JavaScriptEncoder_EmptySource_EmptyDest() { OperationStatus status = System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode( "".AsSpan(), destination: null, out int _, out int _, isFinalBlock: true); @@ -563,48 +455,48 @@ public void Ctor_WithTextEncoderSettings() filter.AllowCharacters('a', 'b'); filter.AllowCharacters('\0', '&', '\uFFFF', 'd'); - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(filter); + JavaScriptEncoder encoder = JavaScriptEncoder.Create(filter); // Act & assert - Assert.Equal("a", encoder.JavaScriptStringEncode("a")); - Assert.Equal("b", encoder.JavaScriptStringEncode("b")); - Assert.Equal(@"\u0063", encoder.JavaScriptStringEncode("c")); - Assert.Equal("d", encoder.JavaScriptStringEncode("d")); - Assert.Equal(@"\u0000", encoder.JavaScriptStringEncode("\0")); // we still always encode control chars - Assert.Equal(@"\u0026", encoder.JavaScriptStringEncode("&")); // we still always encode HTML-special chars - Assert.Equal(@"\uFFFF", encoder.JavaScriptStringEncode("\uFFFF")); // we still always encode non-chars and other forbidden chars + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal("b", encoder.Encode("b")); + Assert.Equal(@"\u0063", encoder.Encode("c")); + Assert.Equal("d", encoder.Encode("d")); + Assert.Equal(@"\u0000", encoder.Encode("\0")); // we still always encode control chars + Assert.Equal(@"\u0026", encoder.Encode("&")); // we still always encode HTML-special chars + Assert.Equal(@"\uFFFF", encoder.Encode("\uFFFF")); // we still always encode non-chars and other forbidden chars } [Fact] public void Ctor_WithUnicodeRanges() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); // Act & assert - Assert.Equal(@"\u0061", encoder.JavaScriptStringEncode("a")); - Assert.Equal("\u00E9", encoder.JavaScriptStringEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal("\u2601", encoder.JavaScriptStringEncode("\u2601" /* CLOUD */)); + Assert.Equal(@"\u0061", encoder.Encode("a")); + Assert.Equal("\u00E9", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); + Assert.Equal("\u2601", encoder.Encode("\u2601" /* CLOUD */)); } [Fact] - public void Ctor_WithNoParameters_DefaultsToBasicLatin() + public void DefaultFactory_IsBasicLatin() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(); + JavaScriptEncoder encoder = JavaScriptEncoder.Default; // Act & assert - Assert.Equal("a", encoder.JavaScriptStringEncode("a")); - Assert.Equal(@"\u00E9", encoder.JavaScriptStringEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal(@"\u2601", encoder.JavaScriptStringEncode("\u2601" /* CLOUD */)); + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal(@"\u00E9", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); + Assert.Equal(@"\u2601", encoder.Encode("\u2601" /* CLOUD */)); } [Fact] public void Default_EquivalentToBasicLatin() { // Arrange - JavaScriptStringEncoder controlEncoder = new JavaScriptStringEncoder(UnicodeRanges.BasicLatin); - JavaScriptStringEncoder testEncoder = JavaScriptStringEncoder.Default; + JavaScriptEncoder controlEncoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin); + JavaScriptEncoder testEncoder = JavaScriptEncoder.Default; // Act & assert for (int i = 0; i <= char.MaxValue; i++) @@ -612,20 +504,20 @@ public void Default_EquivalentToBasicLatin() if (!IsSurrogateCodePoint(i)) { string input = new string((char)i, 1); - Assert.Equal(controlEncoder.JavaScriptStringEncode(input), testEncoder.JavaScriptStringEncode(input)); + Assert.Equal(controlEncoder.Encode(input), testEncoder.Encode(input)); } } } [Fact] - public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple_Escaping() + public void JavaScriptEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple_Escaping() { // The following two calls could be simply InlineData to the Theory below // Unfortunately, the xUnit logger fails to escape the inputs when logging the test results, // and so the suite fails despite all tests passing. // TODO: I will try to fix it in xUnit, but for now this is a workaround to enable these tests. - JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple("\b", @"\b"); - JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple("\f", @"\f"); + JavaScriptEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple("\b", @"\b"); + JavaScriptEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple("\f", @"\f"); } [Theory] @@ -639,23 +531,23 @@ public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_S [InlineData("\n", @"\n")] [InlineData("\t", @"\t")] [InlineData("\r", @"\r")] - public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string input, string expected) + public void JavaScriptEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string input, string expected) { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.All); + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); } [Fact] - public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() + public void JavaScriptEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.All); + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // Act & assert - BMP chars for (int i = 0; i <= 0xFFFF; i++) @@ -664,7 +556,7 @@ public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_E string expected; if (IsSurrogateCodePoint(i)) { - expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char + expected = "\\uFFFD"; // unpaired surrogate -> Unicode replacement char } else { @@ -694,7 +586,7 @@ public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_E { mustEncode = true; // control char } - else if (!UnicodeHelpers.IsCharacterDefined((char)i)) + else if (!UnicodeTestHelpers.IsCharacterDefined((char)i)) { mustEncode = true; // undefined (or otherwise disallowed) char } @@ -710,7 +602,7 @@ public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_E } } - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } @@ -719,19 +611,19 @@ public void JavaScriptStringEncode_AllRangesAllowed_StillEncodesForbiddenChars_E { string input = char.ConvertFromUtf32(i); string expected = string.Format(CultureInfo.InvariantCulture, @"\u{0:X4}\u{1:X4}", (uint)input[0], (uint)input[1]); - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } } [Fact] - public void JavaScriptStringEncode_NoRangesAllowed_EmitsShortFormForCertainCodePoints() + public void JavaScriptEncode_NoRangesAllowed_EmitsShortFormForCertainCodePoints() { // This test ensures that when we're encoding, we always emit the "\uXXXX" form of the // code point except for very specific code points where we allow a shorter representation. // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.None); // allow no codepoints + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.None); // allow no codepoints // "[U+0000][U+0001]...[U+007F]" string input = new string(Enumerable.Range(0, 128).Select(i => (char)i).ToArray()); @@ -747,24 +639,24 @@ public void JavaScriptStringEncode_NoRangesAllowed_EmitsShortFormForCertainCodeP expected = expected.Replace(@"\u005C", @"\\"); // U+005C REVERSE SOLIDUS -> "\\" // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); } [Fact] - public void JavaScriptStringEncode_BadSurrogates_ReturnsUnicodeReplacementChar() + public void JavaScriptEncode_BadSurrogates_ReturnsUnicodeReplacementChar() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.All); // allow all codepoints + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // allow all codepoints // "abcde" const string Input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; - const string Expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD\\uD800\\uDFFFe\uFFFD"; // 'D800' 'DFFF' was preserved since it's valid + const string Expected = "a\\uFFFDb\\uFFFDc\\uFFFD\\uFFFDd\\uFFFD\\uD800\\uDFFFe\\uFFFD"; // 'D800' 'DFFF' was preserved since it's valid // Act - string retVal = encoder.JavaScriptStringEncode(Input); + string retVal = encoder.Encode(Input); // Assert Assert.Equal(Expected, retVal); @@ -778,20 +670,19 @@ public void JavaScriptEncoder_BadSurrogates_ReturnsUnicodeReplacementChar() // "abcde" const string Input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; - const string Expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD\\uD800\\uDFFFe\uFFFD"; // 'D800' 'DFFF' was preserved since it's valid + const string Expected = "a\\uFFFDb\\uFFFDc\\uFFFD\\uFFFDd\\uFFFD\\uD800\\uDFFFe\\uFFFD"; // 'D800' 'DFFF' was preserved since it's valid // String-based Encode() string retVal = encoder.Encode(Input); Assert.Equal(Expected, retVal); // OperationStatus-based Encode() - Span destination = new char[23]; + Span destination = new char[Expected.Length + 1]; OperationStatus status = encoder.Encode(Input.AsSpan(), destination, out int charsConsumed, out int charsWritten, isFinalBlock: true); Assert.Equal(OperationStatus.Done, status); - Assert.Equal(13, charsConsumed); - Assert.Equal(13, Input.Length); - Assert.Equal(23, charsWritten); - Assert.Equal(Expected, new string(destination.Slice(0, charsWritten).ToArray())); + Assert.Equal(Input.Length, charsConsumed); + Assert.Equal(Expected.Length, charsWritten); + Assert.Equal(Expected, destination.Slice(0, charsWritten).ToString()); } [Fact] @@ -800,11 +691,9 @@ public void JavaScriptEncoder_UnpairedSurrogatesReplaced() // Arrange JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // allow all codepoints - // "a" - const string Input = "a\uDFFF\uD800\uD800"; - const string Expected = "a\uFFFD\uFFFD\uFFFD"; - - Assert.Equal(4, Input.Length); + // "a" + const string Input = "a\uDFFF\uD800\uD800\uFFFD"; + const string Expected = "a\\uFFFD\\uFFFD\\uFFFD\uFFFD"; // final U+FFFD left unchanged since it's well-formed // String-based Encode() string retVal = encoder.Encode(Input); @@ -812,12 +701,12 @@ public void JavaScriptEncoder_UnpairedSurrogatesReplaced() // OperationStatus-based Encode() OperationStatus status; - Span destination = new char[100]; + Span destination = new char[Expected.Length + 1]; status = encoder.Encode(Input.AsSpan(), destination, out int charsConsumed, out int charsWritten, isFinalBlock: true); Assert.Equal(OperationStatus.Done, status); - Assert.Equal(4, charsConsumed); - Assert.Equal(4, charsWritten); - Assert.Equal(Expected, new string(destination.Slice(0, charsWritten).ToArray())); + Assert.Equal(Input.Length, charsConsumed); + Assert.Equal(Expected.Length, charsWritten); + Assert.Equal(Expected, destination.Slice(0, charsWritten).ToString()); } [Fact] @@ -857,82 +746,82 @@ public void JavaScriptEncoder_NeedsMoreData() } [Fact] - public void JavaScriptStringEncode_EmptyStringInput_ReturnsEmptyString() + public void JavaScriptEncode_EmptyStringInput_ReturnsEmptyString() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(); + JavaScriptEncoder encoder = JavaScriptEncoder.Default; // Act & assert - Assert.Equal("", encoder.JavaScriptStringEncode("")); + Assert.Equal("", encoder.Encode("")); } [Fact] - public void JavaScriptStringEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance() + public void JavaScriptEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(); + JavaScriptEncoder encoder = JavaScriptEncoder.Default; string input = "Hello, there!"; // Act & assert - Assert.Same(input, encoder.JavaScriptStringEncode(input)); + Assert.Same(input, encoder.Encode(input)); } [Fact] - public void JavaScriptStringEncode_NullInput_Throws() + public void JavaScriptEncode_NullInput_Throws() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(); + JavaScriptEncoder encoder = JavaScriptEncoder.Default; - Assert.Throws(() => { encoder.JavaScriptStringEncode(null); }); + Assert.Throws(() => { encoder.Encode(null); }); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingAtBeginning() + public void JavaScriptEncode_WithCharsRequiringEncodingAtBeginning() { - Assert.Equal(@"\u0026Hello, there!", new JavaScriptStringEncoder().JavaScriptStringEncode("&Hello, there!")); + Assert.Equal(@"\u0026Hello, there!", JavaScriptEncoder.Default.Encode("&Hello, there!")); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingAtEnd() + public void JavaScriptEncode_WithCharsRequiringEncodingAtEnd() { - Assert.Equal(@"Hello, there!\u0026", new JavaScriptStringEncoder().JavaScriptStringEncode("Hello, there!&")); + Assert.Equal(@"Hello, there!\u0026", JavaScriptEncoder.Default.Encode("Hello, there!&")); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingInMiddle() + public void JavaScriptEncode_WithCharsRequiringEncodingInMiddle() { - Assert.Equal(@"Hello, \u0026there!", new JavaScriptStringEncoder().JavaScriptStringEncode("Hello, &there!")); + Assert.Equal(@"Hello, \u0026there!", JavaScriptEncoder.Default.Encode("Hello, &there!")); } [Fact] - public void JavaScriptStringEncode_WithCharsRequiringEncodingInterspersed() + public void JavaScriptEncode_WithCharsRequiringEncodingInterspersed() { - Assert.Equal(@"Hello, \u003Cthere\u003E!", new JavaScriptStringEncoder().JavaScriptStringEncode("Hello, !")); + Assert.Equal(@"Hello, \u003Cthere\u003E!", JavaScriptEncoder.Default.Encode("Hello, !")); } [Fact] - public void JavaScriptStringEncode_CharArray() + public void JavaScriptEncode_CharArray() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(); + JavaScriptEncoder encoder = JavaScriptEncoder.Default; var output = new StringWriter(); // Act - encoder.JavaScriptStringEncode("Hello+world!".ToCharArray(), 3, 5, output); + encoder.Encode(output, "Hello+world!".ToCharArray(), 3, 5); // Assert Assert.Equal(@"lo\u002Bwo", output.ToString()); } [Fact] - public void JavaScriptStringEncode_StringSubstring() + public void JavaScriptEncode_StringSubstring() { // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(); + JavaScriptEncoder encoder = JavaScriptEncoder.Default; var output = new StringWriter(); // Act - encoder.JavaScriptStringEncode("Hello+world!", 3, 5, output); + encoder.Encode(output, "Hello+world!", 3, 5); // Assert Assert.Equal(@"lo\u002Bwo", output.ToString()); @@ -941,7 +830,7 @@ public void JavaScriptStringEncode_StringSubstring() [Theory] [InlineData("\"", @"\u0022")] [InlineData("'", @"\u0027")] - public void JavaScriptStringEncode_Quotes(string input, string expected) + public void JavaScriptEncode_Quotes(string input, string expected) { // Per the design document, we provide additional defense-in-depth // against breaking out of HTML attributes by having the encoders @@ -949,24 +838,24 @@ public void JavaScriptStringEncode_Quotes(string input, string expected) // \u-escape these characters instead of using \' and \". // Arrange - JavaScriptStringEncoder encoder = new JavaScriptStringEncoder(UnicodeRanges.All); + JavaScriptEncoder encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // Act - string retVal = encoder.JavaScriptStringEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); } [Fact] - public void JavaScriptStringEncode_DoesNotOutputHtmlSensitiveCharacters() + public void JavaScriptEncode_DoesNotOutputHtmlSensitiveCharacters() { // Per the design document, we provide additional defense-in-depth // by never emitting HTML-sensitive characters unescaped. // Arrange - JavaScriptStringEncoder javaScriptStringEncoder = new JavaScriptStringEncoder(UnicodeRanges.All); - HtmlEncoder htmlEncoder = new HtmlEncoder(UnicodeRanges.All); + JavaScriptEncoder javaScriptEncoder = JavaScriptEncoder.Create(UnicodeRanges.All); + HtmlEncoder htmlEncoder = HtmlEncoder.Create(UnicodeRanges.All); // Act & assert for (int i = 0; i <= 0x10FFFF; i++) @@ -976,9 +865,9 @@ public void JavaScriptStringEncode_DoesNotOutputHtmlSensitiveCharacters() continue; // surrogates don't matter here } - string javaScriptStringEncoded = javaScriptStringEncoder.JavaScriptStringEncode(char.ConvertFromUtf32(i)); - string thenHtmlEncoded = htmlEncoder.HtmlEncode(javaScriptStringEncoded); - Assert.Equal(javaScriptStringEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters + string javaScriptEncoded = javaScriptEncoder.Encode(char.ConvertFromUtf32(i)); + string thenHtmlEncoded = htmlEncoder.Encode(javaScriptEncoded); + Assert.Equal(javaScriptEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters } } diff --git a/src/libraries/System.Text.Encodings.Web/tests/PerformanceTests.cs b/src/libraries/System.Text.Encodings.Web/tests/PerformanceTests.cs deleted file mode 100644 index 2d6cef89a0a84..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/PerformanceTests.cs +++ /dev/null @@ -1,169 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using Xunit; -using Xunit.Abstractions; - -#if RELEASE -namespace System.Text.Encodings.Web.Tests -{ - public class PerformanceTests - { - const int SmallIterations = 500000; - const string SmallString = "(T value) - { } - } -} -#endif diff --git a/src/libraries/System.Text.Encodings.Web/tests/ScalarTestEncoder.cs b/src/libraries/System.Text.Encodings.Web/tests/ScalarTestEncoder.cs deleted file mode 100644 index 3481e50b31ace..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/ScalarTestEncoder.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Globalization; -using System.IO; -using System.Runtime.CompilerServices; - -namespace System.Text.Encodings.Web.Tests -{ - /// - /// Dummy encoder used for unit testing. - /// - public sealed class ScalarTestEncoder : TextEncoder - { - private const int Int32Length = 8; - - /// - /// Returns 0. - /// - public override unsafe int FindFirstCharacterToEncode(char* text, int textLength) - { - return text == null ? -1 : 0; - } - - /// - /// Returns true. - /// - public override bool WillEncode(int unicodeScalar) - { - return true; - } - - /// - /// Returns 8. - /// - public override int MaxOutputCharactersPerInputCharacter - { - get { return Int32Length; } - } - - /// - /// Encodes scalar as a hexadecimal number. - /// - public override unsafe bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten) - { - fixed (char* chars = unicodeScalar.ToString("X8")) - for (int i = 0; i < Int32Length; i++) - buffer[i] = chars[i]; - - numberOfCharactersWritten = Int32Length; - return true; - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/SpanUtilityTests.cs b/src/libraries/System.Text.Encodings.Web/tests/SpanUtilityTests.cs new file mode 100644 index 0000000000000..f97779aaa6135 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/SpanUtilityTests.cs @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class SpanUtilityTests + { + public static IEnumerable IsValidIndexTestData() + { + yield return new object[] { "", -1, false }; + yield return new object[] { "", 0, false }; + yield return new object[] { "", 1, false }; + yield return new object[] { "x", -1, false }; + yield return new object[] { "x", 0, true }; + yield return new object[] { "x", 1, false }; + yield return new object[] { "Hello", -1, false }; + yield return new object[] { "Hello", 0, true }; + yield return new object[] { "Hello", 4, true }; + yield return new object[] { "Hello", 5, false }; + } + + [Theory] + [MemberData(nameof(IsValidIndexTestData))] + public void IsValidIndex_ReadOnlySpan(string inputData, int index, bool expectedValue) + { + ReadOnlySpan span = inputData.AsSpan(); + Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index)); + } + + [Theory] + [MemberData(nameof(IsValidIndexTestData))] + public void IsValidIndex_Span(string inputData, int index, bool expectedValue) + { + Span span = inputData.ToCharArray(); + Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index)); + } + + [Fact] + public void TryWriteFourBytes() + { + Span span = stackalloc byte[0]; + Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40)); + + span = stackalloc byte[3] { 100, 101, 102 }; + Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40)); + Assert.Equal(new byte[] { 100, 101, 102 }, span.ToArray()); + + span = stackalloc byte[4] { 100, 101, 102, 103 }; + Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40)); + Assert.Equal(new byte[] { 10, 20, 30, 40 }, span.ToArray()); + + span = stackalloc byte[5] { 100, 101, 102, 103, 104 }; + Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40)); + Assert.Equal(new byte[] { 10, 20, 30, 40, 104 }, span.ToArray()); + } + + [Fact] + public void TryWriteFiveBytes() + { + Span span = stackalloc byte[0]; + Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50)); + + span = stackalloc byte[4] { 100, 101, 102, 103 }; + Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50)); + Assert.Equal(new byte[] { 100, 101, 102, 103 }, span.ToArray()); + + span = stackalloc byte[5] { 100, 101, 102, 103, 104 }; + Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50)); + Assert.Equal(new byte[] { 10, 20, 30, 40, 50 }, span.ToArray()); + + span = stackalloc byte[6] { 100, 101, 102, 103, 104, 105 }; + Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50)); + Assert.Equal(new byte[] { 10, 20, 30, 40, 50, 105 }, span.ToArray()); + } + + [Fact] + public void TryWriteSixBytes() + { + Span span = stackalloc byte[0]; + Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50, 60)); + + span = stackalloc byte[5] { 100, 101, 102, 103, 104 }; + Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50, 60)); + Assert.Equal(new byte[] { 100, 101, 102, 103, 104 }, span.ToArray()); + + span = stackalloc byte[6] { 100, 101, 102, 103, 104, 105 }; + Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50, 60)); + Assert.Equal(new byte[] { 10, 20, 30, 40, 50, 60 }, span.ToArray()); + + span = stackalloc byte[7] { 100, 101, 102, 103, 104, 105, 106 }; + Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50, 60)); + Assert.Equal(new byte[] { 10, 20, 30, 40, 50, 60, 106 }, span.ToArray()); + } + + [Fact] + public void TryWriteFourChars() + { + Span span = stackalloc char[0]; + Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd')); + + span = stackalloc char[3] { '0', '1', '2' }; + Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd')); + Assert.Equal(new char[] { '0', '1', '2' }, span.ToArray()); + + span = stackalloc char[4] { '0', '1', '2', '3' }; + Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd')); + Assert.Equal(new char[] { 'a', 'b', 'c', 'd' }, span.ToArray()); + + span = stackalloc char[5] { '0', '1', '2', '3', '4' }; + Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd')); + Assert.Equal(new char[] { 'a', 'b', 'c', 'd', '4' }, span.ToArray()); + } + + [Fact] + public void TryWriteFiveChars() + { + Span span = stackalloc char[0]; + Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e')); + + span = stackalloc char[4] { '0', '1', '2', '3' }; + Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e')); + Assert.Equal(new char[] { '0', '1', '2', '3' }, span.ToArray()); + + span = stackalloc char[5] { '0', '1', '2', '3', '4' }; + Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e')); + Assert.Equal(new char[] { 'a', 'b', 'c', 'd', 'e' }, span.ToArray()); + + span = stackalloc char[6] { '0', '1', '2', '3', '4', '5' }; + Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e')); + Assert.Equal(new char[] { 'a', 'b', 'c', 'd', 'e', '5' }, span.ToArray()); + } + + [Fact] + public void TryWriteSixChars() + { + Span span = stackalloc char[0]; + Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f')); + + span = stackalloc char[5] { '0', '1', '2', '3', '4' }; + Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f')); + Assert.Equal(new char[] { '0', '1', '2', '3', '4' }, span.ToArray()); + + span = stackalloc char[6] { '0', '1', '2', '3', '4', '5' }; + Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f')); + Assert.Equal(new char[] { 'a', 'b', 'c', 'd', 'e', 'f' }, span.ToArray()); + + span = stackalloc char[7] { '0', '1', '2', '3', '4', '5', '6' }; + Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f')); + Assert.Equal(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', '6' }, span.ToArray()); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(0, -1)] + [InlineData(7, 0)] + [InlineData(7, 1)] + [InlineData(7, -1)] + [InlineData(8, 1)] + [InlineData(8, 8)] + [InlineData(8, -1)] + [InlineData(8, int.MaxValue)] + [InlineData(8, int.MaxValue - 8)] + [InlineData(int.MaxValue, int.MaxValue - 7)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(int.MaxValue, -1)] + [InlineData(int.MaxValue, int.MinValue)] + public unsafe void TryWriteUInt64LittleEndian_FailureCases(int spanLength, int offset) + { + // fabricate a span of the correct length - we can't deref it because it'll AV + Span span = new Span((byte*)null, spanLength); + Assert.False(SpanUtility.TryWriteUInt64LittleEndian(span, offset, 0xdeadbeef_deadbeef)); + } + + [Fact] + public void TryWriteUInt64LittleEndian_SuccessCases() + { + Span span = stackalloc byte[10] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 }; + Assert.True(SpanUtility.TryWriteUInt64LittleEndian(span, 0, 0x10203040_50607080)); + Assert.Equal(new byte[] { 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x08, 0x09 }, span.ToArray()); + + Assert.True(SpanUtility.TryWriteUInt64LittleEndian(span, 1, 0x1a2a3a4a_5a6a7a8a)); + Assert.Equal(new byte[] { 0x80, 0x8a, 0x7a, 0x6a, 0x5a, 0x4a, 0x3a, 0x2a, 0x1a, 0x09 }, span.ToArray()); + + Assert.True(SpanUtility.TryWriteUInt64LittleEndian(span, 2, 0x1f2f3f4f_5f6f7f8f)); + Assert.Equal(new byte[] { 0x80, 0x8a, 0x8f, 0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f }, span.ToArray()); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj b/src/libraries/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj index 5ad0343d6c04e..7c3a5f19b0f3f 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj +++ b/src/libraries/System.Text.Encodings.Web/tests/System.Text.Encodings.Web.Tests.csproj @@ -10,50 +10,53 @@ - - - - + + + + + + + + + + + + + + - - - - - - - - - - + + - - + + + + + CharUnicodeInfo\UnicodeData.$(UnicodeUcdVersion).txt UnicodeData.txt - - - + + + + diff --git a/src/libraries/System.Text.Encodings.Web/tests/TemporaryEncoderAdapters.cs b/src/libraries/System.Text.Encodings.Web/tests/TemporaryEncoderAdapters.cs deleted file mode 100644 index 46645fd1cb821..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/TemporaryEncoderAdapters.cs +++ /dev/null @@ -1,227 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using System.Text.Unicode; - -namespace System.Text.Encodings.Web.Tests -{ - // These implement ASP.NET interfaces. They will be removed once we transition ASP.NET - internal sealed class HtmlEncoder : IHtmlEncoder - { - System.Text.Encodings.Web.HtmlEncoder _encoder; - static HtmlEncoder s_default; - - /// - /// A default instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static HtmlEncoder Default - { - get - { - if (s_default == null) - { - s_default = new HtmlEncoder(); - } - return s_default; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - s_default = value; - } - } - - public HtmlEncoder() - { - _encoder = System.Text.Encodings.Web.HtmlEncoder.Default; - } - public HtmlEncoder(TextEncoderSettings filter) - { - _encoder = System.Text.Encodings.Web.HtmlEncoder.Create(filter); - } - - public HtmlEncoder(UnicodeRange allowedRange) : this(new TextEncoderSettings(allowedRange)) - { } - - public HtmlEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } - - public void HtmlEncode(char[] value, int startIndex, int characterCount, TextWriter output) - { - _encoder.Encode(output, value, startIndex, characterCount); - } - - public string HtmlEncode(string value) - { - return _encoder.Encode(value); - } - - public void HtmlEncode(string value, int startIndex, int characterCount, TextWriter output) - { - _encoder.Encode(output, value, startIndex, characterCount); - } - } - - internal sealed class JavaScriptStringEncoder : IJavaScriptStringEncoder - { - System.Text.Encodings.Web.JavaScriptEncoder _encoder; - static JavaScriptStringEncoder s_default; - static JavaScriptStringEncoder s_relaxed; - - /// - /// A default instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static JavaScriptStringEncoder Default - { - get - { - if (s_default == null) - { - s_default = new JavaScriptStringEncoder(); - } - return s_default; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - s_default = value; - } - } - - /// - /// A relaxed instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static JavaScriptStringEncoder UnsafeRelaxedJsonEscaping - { - get - { - if (s_relaxed == null) - { - s_relaxed = new JavaScriptStringEncoder(relaxed: true); - } - return s_relaxed; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - s_relaxed = value; - } - } - - public JavaScriptStringEncoder(bool relaxed = false) - { - _encoder = relaxed ? JavaScriptEncoder.UnsafeRelaxedJsonEscaping : JavaScriptEncoder.Default; - } - public JavaScriptStringEncoder(TextEncoderSettings filter) - { - _encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(filter); - } - - public JavaScriptStringEncoder(UnicodeRange allowedRange) : this(new TextEncoderSettings(allowedRange)) - { } - - public JavaScriptStringEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } - - public void JavaScriptStringEncode(char[] value, int startIndex, int characterCount, TextWriter output) - { - _encoder.Encode(output, value, startIndex, characterCount); - } - - public string JavaScriptStringEncode(string value) - { - return _encoder.Encode(value); - } - - public void JavaScriptStringEncode(string value, int startIndex, int characterCount, TextWriter output) - { - _encoder.Encode(output, value, startIndex, characterCount); - } - } - - internal sealed class UrlEncoder : IUrlEncoder - { - System.Text.Encodings.Web.UrlEncoder _encoder; - static UrlEncoder s_default; - - /// - /// A default instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static UrlEncoder Default - { - get - { - if (s_default == null) - { - s_default = new UrlEncoder(); - } - return s_default; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - s_default = value; - } - } - - public UrlEncoder() - { - _encoder = System.Text.Encodings.Web.UrlEncoder.Default; - } - public UrlEncoder(TextEncoderSettings filter) - { - _encoder = System.Text.Encodings.Web.UrlEncoder.Create(filter); - } - - public UrlEncoder(UnicodeRange allowedRange) : this(new TextEncoderSettings(allowedRange)) - { } - - public UrlEncoder(params UnicodeRange[] allowedRanges) : this(new TextEncoderSettings(allowedRanges)) - { } - - public void UrlEncode(char[] value, int startIndex, int characterCount, TextWriter output) - { - _encoder.Encode(output, value, startIndex, characterCount); - } - - public string UrlEncode(string value) - { - return _encoder.Encode(value); ; - } - - public void UrlEncode(string value, int startIndex, int characterCount, TextWriter output) - { - _encoder.Encode(output, value, startIndex, characterCount); - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/TemporaryEncoderExtensions.cs b/src/libraries/System.Text.Encodings.Web/tests/TemporaryEncoderExtensions.cs deleted file mode 100644 index 5a58c78e5d348..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/TemporaryEncoderExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; - -namespace System.Text.Encodings.Web.Tests -{ - /// - /// Helpful extension methods for the encoder classes. - /// - internal static class EncoderExtensions - { - /// - /// HTML-encodes a string and writes the result to the supplied output. - /// - /// - /// The encoded value is also safe for inclusion inside an HTML attribute - /// as long as the attribute value is surrounded by single or double quotes. - /// - public static void HtmlEncode(this IHtmlEncoder htmlEncoder, string value, TextWriter output) - { - if (htmlEncoder == null) - { - throw new ArgumentNullException(nameof(htmlEncoder)); - } - - if (!string.IsNullOrEmpty(value)) - { - htmlEncoder.HtmlEncode(value, 0, value.Length, output); - } - } - - /// - /// JavaScript-escapes a string and writes the result to the supplied output. - /// - public static void JavaScriptStringEncode(this IJavaScriptStringEncoder javaScriptStringEncoder, string value, TextWriter output) - { - if (javaScriptStringEncoder == null) - { - throw new ArgumentNullException(nameof(javaScriptStringEncoder)); - } - - if (!string.IsNullOrEmpty(value)) - { - javaScriptStringEncoder.JavaScriptStringEncode(value, 0, value.Length, output); - } - } - - /// - /// URL-encodes a string and writes the result to the supplied output. - /// - /// - /// The encoded value is safe for use in the segment, query, or - /// fragment portion of a URI. - /// - public static void UrlEncode(this IUrlEncoder urlEncoder, string value, TextWriter output) - { - if (urlEncoder == null) - { - throw new ArgumentNullException(nameof(urlEncoder)); - } - - if (!string.IsNullOrEmpty(value)) - { - urlEncoder.UrlEncode(value, 0, value.Length, output); - } - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/TemporaryInternalTypes.cs b/src/libraries/System.Text.Encodings.Web/tests/TemporaryInternalTypes.cs deleted file mode 100644 index 9383f7ecbfbd9..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/TemporaryInternalTypes.cs +++ /dev/null @@ -1,590 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Text.Unicode; -using System.Threading; - -namespace System.Text.Encodings.Web.Tests -{ - /// - /// A class which can perform HTML encoding given an allow list of characters which - /// can be represented unencoded. - /// - /// - /// Instances of this type will always encode a certain set of characters (such as < - /// and >), even if the filter provided in the constructor allows such characters. - /// Once constructed, instances of this class are thread-safe for multiple callers. - /// - internal unsafe sealed class HtmlEncoderOld : IHtmlEncoder - { - // The default HtmlEncoder (Basic Latin), instantiated on demand - private static HtmlEncoderOld _defaultEncoder; - - // The inner encoder, responsible for the actual encoding routines - private readonly HtmlUnicodeEncoder _innerUnicodeEncoder; - - /// - /// Instantiates an encoder using as its allow list. - /// Any character not in the range will be escaped. - /// - public HtmlEncoderOld() - : this(HtmlUnicodeEncoder.BasicLatin) - { - } - - /// - /// Instantiates an encoder specifying which Unicode character ranges are allowed to - /// pass through the encoder unescaped. Any character not in the set of ranges specified - /// by will be escaped. - /// - public HtmlEncoderOld(params UnicodeRange[] allowedRanges) - : this(new HtmlUnicodeEncoder(new TextEncoderSettings(allowedRanges))) - { - } - - /// - /// Instantiates an encoder using a custom code point filter. Any character not in the - /// set returned by 's - /// method will be escaped. - /// - public HtmlEncoderOld(TextEncoderSettings settings) - : this(new HtmlUnicodeEncoder(settings)) - { - } - - private HtmlEncoderOld(HtmlUnicodeEncoder innerEncoder) - { - Debug.Assert(innerEncoder != null); - _innerUnicodeEncoder = innerEncoder; - } - - /// - /// A default instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static HtmlEncoderOld Default - { - get - { - return Volatile.Read(ref _defaultEncoder) ?? CreateDefaultEncoderSlow(); - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - Volatile.Write(ref _defaultEncoder, value); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] // the JITter can attempt to inline the caller itself without worrying about us - private static HtmlEncoderOld CreateDefaultEncoderSlow() - { - var onDemandEncoder = new HtmlEncoderOld(); - return Interlocked.CompareExchange(ref _defaultEncoder, onDemandEncoder, null) ?? onDemandEncoder; - } - - /// - /// Everybody's favorite HtmlEncode routine. - /// - public void HtmlEncode(char[] value, int startIndex, int characterCount, TextWriter output) - { - _innerUnicodeEncoder.Encode(value, startIndex, characterCount, output); - } - - /// - /// Everybody's favorite HtmlEncode routine. - /// - public string HtmlEncode(string value) - { - return _innerUnicodeEncoder.Encode(value); - } - - /// - /// Everybody's favorite HtmlEncode routine. - /// - public void HtmlEncode(string value, int startIndex, int characterCount, TextWriter output) - { - _innerUnicodeEncoder.Encode(value, startIndex, characterCount, output); - } - - private sealed class HtmlUnicodeEncoder : UnicodeEncoderBase - { - // A singleton instance of the basic latin encoder. - private static HtmlUnicodeEncoder _basicLatinSingleton; - - // The worst case encoding is 8 output chars per input char: [input] U+FFFF -> [output] "￿" - // We don't need to worry about astral code points since they consume *two* input chars to - // generate at most 10 output chars ("􏿿"), which equates to 5 output chars per input char. - private const int MaxOutputCharsPerInputChar = 8; - - internal HtmlUnicodeEncoder(TextEncoderSettings filter) - : base(filter, MaxOutputCharsPerInputChar) - { - } - - internal static HtmlUnicodeEncoder BasicLatin - { - get - { - HtmlUnicodeEncoder encoder = Volatile.Read(ref _basicLatinSingleton); - if (encoder == null) - { - encoder = new HtmlUnicodeEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); - Volatile.Write(ref _basicLatinSingleton, encoder); - } - return encoder; - } - } - - // Writes a scalar value as an HTML-encoded entity. - protected override void WriteEncodedScalar(ref Writer writer, uint value) - { - if (value == (uint)'\"') { writer.Write("""); } - else if (value == (uint)'&') { writer.Write("&"); } - else if (value == (uint)'<') { writer.Write("<"); } - else if (value == (uint)'>') { writer.Write(">"); } - else { WriteEncodedScalarAsNumericEntity(ref writer, (int)value); } - } - - // Writes a scalar value as an HTML-encoded numeric entity. - private static void WriteEncodedScalarAsNumericEntity(ref Writer writer, int value) - { - // We're building the characters up in reverse - char* chars = stackalloc char[8 /* "FFFFFFFF" */]; - int numCharsWritten = 0; - do - { - Debug.Assert(numCharsWritten < 8, "Couldn't have written 8 characters out by this point."); - // Pop off the last nibble - chars[numCharsWritten++] = HexConverter.ToCharLower(value); - value >>= 4; - } while (value != 0); - - // Finally, write out the HTML-encoded scalar value. - writer.Write('&'); - writer.Write('#'); - writer.Write('x'); - Debug.Assert(numCharsWritten > 0, "At least one character should've been written."); - do - { - writer.Write(chars[--numCharsWritten]); - } while (numCharsWritten != 0); - writer.Write(';'); - } - } - } - - /// - /// A class which can perform JavaScript string escaping given an allow list of characters which - /// can be represented unescaped. - /// - /// - /// Instances of this type will always encode a certain set of characters (such as ' - /// and "), even if the filter provided in the constructor allows such characters. - /// Once constructed, instances of this class are thread-safe for multiple callers. - /// - internal sealed class JavaScriptStringEncoderOld : IJavaScriptStringEncoder - { - // The default JavaScript string encoder (Basic Latin), instantiated on demand - private static JavaScriptStringEncoderOld _defaultEncoder; - - // The inner encoder, responsible for the actual encoding routines - private readonly JavaScriptStringUnicodeEncoder _innerUnicodeEncoder; - - /// - /// Instantiates an encoder using as its allow list. - /// Any character not in the range will be escaped. - /// - public JavaScriptStringEncoderOld() - : this(JavaScriptStringUnicodeEncoder.BasicLatin) - { - } - - /// - /// Instantiates an encoder specifying which Unicode character ranges are allowed to - /// pass through the encoder unescaped. Any character not in the set of ranges specified - /// by will be escaped. - /// - public JavaScriptStringEncoderOld(params UnicodeRange[] allowedRanges) - : this(new JavaScriptStringUnicodeEncoder(new TextEncoderSettings(allowedRanges))) - { - } - - /// - /// Instantiates an encoder using a custom code point filter. Any character not in the - /// set returned by 's - /// method will be escaped. - /// - public JavaScriptStringEncoderOld(TextEncoderSettings settings) - : this(new JavaScriptStringUnicodeEncoder(settings)) - { - } - - private JavaScriptStringEncoderOld(JavaScriptStringUnicodeEncoder innerEncoder) - { - Debug.Assert(innerEncoder != null); - _innerUnicodeEncoder = innerEncoder; - } - - /// - /// A default instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static JavaScriptStringEncoderOld Default - { - get - { - return Volatile.Read(ref _defaultEncoder) ?? CreateDefaultEncoderSlow(); - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - Volatile.Write(ref _defaultEncoder, value); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] // the JITter can attempt to inline the caller itself without worrying about us - private static JavaScriptStringEncoderOld CreateDefaultEncoderSlow() - { - var onDemandEncoder = new JavaScriptStringEncoderOld(); - return Interlocked.CompareExchange(ref _defaultEncoder, onDemandEncoder, null) ?? onDemandEncoder; - } - - /// - /// Everybody's favorite JavaScriptStringEncode routine. - /// - public void JavaScriptStringEncode(char[] value, int startIndex, int characterCount, TextWriter output) - { - _innerUnicodeEncoder.Encode(value, startIndex, characterCount, output); - } - - /// - /// Everybody's favorite JavaScriptStringEncode routine. - /// - public string JavaScriptStringEncode(string value) - { - return _innerUnicodeEncoder.Encode(value); - } - - /// - /// Everybody's favorite JavaScriptStringEncode routine. - /// - public void JavaScriptStringEncode(string value, int startIndex, int characterCount, TextWriter output) - { - _innerUnicodeEncoder.Encode(value, startIndex, characterCount, output); - } - - private sealed class JavaScriptStringUnicodeEncoder : UnicodeEncoderBase - { - // A singleton instance of the basic latin encoder. - private static JavaScriptStringUnicodeEncoder _basicLatinSingleton; - - // The worst case encoding is 6 output chars per input char: [input] U+FFFF -> [output] "\uFFFF" - // We don't need to worry about astral code points since they're represented as encoded - // surrogate pairs in the output. - private const int MaxOutputCharsPerInputChar = 6; - - internal JavaScriptStringUnicodeEncoder(TextEncoderSettings filter) - : base(filter, MaxOutputCharsPerInputChar) - { - // The only interesting characters above and beyond what the base encoder - // already covers are the solidus and reverse solidus. - ForbidCharacter('\\'); - ForbidCharacter('/'); - } - - internal static JavaScriptStringUnicodeEncoder BasicLatin - { - get - { - JavaScriptStringUnicodeEncoder encoder = Volatile.Read(ref _basicLatinSingleton); - if (encoder == null) - { - encoder = new JavaScriptStringUnicodeEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); - Volatile.Write(ref _basicLatinSingleton, encoder); - } - return encoder; - } - } - - // Writes a scalar value as a JavaScript-escaped character (or sequence of characters). - // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9 - // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 - // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf - protected override void WriteEncodedScalar(ref Writer writer, uint value) - { - // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not. - // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/". - // (In ECMA-262 this character is a NonEscape character.) - // HTML-specific characters (including apostrophe and quotes) will - // be written out as numeric entities for defense-in-depth. - // See UnicodeEncoderBase ctor comments for more info. - - if (value == (uint)'\b') { writer.Write(@"\b"); } - else if (value == (uint)'\t') { writer.Write(@"\t"); } - else if (value == (uint)'\n') { writer.Write(@"\n"); } - else if (value == (uint)'\f') { writer.Write(@"\f"); } - else if (value == (uint)'\r') { writer.Write(@"\r"); } - else if (value == (uint)'/') { writer.Write(@"\/"); } - else if (value == (uint)'\\') { writer.Write(@"\\"); } - else { WriteEncodedScalarAsNumericEntity(ref writer, (int)value); } - } - - // Writes a scalar value as an JavaScript-escaped character (or sequence of characters). - private static void WriteEncodedScalarAsNumericEntity(ref Writer writer, int value) - { - if (UnicodeHelpers.IsSupplementaryCodePoint(value)) - { - // Convert this back to UTF-16 and write out both characters. - char leadingSurrogate, trailingSurrogate; - UnicodeHelpers.GetUtf16SurrogatePairFromAstralScalarValue(value, out leadingSurrogate, out trailingSurrogate); - WriteEncodedSingleCharacter(ref writer, leadingSurrogate); - WriteEncodedSingleCharacter(ref writer, trailingSurrogate); - } - else - { - // This is only a single character. - WriteEncodedSingleCharacter(ref writer, value); - } - } - - // Writes an encoded scalar value (in the BMP) as a JavaScript-escaped character. - private static void WriteEncodedSingleCharacter(ref Writer writer, int value) - { - Debug.Assert(!UnicodeHelpers.IsSupplementaryCodePoint(value), "The incoming value should've been in the BMP."); - - // Encode this as 6 chars "\uFFFF". - writer.Write('\\'); - writer.Write('u'); - writer.Write(HexConverter.ToCharLower(value >> 12)); - writer.Write(HexConverter.ToCharLower(value >> 8)); - writer.Write(HexConverter.ToCharLower(value >> 4)); - writer.Write(HexConverter.ToCharLower(value)); - } - } - } - - - /// - /// A class which can perform URL string escaping given an allow list of characters which - /// can be represented unescaped. - /// - /// - /// Instances of this type will always encode a certain set of characters (such as + - /// and ?), even if the filter provided in the constructor allows such characters. - /// Once constructed, instances of this class are thread-safe for multiple callers. - /// - internal sealed class UrlEncoderOld : IUrlEncoder - { - // The default URL string encoder (Basic Latin), instantiated on demand - private static UrlEncoderOld _defaultEncoder; - - // The inner encoder, responsible for the actual encoding routines - private readonly UrlUnicodeEncoder _innerUnicodeEncoder; - - /// - /// Instantiates an encoder using as its allow list. - /// Any character not in the range will be escaped. - /// - public UrlEncoderOld() - : this(UrlUnicodeEncoder.BasicLatin) - { - } - - /// - /// Instantiates an encoder specifying which Unicode character ranges are allowed to - /// pass through the encoder unescaped. Any character not in the set of ranges specified - /// by will be escaped. - /// - public UrlEncoderOld(params UnicodeRange[] allowedRanges) - : this(new UrlUnicodeEncoder(new TextEncoderSettings(allowedRanges))) - { - } - - /// - /// Instantiates an encoder using a custom code point filter. Any character not in the - /// set returned by 's - /// method will be escaped. - /// - public UrlEncoderOld(TextEncoderSettings settings) - : this(new UrlUnicodeEncoder(settings)) - { - } - - private UrlEncoderOld(UrlUnicodeEncoder innerEncoder) - { - Debug.Assert(innerEncoder != null); - _innerUnicodeEncoder = innerEncoder; - } - - /// - /// A default instance of . - /// - /// - /// This normally corresponds to . However, this property is - /// settable so that a developer can change the default implementation application-wide. - /// - public static UrlEncoderOld Default - { - get - { - return Volatile.Read(ref _defaultEncoder) ?? CreateDefaultEncoderSlow(); - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - Volatile.Write(ref _defaultEncoder, value); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] // the JITter can attempt to inline the caller itself without worrying about us - private static UrlEncoderOld CreateDefaultEncoderSlow() - { - var onDemandEncoder = new UrlEncoderOld(); - return Interlocked.CompareExchange(ref _defaultEncoder, onDemandEncoder, null) ?? onDemandEncoder; - } - - /// - /// Everybody's favorite UrlEncode routine. - /// - public void UrlEncode(char[] value, int startIndex, int characterCount, TextWriter output) - { - _innerUnicodeEncoder.Encode(value, startIndex, characterCount, output); - } - - /// - /// Everybody's favorite UrlEncode routine. - /// - public string UrlEncode(string value) - { - return _innerUnicodeEncoder.Encode(value); - } - - /// - /// Everybody's favorite UrlEncode routine. - /// - public void UrlEncode(string value, int startIndex, int characterCount, TextWriter output) - { - _innerUnicodeEncoder.Encode(value, startIndex, characterCount, output); - } - - private sealed class UrlUnicodeEncoder : UnicodeEncoderBase - { - // A singleton instance of the basic latin encoder. - private static UrlUnicodeEncoder _basicLatinSingleton; - - // We perform UTF8 conversion of input, which means that the worst case is - // 9 output chars per input char: [input] U+FFFF -> [output] "%XX%YY%ZZ". - // We don't need to worry about astral code points since they consume 2 input - // chars to produce 12 output chars "%XX%YY%ZZ%WW", which is 6 output chars per input char. - private const int MaxOutputCharsPerInputChar = 9; - - internal UrlUnicodeEncoder(TextEncoderSettings filter) - : base(filter, MaxOutputCharsPerInputChar) - { - // Per RFC 3987, Sec. 2.2, we want encodings that are safe for - // four particular components: 'isegment', 'ipath-noscheme', - // 'iquery', and 'ifragment'. The relevant definitions are below. - // - // ipath-noscheme = isegment-nz-nc *( "/" isegment ) - // - // isegment = *ipchar - // - // isegment-nz-nc = 1*( iunreserved / pct-encoded / sub-delims - // / "@" ) - // ; non-zero-length segment without any colon ":" - // - // ipchar = iunreserved / pct-encoded / sub-delims / ":" - // / "@" - // - // iquery = *( ipchar / iprivate / "/" / "?" ) - // - // ifragment = *( ipchar / "/" / "?" ) - // - // iunreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" / ucschar - // - // ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF - // / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD - // / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD - // / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD - // / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD - // / %xD0000-DFFFD / %xE1000-EFFFD - // - // pct-encoded = "%" HEXDIG HEXDIG - // - // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - // / "*" / "+" / "," / ";" / "=" - // - // The only common characters between these four components are the - // intersection of 'isegment-nz-nc' and 'ipchar', which is really - // just 'isegment-nz-nc' (colons forbidden). - // - // From this list, the base encoder already forbids "&", "'", "+", - // and we'll additionally forbid "=" since it has special meaning - // in x-www-form-urlencoded representations. - // - // This means that the full list of allowed characters from the - // Basic Latin set is: - // ALPHA / DIGIT / "-" / "." / "_" / "~" / "!" / "$" / "(" / ")" / "*" / "," / ";" / "@" - - const string forbiddenChars = @" #%/:=?[\]^`{|}"; // chars from Basic Latin which aren't already disallowed by the base encoder - foreach (char c in forbiddenChars) - { - ForbidCharacter(c); - } - - // Specials (U+FFF0 .. U+FFFF) are forbidden by the definition of 'ucschar' above - for (int i = 0; i < 16; i++) - { - ForbidCharacter((char)(0xFFF0 | i)); - } - - // Supplementary characters are forbidden anyway by the base encoder - } - - internal static UrlUnicodeEncoder BasicLatin - { - get - { - UrlUnicodeEncoder encoder = Volatile.Read(ref _basicLatinSingleton); - if (encoder == null) - { - encoder = new UrlUnicodeEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin)); - Volatile.Write(ref _basicLatinSingleton, encoder); - } - return encoder; - } - } - - // Writes a scalar value as a percent-encoded sequence of UTF8 bytes, per RFC 3987. - protected override void WriteEncodedScalar(ref Writer writer, uint value) - { - uint asUtf8 = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue(value); - do - { - writer.Write('%'); - writer.Write(HexConverter.ToCharLower((int)(asUtf8 >> 4))); - writer.Write(HexConverter.ToCharLower((int)asUtf8)); - } while ((asUtf8 >>= 8) != 0); - } - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs new file mode 100644 index 0000000000000..5a8f3e6a2df4d --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderBatteryTests.cs @@ -0,0 +1,241 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using Xunit; + +namespace System.Text.Encodings.Web.Tests +{ + public class TextEncoderBatteryTests + { + private static TextEncoder GetBatteryTextEncoder() + { + // only even-valued scalars are allowed; odd-valued scalars are disallowed + return new ConfigurableScalarTextEncoder(scalarValue => scalarValue % 2 == 0); + } + + // 2 elements: [0] = input data (string), [1] = expected output data (string) + public static IEnumerable TestData() + { + static IEnumerable<(string input, string output)> RealTestData() + { + yield return ("", ""); + yield return ("xyz", "x[0079]z"); + yield return ("bdf", "bdf"); + yield return ("bdfbdfbdfbdfbdf", "bdfbdfbdfbdfbdf"); + yield return ("\U0001F600" /* grinning face */, "\U0001F600"); // not escaped since scalar value is even + yield return ("\U0001F601" /* grinning face with smiling eyes */, "[1F601]"); // escaped since scalar value is odd + yield return ("\U0001F3C0\U0001F3C1\U0001F3C2\U0001F3C3\U0001F3C4" /* various sports emoji */, + "\U0001F3C0[1F3C1]\U0001F3C2[1F3C3]\U0001F3C4"); + yield return ("bd\ud800fh", "bd[FFFD]fh"); // standalone high surrogate char + yield return ("bd\udffffh", "bd[FFFD]fh"); // standalone low surrogate char + yield return ("bd\ue000fh", "bd\ue000fh"); + yield return ("bd\ue001fh", "bd[E001]fh"); + yield return ("bd\udfd0\ud83c\udfd0\ud83cfh", "bd[FFFD]\U0001F3D0[FFFD]fh"); // U+1F3D0 VOLLEYBALL + yield return ("bd\udfd1\ud83c\udfd1\ud83cfh", "bd[FFFD][1F3D1][FFFD]fh"); // U+1F3D1 FIELD HOCKEY STICK AND BALL + yield return ("\ufffd\ud800\ufffd", "[FFFD][FFFD][FFFD]"); // U+FFFD is escaped since is odd + yield return ("xyz\ud800", "x[0079]z[FFFD]"); // ends with standalone high surrogate char + yield return ("xyz\udfff", "x[0079]z[FFFD]"); // ends with standalone low surrogate char + yield return ("xyz\U0001F3C0", "x[0079]z\U0001F3C0"); // ends with valid surrogate pair + + // really long input which does not need to be escaped + { + StringBuilder sb = new StringBuilder(); + + for (int i = 0x40; i < 0x4000; i += 2) + { + sb.Append((char)i); + } + + yield return (sb.ToString(), sb.ToString()); + } + + // really long input which needs to be escaped + { + StringBuilder sbInput = new StringBuilder(); + StringBuilder sbOutput = new StringBuilder(); + + for (int i = 0x40; i < 0x4000; i++) + { + sbInput.Append((char)i); + if (i % 2 == 0) + { + sbOutput.Append((char)i); + } + else + { + sbOutput.AppendFormat(CultureInfo.InvariantCulture, "[{0:X4}]", i); + } + } + + yield return (sbInput.ToString(), sbOutput.ToString()); + } + + // really long input which contains surrogate chars (no escape needed) + // also offset everything by 1 to account for the TextEncoder inner loop's + // "needs more data" handling logic. + { + StringBuilder sb = new StringBuilder(); + + for (int i = 0x10000; i < 0x14000; i += 2) + { + sb.Append(char.ConvertFromUtf32(i)); + } + + yield return (sb.ToString(), sb.ToString()); + yield return ("x" + sb.ToString(), "x" + sb.ToString()); + } + } + + foreach ((string input, string output) in RealTestData()) + { + yield return new[] { Escape(input), Escape(output) }; + } + } + + [Theory] + [MemberData(nameof(TestData))] + public void Encode_String(string input, string expectedOutput) + { + input = Unescape(input); + expectedOutput = Unescape(expectedOutput); + + // Arrange + + TextEncoder encoder = GetBatteryTextEncoder(); + + // Act + + string actualOutput = encoder.Encode(input); + + // Assert + + Assert.Equal(expectedOutput, actualOutput); + } + + [Theory] + [MemberData(nameof(TestData))] + public void Encode_TextWriter_String(string input, string expectedOutput) + { + input = Unescape(input); + expectedOutput = Unescape(expectedOutput); + + // Arrange + + TextEncoder encoder = GetBatteryTextEncoder(); + StringWriter writer = new StringWriter(); + + // Act + + encoder.Encode(writer, input); + + // Assert + + Assert.Equal(expectedOutput, writer.ToString()); + } + + [Theory] + [MemberData(nameof(TestData))] + public void Encode_TextWriter_String_WithOffset(string input, string expectedOutput) + { + input = Unescape(input); + expectedOutput = Unescape(expectedOutput); + + // Arrange + + TextEncoder encoder = GetBatteryTextEncoder(); + StringWriter writer; + + // Act & assert - 1 + + writer = new StringWriter(); + encoder.Encode(writer, input, 0, input.Length); + Assert.Equal(expectedOutput, writer.ToString()); + + // Act & assert - 2 + + writer = new StringWriter(); + encoder.Encode(writer, "xxx" + input + "yyy", 3, input.Length); + Assert.Equal(expectedOutput, writer.ToString()); + + // Act & assert - 3 + + writer = new StringWriter(); + encoder.Encode(writer, "\ud800" + input + "\udfff", 1, input.Length); + Assert.Equal(expectedOutput, writer.ToString()); + } + + [Theory] + [MemberData(nameof(TestData))] + public void Encode_TextWriter_CharArray_WithOffset(string input, string expectedOutput) + { + input = Unescape(input); + expectedOutput = Unescape(expectedOutput); + + // Arrange + + TextEncoder encoder = GetBatteryTextEncoder(); + StringWriter writer; + + // Act & assert - 1 + + writer = new StringWriter(); + encoder.Encode(writer, input.ToCharArray(), 0, input.Length); + Assert.Equal(expectedOutput, writer.ToString()); + + // Act & assert - 2 + + writer = new StringWriter(); + encoder.Encode(writer, ("xxx" + input + "yyy").ToCharArray(), 3, input.Length); + Assert.Equal(expectedOutput, writer.ToString()); + + // Act & assert - 3 + + writer = new StringWriter(); + encoder.Encode(writer, ("\ud800" + input + "\udfff").ToCharArray(), 1, input.Length); + Assert.Equal(expectedOutput, writer.ToString()); + } + + /* + * ESCAPING & UNESCAPING + * ===================== + * + * The xunit runner doesn't like strings that contain malformed UTF-16 data. + * To smuggle malformed UTF-16 data across the test runner, we'll encode all surrogate + * chars (not supplementary chars) as @XXXX. A supplementary char is thus represented + * as @XXXX@YYYY (10 chars total) in the stream. + */ + + private static string Escape(string value) + { + value = value.Replace(@"@", @"@0040"); + StringBuilder sb = new StringBuilder(value.Length); + foreach (char ch in value) + { + sb.Append(char.IsSurrogate(ch) ? FormattableString.Invariant($@"@{(int)ch:X4}") : ch); + } + return sb.ToString(); + } + + private static string Unescape(string value) + { + StringBuilder sb = new StringBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + { + char ch = value[i]; + if (ch != '@') + { + sb.Append(ch); + } + else + { + sb.Append((char)ushort.Parse(value.Substring(i + 1, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture)); + i += 4; + } + } + return sb.ToString(); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderSettingsTests.cs b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderSettingsTests.cs index 49a4be7238e93..b7945bfaf00b4 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderSettingsTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderSettingsTests.cs @@ -1,11 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text.Internal; using System.Text.Unicode; using Xunit; @@ -13,16 +11,23 @@ namespace System.Text.Encodings.Web.Tests { internal static class TextEncoderSettingsExtensions { - public static AllowedCharactersBitmap GetAllowedCharacters(this TextEncoderSettings settings) + private static readonly Lazy _lazyGetBitmapFnPtr = new Lazy(InitializeGetBitmapFnPtr); + + private static IntPtr InitializeGetBitmapFnPtr() + { + var mi = typeof(TextEncoderSettings).GetMethod("GetAllowedCodePointsBitmap", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + return mi.MethodHandle.GetFunctionPointer(); + } + + public static unsafe ref readonly AllowedBmpCodePointsBitmap GetAllowedBmpCodePointsBitmap(this TextEncoderSettings settings) { - object bitmap = settings.GetType().InvokeMember("GetAllowedCharacters", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, settings, null); - object underlyingArray = bitmap.GetType().GetField("_allowedCharacters", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bitmap); - return (AllowedCharactersBitmap)typeof(AllowedCharactersBitmap).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(uint[]) }, null).Invoke(new object[] { underlyingArray }); + IntPtr getBitmapFnPtr = _lazyGetBitmapFnPtr.Value; + return ref ((delegate* managed)getBitmapFnPtr)(settings); } public static bool IsCharacterAllowed(this TextEncoderSettings settings, char character) { - return GetAllowedCharacters(settings).IsCharacterAllowed(character); + return GetAllowedBmpCodePointsBitmap(settings).IsCharAllowed(character); } } @@ -402,6 +407,22 @@ public void GetAllowedCodePoints() Assert.Equal(expected, retVal); } + [Fact] + public void GetAllowedCodePointsBmp_WhenSubclassed() + { + // Arrange + var settings = new OddTextEncoderSettings(); + + // Act + ref readonly var bitmap = ref settings.GetAllowedBmpCodePointsBitmap(); + + // Assert + for (int i = 0; i <= char.MaxValue; i++) + { + Assert.Equal(i % 2 == 1, bitmap.IsCharAllowed((char)i)); // only odd chars are allowed + } + } + // a code point filter which allows only odd code points through private sealed class OddTextEncoderSettings : TextEncoderSettings { diff --git a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderTests.cs b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderTests.cs index e841e746c505a..35166fb19c13c 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/TextEncoderTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/TextEncoderTests.cs @@ -1,11 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Buffers; using System.Collections.Generic; using System.Linq; -using System.Text; using Xunit; namespace System.Text.Encodings.Web.Tests @@ -16,13 +14,13 @@ public class TextEncoderTests public void EncodeIntoBuffer_SurrogatePairs() { // Arange - ScalarTestEncoder encoder = new ScalarTestEncoder(); + TextEncoder encoder = new ConfigurableScalarTextEncoder(_ => false); const string X = "\U00000058"; // LATIN CAPITAL LETTER X (ascii) const string Pair = "\U0001033A"; // GOTHIC LETTER KUSMA (surrogate pair) - const string eX = "00000058"; - const string ePair = "0001033A"; + const string eX = "[0058]"; + const string ePair = "[1033A]"; // Act & assert Assert.Equal("", encoder.Encode("")); @@ -55,6 +53,7 @@ public void EncodeIntoBuffer_SurrogatePairs() [InlineData(10, 10)] [InlineData(11, 11)] [InlineData(12, 11)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void EncodeUtf8_WellFormedInput_DoesNotRequireEncoding_CopiedToDestinationCorrectly(int destinationSize, int expectedBytesCopied) { // This test considers input which is well-formed and doesn't need to be encoded. @@ -83,14 +82,12 @@ public void EncodeUtf8_WellFormedInput_DoesNotRequireEncoding_CopiedToDestinatio Assert.Equal(expectedBytesCopied, bytesConsumed); Assert.Equal(expectedBytesCopied, bytesWritten); // bytes written should match bytes consumed if no encoding needs to take place Assert.Equal(fullUtf8Input.AsSpan(0, bytesConsumed).ToArray(), destination.AsSpan(0, bytesWritten).ToArray()); // ensure byte-for-byte copy - Assert.True(destination.AsSpan(bytesWritten).ToArray().All(el => el == 0)); // all remaining bytes should be unchanged destination = new byte[destinationSize]; Assert.Equal(expectedOpStatus, encoder.EncodeUtf8(fullUtf8Input, destination, out bytesConsumed, out bytesWritten, isFinalBlock: false)); Assert.Equal(expectedBytesCopied, bytesConsumed); Assert.Equal(expectedBytesCopied, bytesWritten); // bytes written should match bytes consumed if no encoding needs to take place Assert.Equal(fullUtf8Input.AsSpan(0, bytesConsumed).ToArray(), destination.AsSpan(0, bytesWritten).ToArray()); // ensure byte-for-byte copy - Assert.True(destination.AsSpan(bytesWritten).ToArray().All(el => el == 0)); // all remaining bytes should be unchanged } [Fact] @@ -179,7 +176,7 @@ public void EncodeUtf8_MixedInputWhichRequiresEncodingOrReplacement() { destination = new byte[destinationLength]; - Assert.Equal(OperationStatus.Done, encoder.EncodeUtf8(aggregateInputBytesSoFar.ToArray(), destination, out bytesConsumed, out bytesWritten, isFinalBlock: false)); + Assert.Equal(OperationStatus.Done, encoder.EncodeUtf8(aggregateInputBytesSoFar.ToArray(), destination, out bytesConsumed, out bytesWritten, isFinalBlock: false)); Assert.Equal(aggregateInputBytesSoFar.Count, bytesConsumed); Assert.Equal(expectedOutputBytesSoFar.Count, bytesWritten); Assert.Equal(expectedOutputBytesSoFar.ToArray(), new Span(destination, 0, expectedOutputBytesSoFar.Count).ToArray()); @@ -261,6 +258,7 @@ public void FindFirstCharToEncodeUtf8_WellFormedData_SomeCharsDisallowed() [InlineData(new byte[] { 0xF1, 0x80, 0x80 }, 0)] [InlineData(new byte[] { 0xF1, 0x80, 0x80, 0x80, 0xFF }, 4)] [InlineData(new byte[] { 0xFF, 0x80, 0x80, 0x80, 0xFF }, 0)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void FindFirstCharToEncodeUtf8_IllFormedData_ReturnsIndexOfIllFormedSubsequence(byte[] utf8Data, int expectedIndex) { // Arrange @@ -275,5 +273,87 @@ public void FindFirstCharToEncodeUtf8_IllFormedData_ReturnsIndexOfIllFormedSubse Assert.Equal(expectedIndex, actualIndex); } + + [Theory] + [InlineData("", 0, "", 0, OperationStatus.Done)] + [InlineData("", 20, "", 0, OperationStatus.Done)] + [InlineData("ABC", 0, "", 0, OperationStatus.DestinationTooSmall)] + [InlineData("ABC", 2, "AB", 2, OperationStatus.DestinationTooSmall)] + [InlineData("ABC", 3, "ABC", 3, OperationStatus.Done)] + [InlineData("ABC", 30, "ABC", 3, OperationStatus.Done)] + [InlineData("ABC+DEF", 3, "ABC", 3, OperationStatus.DestinationTooSmall)] + [InlineData("ABC+DEF", 8, "ABC", 3, OperationStatus.DestinationTooSmall)] + [InlineData("ABC+DEF", 9, "ABC[002B]", 4, OperationStatus.DestinationTooSmall)] + [InlineData("ABC+DEF", 12, "ABC[002B]DEF", 7, OperationStatus.Done)] + public void EncodeUtf16_OperationStatus_AlphaNumericOnly(string input, int destBufferSize, string expectedOutput, int expectedCharsConsumed, OperationStatus expectedResult) + { + // Arrange + + var encoder = new ConfigurableScalarTextEncoder(scalar => UnicodeUtility.IsInRangeInclusive((uint)scalar | 0x20, 'a', 'z')); // allow only [A-Za-z] unescaped + using BoundedMemory boundedInput = BoundedMemory.AllocateFromExistingData(input.AsSpan()); + using BoundedMemory boundedOutput = BoundedMemory.Allocate(destBufferSize); + + // Act + + OperationStatus actualResult = encoder.Encode(boundedInput.Span, boundedOutput.Span, out int actualCharsConsumed, out int actualCharsWritten); + + // Assert + + Assert.Equal(expectedResult, actualResult); + Assert.Equal(expectedCharsConsumed, actualCharsConsumed); + Assert.Equal(expectedOutput, boundedOutput.Span.Slice(0, actualCharsWritten).ToString()); + } + + [Theory] + [InlineData("ABC\U0001F600", 4, "ABC", 3, OperationStatus.DestinationTooSmall)] // don't allow breaking across a surrogate + [InlineData("ABC\U0001F600", 5, "ABC\U0001F600", 5, OperationStatus.Done)] + public void EncodeUtf16_OperationStatus_AllowEverything(string input, int destBufferSize, string expectedOutput, int expectedCharsConsumed, OperationStatus expectedResult) + { + // Arrange + + var encoder = new ConfigurableScalarTextEncoder(_ => true); // allow all well-formed scalars + using BoundedMemory boundedInput = BoundedMemory.AllocateFromExistingData(input.AsSpan()); + using BoundedMemory boundedOutput = BoundedMemory.Allocate(destBufferSize); + + // Act + + OperationStatus actualResult = encoder.Encode(boundedInput.Span, boundedOutput.Span, out int actualCharsConsumed, out int actualCharsWritten); + + // Assert + + Assert.Equal(expectedResult, actualResult); + Assert.Equal(expectedCharsConsumed, actualCharsConsumed); + Assert.Equal(expectedOutput, boundedOutput.Span.Slice(0, actualCharsWritten).ToString()); + } + + [Theory] + [InlineData(new[] { 'A', 'B', '\ud83d' }, 2, true, "AB", 2, OperationStatus.DestinationTooSmall)] + [InlineData(new[] { 'A', 'B', '\ud83d' }, 2, false, "AB", 2, OperationStatus.NeedMoreData)] + [InlineData(new[] { 'A', 'B', '\ud83d' }, 3, true, "AB", 2, OperationStatus.DestinationTooSmall)] + [InlineData(new[] { 'A', 'B', '\ud83d' }, 3, false, "AB", 2, OperationStatus.NeedMoreData)] + [InlineData(new[] { 'A', 'B', '\ud83d' }, 10, true, "AB[FFFD]", 3, OperationStatus.Done)] + [InlineData(new[] { 'A', 'B', '\ud83d' }, 10, false, "AB", 2, OperationStatus.NeedMoreData)] + [InlineData(new[] { 'A', 'B', '\ud83d', '\ude00' }, 2, true, "AB", 2, OperationStatus.DestinationTooSmall)] + [InlineData(new[] { 'A', 'B', '\ud83d', '\ude00' }, 2, false, "AB", 2, OperationStatus.DestinationTooSmall)] + [InlineData(new[] { 'A', 'B', '\ud83d', '\ude00' }, 4, true, "AB\U0001F600", 4, OperationStatus.Done)] + [InlineData(new[] { 'A', 'B', '\ud83d', '\ude00' }, 4, false, "AB\U0001F600", 4, OperationStatus.Done)] + public void EncodeUtf16_OperationStatus_SurrogateHandlingEdgeCases(char[] input, int destBufferSize, bool isFinalBlock, string expectedOutput, int expectedCharsConsumed, OperationStatus expectedResult) + { + // Arrange + + var encoder = new ConfigurableScalarTextEncoder(_ => true); // allow all well-formed scalars + using BoundedMemory boundedInput = BoundedMemory.AllocateFromExistingData(input); + using BoundedMemory boundedOutput = BoundedMemory.Allocate(destBufferSize); + + // Act + + OperationStatus actualResult = encoder.Encode(boundedInput.Span, boundedOutput.Span, out int actualCharsConsumed, out int actualCharsWritten, isFinalBlock); + + // Assert + + Assert.Equal(expectedResult, actualResult); + Assert.Equal(expectedCharsConsumed, actualCharsConsumed); + Assert.Equal(expectedOutput, boundedOutput.Span.Slice(0, actualCharsWritten).ToString()); + } } } diff --git a/src/libraries/System.Text.Encodings.Web/tests/UnicodeEncoderBase.cs b/src/libraries/System.Text.Encodings.Web/tests/UnicodeEncoderBase.cs deleted file mode 100644 index 7e9a30b6aa184..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/UnicodeEncoderBase.cs +++ /dev/null @@ -1,303 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Internal; -using System.Text.Unicode; - -namespace System.Text.Encodings.Web.Tests -{ - internal unsafe abstract class UnicodeEncoderBase - { - // A bitmap of characters which are allowed to be returned unescaped. - private AllowedCharactersBitmap _allowedCharacters; - - // The worst-case number of output chars generated for any input char. - private readonly int _maxOutputCharsPerInputChar; - - /// - /// Instantiates an encoder using a custom allow list of characters. - /// - protected UnicodeEncoderBase(TextEncoderSettings filter, int maxOutputCharsPerInputChar) - { - _maxOutputCharsPerInputChar = maxOutputCharsPerInputChar; - _allowedCharacters = filter.GetAllowedCharacters(); - - // Forbid characters that are special in HTML. - // Even though this is a common encoder used by everybody (including URL - // and JavaScript strings), it's unfortunately common for developers to - // forget to HTML-encode a string once it has been URL-encoded or - // JavaScript string-escaped, so this offers extra protection. - ForbidCharacter('<'); - ForbidCharacter('>'); - ForbidCharacter('&'); - ForbidCharacter('\''); // can be used to escape attributes - ForbidCharacter('\"'); // can be used to escape attributes - ForbidCharacter('+'); // technically not HTML-specific, but can be used to perform UTF7-based attacks - - // Forbid codepoints which aren't mapped to characters or which are otherwise always disallowed - // (includes categories Cc, Cs, Co, Cn, Zs [except U+0020 SPACE], Zl, Zp) - _allowedCharacters.ForbidUndefinedCharacters(); - } - - // Marks a character as forbidden (must be returned encoded) - protected void ForbidCharacter(char c) - { - _allowedCharacters.ForbidCharacter(c); - } - - /// - /// Entry point to the encoder. - /// - public void Encode(char[] value, int startIndex, int characterCount, TextWriter output) - { - // Input checking - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - if (output == null) - { - throw new ArgumentNullException(nameof(output)); - } - ValidateInputs(startIndex, characterCount, actualInputLength: value.Length); - - if (characterCount != 0) - { - fixed (char* pChars = value) - { - int indexOfFirstCharWhichRequiresEncoding = GetIndexOfFirstCharWhichRequiresEncoding(&pChars[startIndex], characterCount); - if (indexOfFirstCharWhichRequiresEncoding < 0) - { - // All chars are valid - just copy the buffer as-is. - output.Write(value, startIndex, characterCount); - } - else - { - // Flush all chars which are known to be valid, then encode the remainder individually - if (indexOfFirstCharWhichRequiresEncoding > 0) - { - output.Write(value, startIndex, indexOfFirstCharWhichRequiresEncoding); - } - EncodeCore(&pChars[startIndex + indexOfFirstCharWhichRequiresEncoding], (uint)(characterCount - indexOfFirstCharWhichRequiresEncoding), output); - } - } - } - } - - /// - /// Entry point to the encoder. - /// - public string Encode(string value) - { - if (string.IsNullOrEmpty(value)) - { - return value; - } - - // Quick check: does the string need to be encoded at all? - // If not, just return the input string as-is. - for (int i = 0; i < value.Length; i++) - { - if (!IsCharacterAllowed(value[i])) - { - return EncodeCore(value, idxOfFirstCharWhichRequiresEncoding: i); - } - } - return value; - } - - /// - /// Entry point to the encoder. - /// - public void Encode(string value, int startIndex, int characterCount, TextWriter output) - { - // Input checking - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - if (output == null) - { - throw new ArgumentNullException(nameof(output)); - } - ValidateInputs(startIndex, characterCount, actualInputLength: value.Length); - - if (characterCount != 0) - { - fixed (char* pChars = value) - { - if (characterCount == value.Length) - { - // Optimize for the common case: we're being asked to encode the entire input string - // (not just a subset). If all characters are safe, we can just spit it out as-is. - int indexOfFirstCharWhichRequiresEncoding = GetIndexOfFirstCharWhichRequiresEncoding(pChars, characterCount); - if (indexOfFirstCharWhichRequiresEncoding < 0) - { - output.Write(value); - } - else - { - // Flush all chars which are known to be valid, then encode the remainder individually - for (int i = 0; i < indexOfFirstCharWhichRequiresEncoding; i++) - { - output.Write(pChars[i]); - } - EncodeCore(&pChars[indexOfFirstCharWhichRequiresEncoding], (uint)(characterCount - indexOfFirstCharWhichRequiresEncoding), output); - } - } - else - { - // We're being asked to encode a subset, so we need to go through the slow path of appending - // each character individually. - EncodeCore(&pChars[startIndex], (uint)characterCount, output); - } - } - } - } - - private string EncodeCore(string input, int idxOfFirstCharWhichRequiresEncoding) - { - Debug.Assert(idxOfFirstCharWhichRequiresEncoding >= 0); - Debug.Assert(idxOfFirstCharWhichRequiresEncoding < input.Length); - - int numCharsWhichMayRequireEncoding = input.Length - idxOfFirstCharWhichRequiresEncoding; - int sbCapacity = checked(idxOfFirstCharWhichRequiresEncoding + EncoderCommon.GetCapacityOfOutputStringBuilder(numCharsWhichMayRequireEncoding, _maxOutputCharsPerInputChar)); - Debug.Assert(sbCapacity >= input.Length); - - // Allocate the StringBuilder with the first (known to not require encoding) part of the input string, - // then begin encoding from the last (potentially requiring encoding) part of the input string. - StringBuilder builder = new StringBuilder(input, 0, idxOfFirstCharWhichRequiresEncoding, sbCapacity); - Writer writer = new Writer(builder); - fixed (char* pInput = input) - { - EncodeCore(ref writer, &pInput[idxOfFirstCharWhichRequiresEncoding], (uint)numCharsWhichMayRequireEncoding); - } - return builder.ToString(); - } - - private void EncodeCore(char* input, uint charsRemaining, TextWriter output) - { - Writer writer = new Writer(output); - EncodeCore(ref writer, input, charsRemaining); - } - - private void EncodeCore(ref Writer writer, char* input, uint charsRemaining) - { - while (charsRemaining != 0) - { - int nextScalar = UnicodeHelpers.GetScalarValueFromUtf16(input, endOfString: (charsRemaining == 1)); - if (UnicodeHelpers.IsSupplementaryCodePoint(nextScalar)) - { - // Supplementary characters should always be encoded numerically. - WriteEncodedScalar(ref writer, (uint)nextScalar); - - // We consume two UTF-16 characters for a single supplementary character. - input += 2; - charsRemaining -= 2; - } - else - { - // Otherwise, this was a BMP character. - input++; - charsRemaining--; - char c = (char)nextScalar; - if (IsCharacterAllowed(c)) - { - writer.Write(c); - } - else - { - WriteEncodedScalar(ref writer, (uint)nextScalar); - } - } - } - } - - private int GetIndexOfFirstCharWhichRequiresEncoding(char* input, int inputLength) - { - for (int i = 0; i < inputLength; i++) - { - if (!IsCharacterAllowed(input[i])) - { - return i; - } - } - return -1; // no characters require encoding - } - - // Determines whether the given character can be returned unencoded. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool IsCharacterAllowed(char c) - { - return _allowedCharacters.IsCharacterAllowed(c); - } - - private static void ValidateInputs(int startIndex, int characterCount, int actualInputLength) - { - if (startIndex < 0 || startIndex > actualInputLength) - { - throw new ArgumentOutOfRangeException(nameof(startIndex)); - } - if (characterCount < 0 || characterCount > (actualInputLength - startIndex)) - { - throw new ArgumentOutOfRangeException(nameof(characterCount)); - } - } - - protected abstract void WriteEncodedScalar(ref Writer writer, uint value); - - /// - /// Provides an abstraction over both StringBuilder and TextWriter. - /// Declared as a struct so we can allocate on the stack and pass by - /// reference. Eliminates chatty virtual dispatches on hot paths. - /// - protected struct Writer - { - private readonly StringBuilder _innerBuilder; - private readonly TextWriter _innerWriter; - - public Writer(StringBuilder innerBuilder) - { - _innerBuilder = innerBuilder; - _innerWriter = null; - } - - public Writer(TextWriter innerWriter) - { - _innerBuilder = null; - _innerWriter = innerWriter; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(char value) - { - if (_innerBuilder != null) - { - _innerBuilder.Append(value); - } - else - { - _innerWriter.Write(value); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Write(string value) - { - if (_innerBuilder != null) - { - _innerBuilder.Append(value); - } - else - { - _innerWriter.Write(value); - } - } - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/UnicodeEncoderBaseTests.cs b/src/libraries/System.Text.Encodings.Web/tests/UnicodeEncoderBaseTests.cs deleted file mode 100644 index c72cc67b668ba..0000000000000 --- a/src/libraries/System.Text.Encodings.Web/tests/UnicodeEncoderBaseTests.cs +++ /dev/null @@ -1,408 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text.Unicode; -using Xunit; - -namespace System.Text.Encodings.Web.Tests -{ - public class UnicodeEncoderBaseTests - { - [Fact] - public void Ctor_WithCustomFilters() - { - // Arrange - var filter = new TextEncoderSettings(); - filter.AllowCharacters('a', 'b'); - filter.AllowCharacters('\0', '&', '\uFFFF', 'd'); - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(filter); - - // Act & assert - Assert.Equal("a", encoder.Encode("a")); - Assert.Equal("b", encoder.Encode("b")); - Assert.Equal("[U+0063]", encoder.Encode("c")); - Assert.Equal("d", encoder.Encode("d")); - Assert.Equal("[U+0000]", encoder.Encode("\0")); // we still always encode control chars - Assert.Equal("[U+0026]", encoder.Encode("&")); // we still always encode HTML-special chars - Assert.Equal("[U+FFFF]", encoder.Encode("\uFFFF")); // we still always encode non-chars and other forbidden chars - } - - [Fact] - public void Ctor_WithUnicodeRanges() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(new TextEncoderSettings(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols)); - - // Act & assert - Assert.Equal("[U+0061]", encoder.Encode("a")); - Assert.Equal("\u00E9", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal("\u2601", encoder.Encode("\u2601" /* CLOUD */)); - } - - [Fact] - public void Encode_AllRangesAllowed_StillEncodesForbiddenChars_Simple() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - const string input = "Hello <>&\'\"+ there!"; - const string expected = "Hello [U+003C][U+003E][U+0026][U+0027][U+0022][U+002B] there!"; - - // Act & assert - Assert.Equal(expected, encoder.Encode(input)); - } - - [Fact] - public void Encode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - - // Act & assert - BMP chars - for (int i = 0; i <= 0xFFFF; i++) - { - string input = new string((char)i, 1); - string expected; - if (IsSurrogateCodePoint(i)) - { - expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char - } - else - { - bool mustEncode = false; - switch (i) - { - case '<': - case '>': - case '&': - case '\"': - case '\'': - case '+': - mustEncode = true; - break; - } - - if (i <= 0x001F || (0x007F <= i && i <= 0x9F)) - { - mustEncode = true; // control char - } - else if (!UnicodeHelpers.IsCharacterDefined((char)i)) - { - mustEncode = true; // undefined (or otherwise disallowed) char - } - - if (mustEncode) - { - expected = string.Format(CultureInfo.InvariantCulture, "[U+{0:X4}]", i); - } - else - { - expected = input; // no encoding - } - } - - string retVal = encoder.Encode(input); - Assert.Equal(expected, retVal); - } - - // Act & assert - astral chars - for (int i = 0x10000; i <= 0x10FFFF; i++) - { - string input = char.ConvertFromUtf32(i); - string expected = string.Format(CultureInfo.InvariantCulture, "[U+{0:X}]", i); - string retVal = encoder.Encode(input); - Assert.Equal(expected, retVal); - } - } - - [Fact] - public void Encode_BadSurrogates_ReturnsUnicodeReplacementChar() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); // allow all codepoints - - // "abcde" - const string input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; - const string expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD[U+103FF]e\uFFFD"; - - // Act - string retVal = encoder.Encode(input); - - // Assert - Assert.Equal(expected, retVal); - } - - [Fact] - public void Encode_EmptyStringInput_ReturnsEmptyString() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - - // Act & assert - Assert.Equal("", encoder.Encode("")); - } - - [Fact] - public void Encode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - string input = "Hello, there!"; - - // Act & assert - Assert.Same(input, encoder.Encode(input)); - } - - [Fact] - public void Encode_NullInput_ReturnsNull() - { - // Arrange - UnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - - // Act & assert - Assert.Null(encoder.Encode(null)); - } - - [Fact] - public void Encode_WithCharsRequiringEncodingAtBeginning() - { - Assert.Equal("[U+0026]Hello, there!", new CustomUnicodeEncoderBase(UnicodeRanges.All).Encode("&Hello, there!")); - } - - [Fact] - public void Encode_WithCharsRequiringEncodingAtEnd() - { - Assert.Equal("Hello, there![U+0026]", new CustomUnicodeEncoderBase(UnicodeRanges.All).Encode("Hello, there!&")); - } - - [Fact] - public void Encode_WithCharsRequiringEncodingInMiddle() - { - Assert.Equal("Hello, [U+0026]there!", new CustomUnicodeEncoderBase(UnicodeRanges.All).Encode("Hello, &there!")); - } - - [Fact] - public void Encode_WithCharsRequiringEncodingInterspersed() - { - Assert.Equal("Hello, [U+003C]there[U+003E]!", new CustomUnicodeEncoderBase(UnicodeRanges.All).Encode("Hello, !")); - } - - [Fact] - public void Encode_CharArray_ParameterChecking_NegativeTestCases() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(); - - // Act & assert - Assert.Throws(() => encoder.Encode((char[])null, 0, 0, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc".ToCharArray(), 0, 3, null)); - Assert.Throws(() => encoder.Encode("abc".ToCharArray(), -1, 2, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc".ToCharArray(), 2, 2, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc".ToCharArray(), 4, 0, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc".ToCharArray(), 2, -1, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc".ToCharArray(), 1, 3, new StringWriter())); - } - - //[Fact] - //public void Encode_CharArray_ZeroCount_DoesNotCallIntoTextWriter() - //{ - // // Arrange - // CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(); - // TextWriter output = new Mock(MockBehavior.Strict).Object; - - // // Act - // encoder.Encode("abc".ToCharArray(), 2, 0, output); - - // // Assert - // // If we got this far (without TextWriter throwing), success! - //} - - [Fact] - public void Encode_CharArray_AllCharsValid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - StringWriter output = new StringWriter(); - - // Act - encoder.Encode("abc&xyz".ToCharArray(), 4, 2, output); - - // Assert - Assert.Equal("xy", output.ToString()); - } - - [Fact] - public void Encode_CharArray_AllCharsInvalid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(); - StringWriter output = new StringWriter(); - - // Act - encoder.Encode("abc&xyz".ToCharArray(), 4, 2, output); - - // Assert - Assert.Equal("[U+0078][U+0079]", output.ToString()); - } - - [Fact] - public void Encode_CharArray_SomeCharsValid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - StringWriter output = new StringWriter(); - - // Act - encoder.Encode("abc&xyz".ToCharArray(), 2, 3, output); - - // Assert - Assert.Equal("c[U+0026]x", output.ToString()); - } - - [Fact] - public void Encode_StringSubstring_ParameterChecking_NegativeTestCases() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(); - - // Act & assert - Assert.Throws(() => encoder.Encode((string)null, 0, 0, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc", 0, 3, null)); - Assert.Throws(() => encoder.Encode("abc", -1, 2, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc", 2, 2, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc", 4, 0, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc", 2, -1, new StringWriter())); - Assert.Throws(() => encoder.Encode("abc", 1, 3, new StringWriter())); - } - - //[Fact] - //public void Encode_StringSubstring_ZeroCount_DoesNotCallIntoTextWriter() - //{ - // // Arrange - // CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(); - // TextWriter output = new Mock(MockBehavior.Strict).Object; - - // // Act - // encoder.Encode("abc", 2, 0, output); - - // // Assert - // // If we got this far (without TextWriter throwing), success! - //} - - [Fact] - public void Encode_StringSubstring_AllCharsValid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - StringWriter output = new StringWriter(); - - // Act - encoder.Encode("abc&xyz", 4, 2, output); - - // Assert - Assert.Equal("xy", output.ToString()); - } - - //[Fact] - //public void Encode_StringSubstring_EntireString_AllCharsValid_ForwardDirectlyToOutput() - //{ - // // Arrange - // CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - // var mockWriter = new Mock(MockBehavior.Strict); - // mockWriter.Setup(o => o.Write("abc")).Verifiable(); - - // // Act - // encoder.Encode("abc", 0, 3, mockWriter.Object); - - // // Assert - // mockWriter.Verify(); - //} - - [Fact] - public void Encode_StringSubstring_AllCharsInvalid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(); - StringWriter output = new StringWriter(); - - // Act - encoder.Encode("abc&xyz", 4, 2, output); - - // Assert - Assert.Equal("[U+0078][U+0079]", output.ToString()); - } - - [Fact] - public void Encode_StringSubstring_SomeCharsValid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - StringWriter output = new StringWriter(); - - // Act - encoder.Encode("abc&xyz", 2, 3, output); - - // Assert - Assert.Equal("c[U+0026]x", output.ToString()); - } - - [Fact] - public void Encode_StringSubstring_EntireString_SomeCharsValid() - { - // Arrange - CustomUnicodeEncoderBase encoder = new CustomUnicodeEncoderBase(UnicodeRanges.All); - StringWriter output = new StringWriter(); - - // Act - const string input = "abc&xyz"; - encoder.Encode(input, 0, input.Length, output); - - // Assert - Assert.Equal("abc[U+0026]xyz", output.ToString()); - } - - private static bool IsSurrogateCodePoint(int codePoint) - { - return (0xD800 <= codePoint && codePoint <= 0xDFFF); - } - - private sealed class CustomTextEncoderSettings : TextEncoderSettings - { - private readonly int[] _allowedCodePoints; - - public CustomTextEncoderSettings(params int[] allowedCodePoints) - { - _allowedCodePoints = allowedCodePoints; - } - - public override IEnumerable GetAllowedCodePoints() - { - return _allowedCodePoints; - } - } - - private sealed class CustomUnicodeEncoderBase : UnicodeEncoderBase - { - // We pass a (known bad) value of 1 for 'max output chars per input char', - // which also tests that the code behaves properly even if the original - // estimate is incorrect. - public CustomUnicodeEncoderBase(TextEncoderSettings filter) - : base(filter, maxOutputCharsPerInputChar: 1) - { - } - - public CustomUnicodeEncoderBase(params UnicodeRange[] allowedRanges) - : this(new TextEncoderSettings(allowedRanges)) - { - } - - protected override void WriteEncodedScalar(ref Writer writer, uint value) - { - writer.Write(string.Format(CultureInfo.InvariantCulture, "[U+{0:X4}]", value)); - } - } - } -} diff --git a/src/libraries/System.Text.Encodings.Web/tests/UnicodeHelpersTests.cs b/src/libraries/System.Text.Encodings.Web/tests/UnicodeHelpersTests.cs index 7b7f115640174..6be6ac9c8233f 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/UnicodeHelpersTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/UnicodeHelpersTests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Text; using System.Text.Unicode; using Xunit; @@ -20,68 +19,6 @@ public unsafe class UnicodeHelpersTests private static readonly UTF8Encoding _utf8EncodingThrowOnInvalidBytes = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - // To future refactorers: - // The following GetScalarValueFromUtf16_* tests must not be done as a [Theory]. If done via [InlineData], the invalid - // code points will get sanitized with replacement characters before they even reach the test, as the strings are parsed - // from the attributes in reflection. And if done via [MemberData], the XmlWriter used by xunit will throw exceptions - // when it attempts to write out the test arguments, due to the invalid text. - - [Fact] - public void GetScalarValueFromUtf16_NormalBMPChar_EndOfString() - { - GetScalarValueFromUtf16("a", 'a'); - } - - [Fact] - public void GetScalarValueFromUtf16_NormalBMPChar_NotEndOfString() - { - GetScalarValueFromUtf16("ab", 'a'); - } - - [Fact] - public void GetScalarValueFromUtf16_TrailingSurrogate_EndOfString() - { - GetScalarValueFromUtf16("\uDFFF", UnicodeReplacementChar); - } - - [Fact] - public void GetScalarValueFromUtf16_TrailingSurrogate_NotEndOfString() - { - GetScalarValueFromUtf16("\uDFFFx", UnicodeReplacementChar); - } - - [Fact] - public void GetScalarValueFromUtf16_LeadingSurrogate_EndOfString() - { - GetScalarValueFromUtf16("\uD800", UnicodeReplacementChar); - } - - [Fact] - public void GetScalarValueFromUtf16_LeadingSurrogate_NotEndOfString() - { - GetScalarValueFromUtf16("\uD800x", UnicodeReplacementChar); - } - - [Fact] - public void GetScalarValueFromUtf16_LeadingSurrogate_NotEndOfString_FollowedByLeadingSurrogate() - { - GetScalarValueFromUtf16("\uD800\uD800", UnicodeReplacementChar); - } - - [Fact] - public void GetScalarValueFromUtf16_LeadingSurrogate_NotEndOfString_FollowedByTrailingSurrogate() - { - GetScalarValueFromUtf16("\uD800\uDFFF", 0x103FF); - } - - private void GetScalarValueFromUtf16(string input, int expectedResult) - { - fixed (char* pInput = input) - { - Assert.Equal(expectedResult, UnicodeHelpers.GetScalarValueFromUtf16(pInput, endOfString: (input.Length == 1))); - } - } - [Fact] public void GetUtf8RepresentationForScalarValue() { @@ -111,7 +48,7 @@ public void GetUtf8RepresentationForScalarValue() [Fact] public void IsCharacterDefined() { - Assert.All(ReadListOfDefinedCharacters().Select((defined, idx) => new { defined, idx }), c => Assert.Equal(c.defined, UnicodeHelpers.IsCharacterDefined((char)c.idx))); + Assert.All(ReadListOfDefinedCharacters().Select((defined, idx) => new { defined, idx }), c => Assert.Equal(c.defined, UnicodeTestHelpers.IsCharacterDefined((char)c.idx))); } private static bool[] ReadListOfDefinedCharacters() diff --git a/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangeTests.cs b/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangeTests.cs index 51d1d9adc4b3f..c54b770e7df93 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangeTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangeTests.cs @@ -12,6 +12,7 @@ public class UnicodeRangeTests [Theory] [InlineData(-1, 16)] [InlineData(0x10000, 16)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void Ctor_FailureCase_FirstCodePoint(int firstCodePoint, int rangeSize) { AssertExtensions.Throws("firstCodePoint", () => new UnicodeRange(firstCodePoint, rangeSize)); @@ -20,6 +21,7 @@ public void Ctor_FailureCase_FirstCodePoint(int firstCodePoint, int rangeSize) [Theory] [InlineData(0x0100, -1)] [InlineData(0x0100, 0x10000)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public void Ctor_FailureCase_RangeSize(int firstCodePoint, int rangeSize) { AssertExtensions.Throws("length", () => new UnicodeRange(firstCodePoint, rangeSize)); diff --git a/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangesTests.cs b/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangesTests.cs index efa50bff2f8ee..4217d130ff5e2 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangesTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/UnicodeRangesTests.cs @@ -27,6 +27,7 @@ public static void Range_All() [Theory] [MemberData(nameof(UnicodeRanges_GeneratedData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/49568", typeof(PlatformDetection), nameof(PlatformDetection.IsMacOsAppleSilicon))] public static void Range_Unicode(ushort first, ushort last, string blockName) { Assert.Equal(0x0, first & 0xF); // first char in any block should be U+nnn0 diff --git a/src/libraries/System.Text.Encodings.Web/tests/UnicodeTestHelpers.cs b/src/libraries/System.Text.Encodings.Web/tests/UnicodeTestHelpers.cs new file mode 100644 index 0000000000000..bd8f3d4a71570 --- /dev/null +++ b/src/libraries/System.Text.Encodings.Web/tests/UnicodeTestHelpers.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.Encodings.Web; + +namespace System.Text.Unicode +{ + internal static class UnicodeTestHelpers + { + private static Lazy> _lazyBitmap = new Lazy>(InitializeLazyBitmap); + + /// + /// Returns a value stating whether a character is defined per the checked-in version + /// of the Unicode specification. Certain classes of characters (control chars, + /// private use, surrogates, some whitespace) are considered "undefined" for + /// our purposes. + /// + internal static bool IsCharacterDefined(char c) => _lazyBitmap.Value.Value.IsCharAllowed(c); + + private static unsafe StrongBox InitializeLazyBitmap() + { + // Initialize the bitmap to all-ones (everything allowed), then mask it with + // our carried list of defined chars. Everything that's disallowed will be set + // to zero. + + AllowedBmpCodePointsBitmap bitmap = default; + MemoryMarshal.AsBytes(new Span(&bitmap, 1)).Fill(0xFF); + bitmap.ForbidUndefinedCharacters(); + return new StrongBox(bitmap); + } + } +} diff --git a/src/libraries/System.Text.Encodings.Web/tests/UrlEncoderTests.cs b/src/libraries/System.Text.Encodings.Web/tests/UrlEncoderTests.cs index 46bb504eaeec6..dbbfc54a3d252 100644 --- a/src/libraries/System.Text.Encodings.Web/tests/UrlEncoderTests.cs +++ b/src/libraries/System.Text.Encodings.Web/tests/UrlEncoderTests.cs @@ -1,11 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Text.Unicode; using Xunit; @@ -33,47 +31,47 @@ public void Ctor_WithTextEncoderSettings() var filter = new TextEncoderSettings(); filter.AllowCharacters('a', 'b'); filter.AllowCharacters('\0', '&', '\uFFFF', 'd'); - UrlEncoder encoder = new UrlEncoder(filter); + UrlEncoder encoder = UrlEncoder.Create(filter); // Act & assert - Assert.Equal("a", encoder.UrlEncode("a")); - Assert.Equal("b", encoder.UrlEncode("b")); - Assert.Equal("%63", encoder.UrlEncode("c")); - Assert.Equal("d", encoder.UrlEncode("d")); - Assert.Equal("%00", encoder.UrlEncode("\0")); // we still always encode control chars - Assert.Equal("%26", encoder.UrlEncode("&")); // we still always encode HTML-special chars - Assert.Equal("%EF%BF%BF", encoder.UrlEncode("\uFFFF")); // we still always encode non-chars and other forbidden chars + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal("b", encoder.Encode("b")); + Assert.Equal("%63", encoder.Encode("c")); + Assert.Equal("d", encoder.Encode("d")); + Assert.Equal("%00", encoder.Encode("\0")); // we still always encode control chars + Assert.Equal("%26", encoder.Encode("&")); // we still always encode HTML-special chars + Assert.Equal("%EF%BF%BF", encoder.Encode("\uFFFF")); // we still always encode non-chars and other forbidden chars } [Fact] public void Ctor_WithUnicodeRanges() { // Arrange - UrlEncoder encoder = new UrlEncoder(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); + UrlEncoder encoder = UrlEncoder.Create(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); // Act & assert - Assert.Equal("%61", encoder.UrlEncode("a")); - Assert.Equal("\u00E9", encoder.UrlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal("\u2601", encoder.UrlEncode("\u2601" /* CLOUD */)); + Assert.Equal("%61", encoder.Encode("a")); + Assert.Equal("\u00E9", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); + Assert.Equal("\u2601", encoder.Encode("\u2601" /* CLOUD */)); } [Fact] - public void Ctor_WithNoParameters_DefaultsToBasicLatin() + public void DefaultFactory_IsBasicLatin() { // Arrange - UrlEncoder encoder = new UrlEncoder(); + UrlEncoder encoder = UrlEncoder.Default; // Act & assert - Assert.Equal("a", encoder.UrlEncode("a")); - Assert.Equal("%C3%A9", encoder.UrlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); - Assert.Equal("%E2%98%81", encoder.UrlEncode("\u2601" /* CLOUD */)); + Assert.Equal("a", encoder.Encode("a")); + Assert.Equal("%C3%A9", encoder.Encode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); + Assert.Equal("%E2%98%81", encoder.Encode("\u2601" /* CLOUD */)); } [Fact] public void Default_EquivalentToBasicLatin() { // Arrange - UrlEncoder controlEncoder = new UrlEncoder(UnicodeRanges.BasicLatin); + UrlEncoder controlEncoder = UrlEncoder.Create(UnicodeRanges.BasicLatin); UrlEncoder testEncoder = UrlEncoder.Default; // Act & assert @@ -82,7 +80,7 @@ public void Default_EquivalentToBasicLatin() if (!IsSurrogateCodePoint(i)) { string input = new string((char)i, 1); - Assert.Equal(controlEncoder.UrlEncode(input), testEncoder.UrlEncode(input)); + Assert.Equal(controlEncoder.Encode(input), testEncoder.Encode(input)); } } } @@ -91,7 +89,7 @@ public void Default_EquivalentToBasicLatin() public void UrlEncode_AllRangesAllowed_StillEncodesForbiddenChars() { // Arrange - UrlEncoder encoder = new UrlEncoder(UnicodeRanges.All); + UrlEncoder encoder = UrlEncoder.Create(UnicodeRanges.All); // Act & assert - BMP chars for (int i = 0; i <= 0xFFFF; i++) @@ -114,7 +112,7 @@ public void UrlEncode_AllRangesAllowed_StillEncodesForbiddenChars() } else if ((0x00A0 <= i && i <= 0xD7FF) | (0xF900 <= i && i <= 0xFDCF) | (0xFDF0 <= i && i <= 0xFFEF)) { - mustEncode = !UnicodeHelpers.IsCharacterDefined((char)i); // 'ucschar' + mustEncode = !UnicodeTestHelpers.IsCharacterDefined((char)i); // 'ucschar' } else { @@ -152,7 +150,7 @@ public void UrlEncode_AllRangesAllowed_StillEncodesForbiddenChars() } } - string retVal = encoder.UrlEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } @@ -161,7 +159,7 @@ public void UrlEncode_AllRangesAllowed_StillEncodesForbiddenChars() { string input = char.ConvertFromUtf32(i); string expected = GetKnownGoodPercentEncodedValue(i); - string retVal = encoder.UrlEncode(input); + string retVal = encoder.Encode(input); Assert.Equal(expected, retVal); } } @@ -170,14 +168,14 @@ public void UrlEncode_AllRangesAllowed_StillEncodesForbiddenChars() public void UrlEncode_BadSurrogates_ReturnsUnicodeReplacementChar() { // Arrange - UrlEncoder encoder = new UrlEncoder(UnicodeRanges.All); // allow all codepoints + UrlEncoder encoder = UrlEncoder.Create(UnicodeRanges.All); // allow all codepoints // "abcde" const string input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; const string expected = "a%EF%BF%BDb%EF%BF%BDc%EF%BF%BD%EF%BF%BDd%EF%BF%BD%F0%90%8F%BFe%EF%BF%BD"; // 'D800' 'DFFF' was preserved since it's valid // Act - string retVal = encoder.UrlEncode(input); + string retVal = encoder.Encode(input); // Assert Assert.Equal(expected, retVal); @@ -187,65 +185,65 @@ public void UrlEncode_BadSurrogates_ReturnsUnicodeReplacementChar() public void UrlEncode_EmptyStringInput_ReturnsEmptyString() { // Arrange - UrlEncoder encoder = new UrlEncoder(); + UrlEncoder encoder = UrlEncoder.Default; // Act & assert - Assert.Equal("", encoder.UrlEncode("")); + Assert.Equal("", encoder.Encode("")); } [Fact] public void UrlEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance() { // Arrange - UrlEncoder encoder = new UrlEncoder(); + UrlEncoder encoder = UrlEncoder.Default; string input = "Hello,there!"; // Act & assert - Assert.Same(input, encoder.UrlEncode(input)); + Assert.Same(input, encoder.Encode(input)); } [Fact] public void UrlEncode_NullInput_ReturnsNull() { // Arrange - UrlEncoder encoder = new UrlEncoder(); + UrlEncoder encoder = UrlEncoder.Default; - Assert.Throws(() => { encoder.UrlEncode(null); }); + Assert.Throws(() => { encoder.Encode(null); }); } [Fact] public void UrlEncode_WithCharsRequiringEncodingAtBeginning() { - Assert.Equal(@"%26Hello,there!", new UrlEncoder().UrlEncode("&Hello,there!")); + Assert.Equal(@"%26Hello,there!", UrlEncoder.Default.Encode("&Hello,there!")); } [Fact] public void UrlEncode_WithCharsRequiringEncodingAtEnd() { - Assert.Equal(@"Hello,there!%26", new UrlEncoder().UrlEncode("Hello,there!&")); + Assert.Equal(@"Hello,there!%26", UrlEncoder.Default.Encode("Hello,there!&")); } [Fact] public void UrlEncode_WithCharsRequiringEncodingInMiddle() { - Assert.Equal(@"Hello,%20%26there!", new UrlEncoder().UrlEncode("Hello, &there!")); + Assert.Equal(@"Hello,%20%26there!", UrlEncoder.Default.Encode("Hello, &there!")); } [Fact] public void UrlEncode_WithCharsRequiringEncodingInterspersed() { - Assert.Equal(@"Hello,%20%3Cthere%3E!", new UrlEncoder().UrlEncode("Hello, !")); + Assert.Equal(@"Hello,%20%3Cthere%3E!", UrlEncoder.Default.Encode("Hello, !")); } [Fact] public void UrlEncode_CharArray() { // Arrange - UrlEncoder encoder = new UrlEncoder(); + UrlEncoder encoder = UrlEncoder.Default; var output = new StringWriter(); // Act - encoder.UrlEncode("Hello+world!".ToCharArray(), 3, 5, output); + encoder.Encode(output, "Hello+world!".ToCharArray(), 3, 5); // Assert Assert.Equal("lo%2Bwo", output.ToString()); @@ -255,11 +253,11 @@ public void UrlEncode_CharArray() public void UrlEncode_StringSubstring() { // Arrange - UrlEncoder encoder = new UrlEncoder(); + UrlEncoder encoder = UrlEncoder.Default; var output = new StringWriter(); // Act - encoder.UrlEncode("Hello+world!", 3, 5, output); + encoder.Encode(output, "Hello+world!", 3, 5); // Assert Assert.Equal("lo%2Bwo", output.ToString()); @@ -272,8 +270,8 @@ public void UrlEncode_DoesNotOutputHtmlSensitiveCharacters() // by never emitting HTML-sensitive characters unescaped. // Arrange - UrlEncoder urlEncoder = new UrlEncoder(UnicodeRanges.All); - HtmlEncoder htmlEncoder = new HtmlEncoder(UnicodeRanges.All); + UrlEncoder urlEncoder = UrlEncoder.Create(UnicodeRanges.All); + HtmlEncoder htmlEncoder = HtmlEncoder.Create(UnicodeRanges.All); // Act & assert for (int i = 0; i <= 0x10FFFF; i++) @@ -283,8 +281,8 @@ public void UrlEncode_DoesNotOutputHtmlSensitiveCharacters() continue; // surrogates don't matter here } - string urlEncoded = urlEncoder.UrlEncode(char.ConvertFromUtf32(i)); - string thenHtmlEncoded = htmlEncoder.HtmlEncode(urlEncoded); + string urlEncoded = urlEncoder.Encode(char.ConvertFromUtf32(i)); + string thenHtmlEncoded = htmlEncoder.Encode(urlEncoded); Assert.Equal(urlEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters } } diff --git a/src/libraries/System.Text.Encodings.Web/tools/GenDefinedCharList/Program.cs b/src/libraries/System.Text.Encodings.Web/tools/GenDefinedCharList/Program.cs index 6b81dfc9b3387..a8414d1b3bb66 100644 --- a/src/libraries/System.Text.Encodings.Web/tools/GenDefinedCharList/Program.cs +++ b/src/libraries/System.Text.Encodings.Web/tools/GenDefinedCharList/Program.cs @@ -227,7 +227,7 @@ private static bool IsRangeDefinition(string rawName, out string rangeName, out // Represents a range of Unicode code points which are all members of a single category. // More info: https://www.unicode.org/faq/blocks_ranges.html - private class UnicodeRange + private sealed class UnicodeRange { public uint FirstCodePoint; public uint LastCodePoint; diff --git a/src/libraries/System.Text.Json/docs/ThreatModel.md b/src/libraries/System.Text.Json/docs/ThreatModel.md new file mode 100644 index 0000000000000..f0031f8340c07 --- /dev/null +++ b/src/libraries/System.Text.Json/docs/ThreatModel.md @@ -0,0 +1,210 @@ +# `System.Text.Json` Threat Model + +## Summary + +`System.Text.Json` is a .NET library providing a serializer (`JsonSerializer`), reader (`Utf8JsonReader`), and writer (`Utf8JsonWriter`) implementation for the JavaScript Object Notation (JSON) Data Interchange Format, as specified in [RFC 8259](https://tools.ietf.org/html/rfc8259). The `JsonDocument` and `JsonElement` types are provided as immutable JSON document representations, and `JsonElement` instances are deserialized when JSON payloads are intended to map with `typeof(object)` properties or collection elements, for example with non-generic collections. + +The emphasis of this document is to describe security threats that were considered when designing `JsonSerializer`, and how they can be mitigated. + +## Serialization + +The notes in this section apply to the following serialization methods: + +- [`JsonSerializer.Serialize`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.serialize?view=net-5.0) +- [`JsonSerializer.SerializeAsync`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.serializeasync?view=net-5.0) +- [`JsonSerializer.SerializeToUtf8Bytes`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.serializetoutf8bytes?view=net-5.0) + +### Threat: Stack overflow due to circular references + +.NET object graphs existing at runtime are generally trusted as inputs to `Utf8JsonWriter` and `JsonSerializer.Serialize` when writing. However, circular references in the instantiated input object graphs may cause a [`StackOverflowException`](https://docs.microsoft.com/dotnet/api/system.stackoverflowexception?view=net-5.0) if not guarded against, and possibly cause the application process to crash. + +#### Mitigation + +Circular references are detected when writing due to a maximum depth setting (64 by default), and a clear [`JsonException`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonexception?view=net-5.0) is thrown when this depth is exceeded. Note that the reference handling feature implemented by the serializer provides a way to preserve object references on serialization and deserialization. + +### Threat: Unintentional information disclosure + +`JsonSerializer` provides an opt-in only feature to allow the serialization of non-public properties and fields. If utilized, this may allow the exposure of private and internal data outside of the type or assembly during serialization. Additionally, the shape of a type can be exposed during serialization. + +#### Mitigation + +- By default, `JsonSerializer` does not allow the serialization of non-public members of input types and will not call any non-public surface area. An explicit opt-in is required to include these members. If this feature is utilized, be sure to understand your type's serialization projection and what data may be exposed. + +- Be aware that the same type may have multiple serialization projections, depending on the serializer in use. The same type may expose one set of data when used with `System.Text.Json.JsonSerializer` and another set of data when used with `Newtonsoft.Json.JsonConvert`. Accidentally using the wrong serializer may lead to information disclosure. For example, a property that is annotated with `Newtonsoft.Json.JsonIgnoreAttribute` will be skipped when serializing and deserializing with `JsonConvert`, but will potentially be included when processed with `JsonSerializer`, as `System.Text.Json`'s serializer does not honor `Newtonsoft.Json` attributes. + +## Deserialization + +The notes in this section apply to the following deserialization methods: + +- [`JsonSerializer.Deserialize`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.deserialize?view=net-5.0) +- [`JsonSerializer.DeserializeAsync`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializer.deserializeasync?view=net-5.0) + +The `Utf8JsonReader` class, leveraged by `JsonSerializer` for deserialization, accepts arbitrary UTF-8 text or binary data as input, and is therefore expected to handle data from potentially untrusted sources. + +### Threat: Denial-of-Service (DoS) attacks due to deeply nested JSON + +Unreasonably deeply nested JSON may be constructed to exhaust stack space on the machine performing deserialization. If unguarded against, this may cause the application process to crash, possibly resulting in a denial of service. + +#### Mitigation + +The reader has a `MaxDepth` property with a default value of 64 to avoid recursing too deep in JSON. The deserializer also has a `JsonSerializerOptions.MaxDepth` property (same default of 64) that is passed to the reader to configure the reader’s `MaxDepth`. The serializer assumes the reader will throw and thus does not check the value itself. The serializer uses the same `MaxDepth` for both serialization and deserialization. During serialization, the serializer does need to check against the `MaxDepth` since the writer does not have a configurable max. However, the writer does have a fail-safe limit of 1,000 depth which means a `MaxDepth` setting of > 1000 is not honored during serialization but is honored during deserialization. + +### Threat: Algorithmic complexity attacks + +Algorithmic complexity attacks have been considered as part of the design of `JsonSerializer`. + +#### Mitigations + +There is no known way to craft JSON that can cause the deserializer to do more work than the adversary had to do. That is, given a payload of length _n_ bytes, the total amount of work performed and memory allocated by `Utf8JsonReader` and `JsonSerializer.Deserialize` will be bounded by `O(n)`. There are no unmitigated `O(n^2)` or higher complexity algorithms in the deserialization routines. + +### Threat: Loading `System.Type`s based on untrusted user data + +Loading unintended types may have significant consequences, whether the type is malicious or just has security-sensitive side effects. A type may contain an exploitable security vulnerability, perform security-sensitive actions in its instance or `static` constructor, have a large memory footprint that facilitates DoS attacks, or may throw non-recoverable exceptions. Types may have `static` constructors that run as soon as the type is loaded and before any instances are created. For these reasons, it is important to control the set of types that the deserializer may load. + +From the perspective of the serializer, this means that deserializing `System.Type` instances from JSON input is dangerous. Similarly, unrestricted polymorphic deserialization via type metadata in JSON, where a string representing the specific type to create is passed to an API such as `Type.GetType(string)`, is dangerous. + +#### Mitigation + +- Serialization and deserialization of `System.Type` instances is not supported by `JsonSerializer` by default. A custom converter can be implemented to handle `System.Type` instances, but care should be taken to avoid processing untrusted data. + +- If a polymorphic deserialization feature is implemented in the future, it will **not** utilize a mechanism where the type to create is specified via passing an arbitrary JSON string to `Type.GetType(string)` and instantiating the indicated instance. + +### Threat: Deserialized instances being in an unintended state + +An instance of a type or an instantiated object graph may have internal consistency constraints that must be enforced. Care must be taken to avoid breaking these constraints during deserialization. + +#### Mitigation + +This situation can be avoided by being aware of the following points: + +- Constructors run when `JsonSerializer.Deserialize` deserializes POCOs. Therefore, logic in constructors may participate in state management of the deserialized instance. + +- If necessitated by your deserialization scenario, use callbacks to ensure that the object is in a valid state. This is not yet a first-class feature in the serializer, but a workaround is described in the [How to migrate from Newtonsoft.Json to System.Text.Json document](https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#callbacks). Be aware that this workaround potentially degrades performance. + +- Where applicable, consider validating object graphs deserialized from untrusted data sources before processing them. Each individual object may be in a consistent state, but an object graph as a whole may not be. + +### Threat: Hash collision vulnerability with dictionaries whose keys are not strings + +A vulnerability exists when serializable types contain dictionaries whose keys are not strings; for example `Dictionary`, `Dictionary`, and `Dictionary`. The problem occurs if a large number of values are inserted into a hashtable where a large number of those values generate the same hash code. This can be used as a DoS attack. This applies broadly to collection types which bucket entries based on a hash code. For example, object graphs which contain properties typed as `HashSet` (where `T` != `string`) may be subject to the same attack. + +#### Mitigation + +- `JsonSerializer` does not instantiate and populate any dictionaries with non-string keys in an unbounded manner in its internal implementation. + +- When deserializing, dictionaries with non-string keys existing in the object graphs of input serializable types will be arbitrarily populated as dictated by the input payloads. Be sure to understand the nature of possible payloads that may be deserialized to dictionary members or types and if they present any security vulnerabilities. + +## Serialization and deserialization + +These notes apply to both serialization and deserialization. + +### Threat: User-provided code execution with adverse effects + +A number of routines in the `JsonSerializer` implementation run code that is provided by the user. For example, when deserializing, `JsonSerializer.Deserialize` may call user-provided constructors and property/field set accessors. Similarly when serializing, `JsonSerializer.Serialize` and property/field get accessors may be called. + +The `JsonConverter` type provides a way for users to author logic that handles the serialization of a type, property, or field. This presents an extensibility point which will be invoked by the serializer. + +These surface areas present security vulnerabilities in the event that they contain code that is malicious or just has security-sensitive side effects. + +#### Mitigation + +It is the responsibility of the code author to ensure that no security vulnerabilities exist. For example, if you create a serializable type with a property of type integer, and in the set accessor implementation allocate an array based on the property value, you expose the possibility of a DoS attack if a malicious message contains an extremely large value for this data member. In general, avoid any allocations based on incoming data or long-running processing in user-provided code (especially if long-running processing can be caused by a small amount of incoming data, i.e. not bounded linearly). + +Also, you should ensure that there is no malicious code in any members within an input object graph, or custom converters which could be called by the serializer, including for types you do not own. + +### Threat: Information disclosure due to exception messages + +The message for an exception thrown by the serializer may contain the value or partial value of the current JSON property (on deserialization) or CLR property (on serialization) being processed. This is direct “user data” exposed; without this information it would be more difficult to determine what the errors are. It is expected that any application that displays or saves this information treats it as raw text. + +#### Mitigation + +- When using the serializer, consider what information might be included if an exception is thrown when handling your input data. + +- Be cautious about how the raw data from the exception message is processed or forwarded by your application. + +### Threat: Errors due to badly constructed, malicious, or non-RFC-compliant JSON payloads + +Malicious payloads may contain badly formatted JSON in an attempt to cause unexpected exceptions, put the serializer or reader in an invalid state, or cause other security-sensitive problems. + +The `JsonWriterOptions.SkipValidation` property specifies whether to allow a caller to write invalid JSON using `Utf8JsonWriter`. For better performance when writing, `JsonSerializer` sets this property to `true`. This setting is used even when a user-provided converter is used to serialize a type. This means that the only safeguard the `JsonSerializer.Serialize` offers against writing invalid JSON is ensuring that the nesting level of the written JSON is the same when the converter is finished as when it started. This limited validation opens the possiblility of writing invalid JSON that may be accidently (or intentionally) misused and presents a threat when passed as an input to parsers such as `JsonSerializer.Deserialize`. + +By default, the `JsonSerializer` and `Utf8JsonReader` strictly honor the [RFC 8259 specification](https://tools.ietf.org/html/rfc8259). When enabling non-RFC-compliant behaviors (such as permitting single or multi-line comments when reading JSON), different deserializers may interpret incoming payloads differently. This could result in two different deserializers reading the exact same payload but populating an object's members with different values. This may have security implications for distributed systems. For example, if a validating frontend uses one deserializer but the backend system uses a different deserializer, this may present an opportunity for an adversary to craft a payload which appears safe when seen by the frontend but which is malicious when deserialized by the backend. + +#### Mitigation + +- The `Utf8JsonReader`, `Utf8JsonWriter`, and `JsonSerializer` types adhere to the [RFC 8259 specification](https://tools.ietf.org/html/rfc8259) by default, requiring the input and output payloads follow the standard formatting for strings, numbers, lists, and objects. Payloads that don't conform will cause a clear `JsonException` to be thrown when reading. + +- Understand how different JSON processing routines across a distributed system handle inputs for both serialization and deserialization, and how any differences may affect the security of the entire system. + +- Avoid writing serialization converters that produce non-standard output and thus require the receiver to opt in to potentially dangerous behaviors. + +- When advancing through JSON payloads, the `Utf8JsonReader` validates that the next token does not violate RFC 8529. However, a deserialization converter may decide to validate that the next token is compatible with the target type they wish to obtain. For instance, if you are expecting a `JsonTokenType.Number` the reader will not validate that the next token is not a `JsonTokenType.StartObject`. For such cases, the converter could check the payload and throw a `JsonException` if it is invalid, similar to what the serializer would do. Otherwise, the reader will throw an `InvalidOperationException`. + + If a `JsonException` or `NotSupportedException` is thrown from a custom converter without a message, the serializer will append a message that includes the path to the part of the JSON that caused the error. For more information on error handling in converters, see [How to write custom converters for JSON serialization (marshalling) in .NET](https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#error-handling). + +## `JsonSerializerOptions` + +The notes in this section apply to usages of the [`JsonSerializerOptions`](https://docs.microsoft.com/dotnet/api/system.text.json.jsonserializeroptions?view=net-5.0) type. + +### Threat: Poor CPU throughput due to not caching custom `JsonSerializerOptions` instances + +`JsonSerializerOptions` can be used to specify custom options when using the serializer. When a new options instance is passed to the serializer, it undergoes a warm-up phase during the first serialization of every type (for which data is present) in the object graph of the input type. This warm-up includes creating a cache of metadata it needs to perform serialization: funcs to property getters, setters, ctor arguments, specified attributes etc. This metadata cache is stored in the options instance. This process is not cheap, and can lead to very bad CPU time performance if done repeatedly. + +#### Mitigation + +It is recommended to cache options instances for reuse on subsequent calls to the serializer to avoid unnecessarily undergoing the warm-up repeatedly. Not caching the options instance can lead to performance issues. + +If an options instance is not passed to the serializer, a default options instance is internally created and cached as a static variable. This means the warm-up phase is only performed once for every type when using the default options instance. + +### Threat: Memory exhaustion due to (de)serializing numerous types + +`JsonSerializer` creates and caches metadata for each unique type that is passed to it as an input (along with serializable types in their object graphs). This metadata is cached on the default `JsonSerializerOptions` instance, or in custom instances that are passed to the serializer. Serializing and deserializing numerous types, for example, multiple anonymous types or types created with reflection, can cause memory to be exhausted due to all the metadata being cached. + +#### Mitigation + +Be sure to maintain an upper bound to the number of types being passed to `JsonSerializer`, for both serialization and deserialization. For example, if implementing an API backend, do not accidentally create throwaway (dynamically generated) types per request to represent response data to be serialized and returned to the client. + +### Threat: Elevation of privilege + +`JsonSerializer` makes no trust decisions, so there's no potential risk due to accidental elevation of privilege. Systems built on top of JSON serialization such as ASP.NET Core's type serialization and custom user libraries need to determine security considerations for input and output data processed by the `JsonSerializer`. + +## Implementation considerations + +- When deserializing, `JsonSerializer.Deserialize` utilizes `Utf8JsonReader`'s `Read()` method to parse, validate, and advance through JSON payloads. The reader also provides methods for data type conversions, for example parsing `DateTime` instances from JSON strings. This functionality is utilized by the serializer when mapping JSON to types and properties. This process is typically a forward-only operation. + + However, the deserializer will "rewind" if all of the following conditions are met: + + - The `DeserializeAsync` method is used. + - A custom converter is used for the given property type. + - The JSON value starts with a StartArray or StartObject. If the JSON value is a primitive (e.g. string, number, bool, null) then the logic assumes no "read-ahead" is necessary for the converter. + + When these conditions are met, the deserializer will "read-ahead" or "skip" to the end of the current JSON scope (not all of the JSON) so that the converter won't encounter a situation where `reader.Read()` returns `false` due to lack of data in the current buffer held by the reader. Then the deserializer "rewinds" the offset back to the beginning so the custom converter sees the first token (which will be a `StartArray` or `StartObject`). + + This special "rewind" mode is **not** `O(n^2)`; instead it is a two-pass algorithm (so `O(2n)`). + + Another scenario where the serializer will go through the input a second time is during synchronous deserialization of objects with parameterized constructors (added in 5.0). The algorithm for this scenario consists of an optimized two-pass read of the JSON payload. The first pass parses the payload for constructor arguments needed to construct the object, while keeping track of the positions and metadata for JSON properties that map to regular CLR object properties. This first read is through the entire subset of the payload that corresponds to the particular object being deserialized to ensure “last one wins” semantics. On the second pass, only JSON mapping directly to properties on POCOs (and not via constructor) is processed. + +- All exceptions that can be thrown directly from the `System.Text.Json` APIs have been documented. Exceptions thrown by lower level APIs which are not part of the normal execution flow of the various types are not documented. An example is `OutOfMemoryException` which can be thrown by `ArrayBufferWriter` when used by `Utf8JsonWriter` and there is no more available memory to write data to. + + Exceptions thrown by the `System.Text.Json` APIs may contain the following: + - Size of the buffer + - Length of committed and uncommitted bytes + - Current token type + - The value or partial value of the current JSON property (on deserialization) or CLR property (on serialization) being processed. + +- The serializer, reader, and writer strictly honor the [RFC 8259 specification](https://tools.ietf.org/html/rfc8259) by default, but more permissive settings such as allowing trailing commas, allowing quoted numbers, and allowing, skipping, or disallowing both single-line and multi-line comments can be opted-into with `JsonSerializerOptions` and `JsonReaderOptions`. + +- The serializer has a built-in converter for `DateTime` and `DateTimeOffset` types which parses and formats according to the ISO 8601:1-2019 extended profile. The specification and information about workarounds using custom converters is documented in the docs website: https://docs.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support. + +- The reference handling feature utilizes a non-standard JSON schema adopted from `Newtonsoft.Json` in which "$id", "$ref", and "$values" metadata properties are interspersed in JSON to allow keeping track of object references. When deserializing, this allows objects to point to each other in the resulting instance. When serializing, the resulting JSON is formatted using the previously mentioned representation. This feature is opt-in only (not enabled by default). When the feature is enabled, any deviation from the expected metadata in the input payloads when deserializing will cause a `JsonException` to be thrown. + +- There are no unmitigated `O(n^2)` or higher complexity algorithms in the `System.Text.Json` APIs. + +- Various UTF-8 and UTF-16 encode and decode APIs in `System.Text.Encoding`, as well as APIs in `System.Text.Web.Encodings` are used extensively to handle transcoding and JSON escaping logic. These APIs are safe for untrusted input. No JSON values are passed to other APIs as input (for example obtaining a `System.Type` instance from the string). No other transformations are applied on the input or output payloads of the serializer. + +- When deserializing, `JsonSerializer` creates a `JsonElement` for JSON that is applied to a member type of `System.Object`. This has potential usability and performance issues in cases such as when a non-generic `IList` property will create a `JsonElement` for each member even if the JSON array only contains primitive values such a `string`s or `bool`s. This is the default behavior since there is no universal, consistent algorithm to map arbitrary JSON data to primitives like `string`, `double` etc. Treating the data as a property bag with `JsonElement` works for all cases and is consistent since it is based upon the CLR type and not the JSON contents. An alternative implementation which creates boxed primitives rather than `JsonElement`s is ambiguous, such as sometimes treating a JSON string as a `DateTime` (detected by parsing), or by creating a `double` for a JSON number if a decimal point is present (which can cause accuracy issues if `decimal` is expected) or creating a `long` for a JSON number if there is no decimal point (which can cause range issues if a `ulong` or `BigInteger` is expected). Thus the default is consistent and safe instead of convenient. It is possible, however, to change the `System.Object` behavior by using a custom converter. + + - When writing, `JsonSerializer` and `Utf8JsonWriter` escape all non-ASCII characters by default to provide defense-in-depth protections against cross-site scripting (XSS) or information-disclosure attacks. HTML-sensitive characters, such as `<`, `>`, `&`, and `'` are escaped as well. We provide an option to override the default behavior by specifying a custom `System.Text.Encodings.Web.JavaScriptEncoder`. + + `JavaScriptEncoder.UnsafeRelaxedJsonEscaping`, a built-in, more permissive encoder is provided. It doesn't escape HTML-sensitive characters or offer additional defense-in-depth protections against XSS or information-disclosure attacks. It should only be used when it is known that the client will be interpreting the resulting payload as UTF-8 encoded JSON. Never allow the output of `UnsafeRelaxedJsonEscaping` to be emitted into an HTML page or a `