diff --git a/.vsconfig b/.vsconfig index b0c6f21fd92d5c..62cee7a8ded460 100755 --- a/.vsconfig +++ b/.vsconfig @@ -1,53 +1,55 @@ { "version": "1.0", "components": [ - "Microsoft.VisualStudio.Component.CoreEditor", - "Microsoft.VisualStudio.Workload.CoreEditor", - "Microsoft.NetCore.Component.SDK", - "Microsoft.VisualStudio.Component.NuGet", - "Microsoft.Net.Component.4.6.1.TargetingPack", "Microsoft.VisualStudio.Component.Roslyn.Compiler", "Microsoft.VisualStudio.Component.Roslyn.LanguageServices", - "Microsoft.VisualStudio.Component.FSharp", - "Microsoft.NetCore.Component.DevelopmentTools", + "Microsoft.Component.MSBuild", + "Microsoft.Net.Component.4.5.2.TargetingPack", + "Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime", + "Microsoft.VisualStudio.Component.SQL.CLR", + "Microsoft.VisualStudio.Component.CoreEditor", + "Microsoft.VisualStudio.Workload.CoreEditor", "Microsoft.Net.Component.4.8.SDK", "Microsoft.Net.Component.4.7.2.TargetingPack", "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites", - "Microsoft.Component.MSBuild", "Microsoft.VisualStudio.Component.TextTemplating", - "Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime", - "Microsoft.VisualStudio.Component.SQL.CLR", + "Microsoft.VisualStudio.Component.NuGet", "Microsoft.VisualStudio.Component.ManagedDesktop.Core", - "Microsoft.Net.Component.4.5.2.TargetingPack", - "Microsoft.Net.Component.4.5.TargetingPack", - "Microsoft.VisualStudio.Component.IntelliCode", - "Microsoft.Net.Component.4.TargetingPack", - "Microsoft.Net.Component.4.5.1.TargetingPack", - "Microsoft.Net.Component.4.6.TargetingPack", - "Microsoft.Net.ComponentGroup.TargetingPacks.Common", + "Microsoft.NetCore.Component.SDK", + "Microsoft.VisualStudio.Component.FSharp", + "Microsoft.ComponentGroup.ClickOnce.Publish", + "Microsoft.NetCore.Component.DevelopmentTools", + "Microsoft.Net.Component.4.8.TargetingPack", + "Microsoft.Net.Component.4.6.1.TargetingPack", "Microsoft.VisualStudio.Component.DiagnosticTools", + "Microsoft.VisualStudio.Component.IntelliCode", "Microsoft.Net.Component.4.6.2.TargetingPack", - "Microsoft.Net.ComponentGroup.4.6.2.DeveloperTools", "Microsoft.Net.Component.4.7.TargetingPack", - "Microsoft.Net.ComponentGroup.4.7.DeveloperTools", - "Microsoft.Net.Component.4.8.TargetingPack", + "Microsoft.VisualStudio.Component.ClassDesigner", + "Microsoft.VisualStudio.Component.GraphDocument", + "Microsoft.VisualStudio.Component.CodeMap", "Microsoft.VisualStudio.Component.VC.CoreIde", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "Microsoft.VisualStudio.Component.Windows10SDK.18362", + "Microsoft.VisualStudio.Component.Windows10SDK.19041", "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites", "Microsoft.ComponentGroup.Blend", "Microsoft.VisualStudio.Component.FSharp.Desktop", "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging", "Microsoft.VisualStudio.Workload.ManagedDesktop", "Microsoft.VisualStudio.Component.VC.Redist.14.Latest", + "Microsoft.VisualStudio.ComponentGroup.ArchitectureTools.Native", "Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core", + "Microsoft.VisualStudio.Component.VC.Tools.ARM64", "Microsoft.VisualStudio.Component.VC.CLI.Support", "Microsoft.VisualStudio.Workload.NativeDesktop", - "Microsoft.VisualStudio.Component.VC.Tools.ARM64", "Microsoft.VisualStudio.Component.VC.Tools.ARM", + "Microsoft.Net.ComponentGroup.TargetingPacks.Common", + "Microsoft.Net.Component.4.6.TargetingPack", "Microsoft.VisualStudio.Component.Git", "Microsoft.VisualStudio.Component.LinqToSql", + "Microsoft.NetCore.Component.Runtime.3.1", + "Microsoft.NetCore.Component.Runtime.5.0", "Microsoft.Net.Component.4.6.2.SDK", "Microsoft.Net.Component.4.7.SDK" ] -} +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index cc4715cfad4f06..88779ccdac0290 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -280,6 +280,9 @@ Properties false + + true diff --git a/eng/Subsets.props b/eng/Subsets.props index f9bde27cb97770..f26f40c7d7a052 100644 --- a/eng/Subsets.props +++ b/eng/Subsets.props @@ -35,14 +35,24 @@ <_subset Condition="'$(Subset)' == ''">+$(DefaultSubsets)+ + + + CoreCLR + Mono + + Mono Mono - CoreCLR + $(PrimaryRuntimeFlavor) clr.native+linuxdac+clr.corelib+clr.tools+clr.nativecorelib+clr.packages + + clr.iltools+clr.packages mono.llvm+ mono.llvm+ @@ -58,8 +68,8 @@ host.native+host.tools $(DefaultHostSubsets)+host.pkg+host.tests - - host.native + + host.native packs.product $(DefaultPacksSubsets)+packs.tests @@ -225,7 +235,7 @@ - + @@ -317,16 +327,16 @@ - + - - - + + + - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2f2190618a1115..9d351471f4b163 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -4,7 +4,7 @@ https://github.com/dotnet/icu 08293141bc33a81b7e58120535079d8eac36519f - + https://github.com/dotnet/msquic d7db669b70f4dd67ec001c192f9809c218cab88b @@ -54,9 +54,9 @@ https://github.com/dotnet/arcade 89806f0b9e93ad2bbe32c654412835c0801a2032 - + https://github.com/dotnet/arcade - 89806f0b9e93ad2bbe32c654412835c0801a2032 + 6224d1b573b73caaa84176bd83dabe75f202cdc7 https://github.com/dotnet/arcade @@ -186,9 +186,9 @@ https://github.com/dotnet/runtime f7e4c261815c66fde2c1e750b744f193e236c17e - + https://github.com/mono/linker - 6eae01980dc694107bdee0bc723d75a0dd601f0e + b888d672e11588e2bbca1cd3eeaee30d53416897 https://github.com/dotnet/xharness diff --git a/eng/Versions.props b/eng/Versions.props index ecd7f036d099fc..bf0541aa321cbb 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -17,8 +17,8 @@ release true - - 4.0.0-3.21367.2 + + 4.0.0-3.21376.12 true false false @@ -62,7 +62,7 @@ 6.0.0-beta.21370.12 6.0.0-beta.21370.12 6.0.0-beta.21370.12 - 6.0.0-beta.21370.12 + 6.0.0-beta.21372.16 6.0.0-beta.21370.12 6.0.0-beta.21370.12 6.0.0-beta.21370.12 @@ -143,7 +143,7 @@ These are used as reference assemblies only, so they must not take a ProdCon/source-build version. Insert "RefOnly" to avoid assignment via PVP. --> - 16.9.0 + 16.10.0 $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) $(RefOnlyMicrosoftBuildVersion) @@ -166,12 +166,12 @@ 5.0.0-preview-20201009.2 - 6.0.100-preview.6.21370.1 + 6.0.100-preview.6.21376.2 $(MicrosoftNETILLinkTasksVersion) 6.0.0-rc.1.21369.1 - 6.0.0-preview.7.21357.1 + 6.0.0-preview.7.21376.1 11.1.0-alpha.1.21369.1 11.1.0-alpha.1.21369.1 diff --git a/eng/pipelines/common/restore-internal-tools.yml b/eng/pipelines/common/restore-internal-tools.yml index 848c9800c167c9..d289d82b53505c 100644 --- a/eng/pipelines/common/restore-internal-tools.yml +++ b/eng/pipelines/common/restore-internal-tools.yml @@ -7,6 +7,7 @@ steps: - script: $(Build.SourcesDirectory)$(dir)build$(scriptExt) -ci -restore + -configuration $(_BuildConfig) -projects $(Build.SourcesDirectory)/eng/common/internal/Tools.csproj /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/RestoreInternal.binlog /v:normal diff --git a/eng/pipelines/common/templates/runtimes/run-test-job.yml b/eng/pipelines/common/templates/runtimes/run-test-job.yml index fb1a3b55f9afc9..96b85e61bf94cd 100644 --- a/eng/pipelines/common/templates/runtimes/run-test-job.yml +++ b/eng/pipelines/common/templates/runtimes/run-test-job.yml @@ -486,6 +486,9 @@ jobs: gcSimulatorTests: true scenarios: - normal + ${{ if in(parameters.testGroup, 'gc-standalone') }}: + scenarios: + - gcstandalone ${{ if in(parameters.testGroup, 'jitelthookenabled') }}: scenarios: - jitelthookenabled diff --git a/eng/pipelines/coreclr/ci.yml b/eng/pipelines/coreclr/ci.yml index 117f645186fb94..c4cbf06a14bde6 100644 --- a/eng/pipelines/coreclr/ci.yml +++ b/eng/pipelines/coreclr/ci.yml @@ -151,20 +151,6 @@ jobs: displayNameArgs: R2R_CG2 liveLibrariesBuildConfig: Release -# -# Crossgen-comparison jobs -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/crossgen-comparison-job.yml - buildConfig: release - platforms: - # Currently failing globally, should be uncommented once the tracking bug gets fixed: - # https://github.com/dotnet/runtime/issues/1282 - # - Linux_arm - helixQueueGroup: ci - helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml - # # Formatting # diff --git a/eng/pipelines/coreclr/gc-standalone.yml b/eng/pipelines/coreclr/gc-standalone.yml new file mode 100644 index 00000000000000..4d309290705328 --- /dev/null +++ b/eng/pipelines/coreclr/gc-standalone.yml @@ -0,0 +1,49 @@ +trigger: none + +schedules: +- cron: "0 5 * * *" + displayName: Mon through Sun at 9:00 PM (UTC-8:00) + branches: + include: + - main + always: true + +jobs: + +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/build-coreclr-and-libraries-job.yml + buildConfig: checked + platforms: + - Linux_arm64 + - windows_arm64 + - windows_x64 + - CoreClrTestBuildHost # Either OSX_x64 or Linux_x64 + jobParameters: + testGroup: gc-standalone + +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/templates/runtimes/build-test-job.yml + buildConfig: checked + platforms: + - CoreClrTestBuildHost # Either OSX_x64 or Linux_x64 + jobParameters: + testGroup: gc-standalone + liveLibrariesBuildConfig: Release + +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/templates/runtimes/run-test-job.yml + buildConfig: checked + platforms: + - Linux_arm64 + - Linux_x64 + - windows_arm64 + - windows_x64 + helixQueueGroup: ci + helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml + jobParameters: + testGroup: gc-standalone + displayNameArgs: GCStandAlone + liveLibrariesBuildConfig: Release diff --git a/eng/pipelines/coreclr/release-tests.yml b/eng/pipelines/coreclr/release-tests.yml index 485389ccf33375..bfa51bd287826d 100644 --- a/eng/pipelines/coreclr/release-tests.yml +++ b/eng/pipelines/coreclr/release-tests.yml @@ -72,17 +72,3 @@ jobs: readyToRun: true displayNameArgs: R2R -# -# Crossgen-comparison jobs -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/crossgen-comparison-job.yml - buildConfig: release - platforms: - - Linux_arm - helixQueueGroup: ci - helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml - jobParameters: - testGroup: outerloop - liveLibrariesBuildConfig: Release diff --git a/eng/pipelines/coreclr/templates/crossgen-comparison-job.yml b/eng/pipelines/coreclr/templates/crossgen-comparison-job.yml deleted file mode 100644 index 282dcee588e5d8..00000000000000 --- a/eng/pipelines/coreclr/templates/crossgen-comparison-job.yml +++ /dev/null @@ -1,203 +0,0 @@ -parameters: - buildConfig: '' - archType: '' - osGroup: '' - osSubgroup: '' - container: '' - helixQueues: '' - runtimeVariant: '' - crossBuild: false - crossrootfsDir: '' - dependOnEvaluatePaths: false - stagedBuild: false - variables: {} - pool: '' - - # When set to a non-empty value (Debug / Release), it determines libraries - # build configuration to use for the tests. Setting this property implies - # a dependency of this job on the appropriate libraries build and is used - # to construct the name of the Azure artifact representing libraries build - # to use for building the tests. - liveLibrariesBuildConfig: '' - -### Crossgen-comparison job -### -### Ensure that the output of cross-architecture, e.g. x64-hosted-arm-targeting, -### crossgen matches that of native, e.g. arm-hosted-arm-targeting, crossgen. - -jobs: -- template: xplat-pipeline-job.yml - parameters: - buildConfig: ${{ parameters.buildConfig }} - archType: ${{ parameters.archType }} - osGroup: ${{ parameters.osGroup }} - osSubgroup: ${{ parameters.osSubgroup }} - stagedBuild: ${{ parameters.stagedBuild }} - runtimeVariant: ${{ parameters.runtimeVariant }} - liveLibrariesBuildConfig: ${{ parameters.liveLibrariesBuildConfig }} - helixType: 'test/crossgen-comparison/' - pool: ${{ parameters.pool }} - dependOnEvaluatePaths: ${{ parameters.dependOnEvaluatePaths }} - - # Compute job name from template parameters - name: ${{ format('test_crossgen_comparison_{0}{1}_{1}_{2}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - displayName: ${{ format('Test crossgen-comparison {0}{1} {2} {3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - - crossBuild: ${{ parameters.crossBuild }} - crossrootfsDir: ${{ parameters.crossrootfsDir }} - - variables: - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - - group: DotNet-HelixApi-Access - - name: hostArchType - value: x64 - - name: targetFlavor - value: $(osGroup).$(archType).$(buildConfigUpper) - - name: crossFlavor - value: $(osGroup).$(hostArchType)_$(archType).$(buildConfigUpper) - - ${{ if ne(parameters.osGroup, 'windows') }}: - - name: artifactsDirectory - value: $(Build.SourcesDirectory)/artifacts - - name: binDirectory - value: $(artifactsDirectory)/bin - - name: productDirectory - value: $(binDirectory)/coreclr - - name: workItemDirectory - value: $(artifactsDirectory)/tests/coreclr/$(targetFlavor)/Tests/Core_Root - - ${{ if eq(parameters.osGroup, 'windows') }}: - - name: artifactsDirectory - value: $(Build.SourcesDirectory)\artifacts - - name: binDirectory - value: $(artifactsDirectory)\bin - - name: productDirectory - value: $(binDirectory)\coreclr - - name: workItemDirectory - value: $(artifactsDirectory)\tests\coreclr\$(targetFlavor)\Tests\Core_Root - - - ${{ parameters.variables }} - - # 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.liveLibrariesBuildConfig, '') }}: - - ${{ format('libraries_build_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.liveLibrariesBuildConfig) }} - - # Run all steps in the container. - # Note that the containers are defined in platform-matrix.yml - container: ${{ parameters.container }} - timeoutInMinutes: 180 # 3 hrs - - steps: - - # Download product build - - template: /eng/pipelines/common/download-artifact-step.yml - parameters: - unpackFolder: $(buildProductRootFolderPath) - artifactFileName: '$(buildProductArtifactName)$(archiveExtension)' - artifactName: '$(buildProductArtifactName)' - displayName: 'product build' - - # Optionally download live-built libraries - - ${{ if ne(parameters.liveLibrariesBuildConfig, '') }}: - - template: /eng/pipelines/common/download-artifact-step.yml - parameters: - unpackFolder: $(librariesDownloadDir) - cleanUnpackFolder: false - artifactFileName: '$(librariesBuildArtifactName)$(archiveExtension)' - artifactName: '$(librariesBuildArtifactName)' - displayName: 'live-built libraries' - - # Populate Core_Root - - script: $(Build.SourcesDirectory)/src/tests/build$(scriptExt) $(buildConfig) $(archType) $(crossArg) generatelayoutonly - displayName: Populate Core_Root - - # Create directories and ensure crossgen is executable - - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: | - chmod +x $(workItemDirectory)/crossgen - mkdir -p $(workItemDirectory)/log/$(crossFlavor) - displayName: Create directories and ensure crossgen is executable - - ${{ if eq(parameters.osGroup, 'windows') }}: - - script: | - mkdir $(workItemDirectory)\log\$(crossFlavor) - displayName: Create directories - - # Create baseline output on the host (x64) machine - - task: PythonScript@0 - displayName: Create cross-platform crossgen baseline - inputs: - scriptSource: 'filePath' - scriptPath: $(Build.SourcesDirectory)/src/tests/Common/scripts/crossgen_comparison.py - pythonInterpreter: /usr/bin/python3 - ${{ if ne(parameters.osGroup, 'windows') }}: - arguments: - crossgen_framework - --crossgen $(productDirectory)/$(targetFlavor)/$(hostArchType)/crossgen - --il_corelib $(productDirectory)/$(targetFlavor)/IL/System.Private.CoreLib.dll - --core_root $(workItemDirectory) - --result_dir $(workItemDirectory)/log/$(crossFlavor) - ${{ if eq(parameters.osGroup, 'windows') }}: - arguments: - crossgen_framework - --crossgen $(productDirectory)\$(targetFlavor)\$(hostArchType)\crossgen - --il_corelib $(productDirectory)\$(targetFlavor)\IL\System.Private.CoreLib.dll - --core_root $(workItemDirectory) - --result_dir $(workItemDirectory)\log\$(crossFlavor) - - # Dump contents and payload information - - ${{ if ne(parameters.osGroup, 'windows') }}: - - script: | - ls $(workItemDirectory) - du -sh $(workItemDirectory) - displayName: Dump contents and payload information - - ${{ if eq(parameters.osGroup, 'windows') }}: - - script: | - dir $(workItemDirectory) - displayName: Dump contents and payload information - - # Send payload to Helix where the native output is generated and compared to the baseline - - template: /eng/common/templates/steps/send-to-helix.yml - parameters: - DisplayNamePrefix: Run native crossgen and compare output to baseline - HelixSource: $(_HelixSource) - HelixType: 'test/crossgen-comparison/' - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - HelixAccessToken: $(HelixApiAccessToken) - HelixTargetQueues: ${{ join(' ', parameters.helixQueues) }} - ${{ if ne(variables['System.TeamProject'], 'internal') }}: - Creator: $(Creator) - WorkItemTimeout: 3:00 # 3 hours - WorkItemDirectory: '$(workItemDirectory)' - CorrelationPayloadDirectory: '$(Build.SourcesDirectory)/src/tests/Common/scripts' - ${{ if ne(parameters.osName, 'windows') }}: - WorkItemCommand: - chmod +x $HELIX_WORKITEM_PAYLOAD/crossgen; - mkdir -p $HELIX_WORKITEM_PAYLOAD/log/$(targetFlavor); - python3 -u $HELIX_CORRELATION_PAYLOAD/crossgen_comparison.py crossgen_framework - --crossgen $HELIX_WORKITEM_PAYLOAD/crossgen - --il_corelib $HELIX_WORKITEM_PAYLOAD/IL/System.Private.CoreLib.dll - --core_root $HELIX_WORKITEM_PAYLOAD - --result_dir $HELIX_WORKITEM_PAYLOAD/log/$(targetFlavor); - python3 -u $HELIX_CORRELATION_PAYLOAD/crossgen_comparison.py compare - --base_dir $HELIX_WORKITEM_PAYLOAD/log/$(crossFlavor) - --diff_dir $HELIX_WORKITEM_PAYLOAD/log/$(targetFlavor) - ${{ if eq(parameters.osName, 'windows') }}: - WorkItemCommand: - mkdir %HELIX_WORKITEM_PAYLOAD%\log\$(targetFlavor); - python3 -u %HELIX_CORRELATION_PAYLOAD%\crossgen_comparison.py crossgen_framework - --crossgen %HELIX_WORKITEM_PAYLOAD%\crossgen - --il_corelib %HELIX_WORKITEM_PAYLOAD%\IL\System.Private.CoreLib.dll - --core_root %HELIX_WORKITEM_PAYLOAD% - --result_dir %HELIX_WORKITEM_PAYLOAD%\log\$(targetFlavor); - python3 -u %HELIX_CORRELATION_PAYLOAD%\crossgen_comparison.py compare - --base_dir %HELIX_WORKITEM_PAYLOAD%\log\$(crossFlavor) - --diff_dir %HELIX_WORKITEM_PAYLOAD%\log\$(targetFlavor) - - # Publish log - - task: PublishPipelineArtifact@1 - displayName: Publish log - inputs: - pathtoPublish: $(workItemDirectory)/log - artifactName: ${{ format('Testlog_crossgen_comparison_{0}{1}_{2}_{3}', parameters.osGroup, parameters.osSubgroup, parameters.archType, parameters.buildConfig) }} - continueOnError: true - condition: always() diff --git a/eng/pipelines/coreclr/templates/xplat-pipeline-job.yml b/eng/pipelines/coreclr/templates/xplat-pipeline-job.yml index e3200502b7060d..712bd74057c4f3 100644 --- a/eng/pipelines/coreclr/templates/xplat-pipeline-job.yml +++ b/eng/pipelines/coreclr/templates/xplat-pipeline-job.yml @@ -129,7 +129,8 @@ jobs: value: '' # 'innerloop' and 'clrinterpreter' jobs run the Priority 0 tests; everything else runs the Priority 1 tests. - - ${{ if and(ne(parameters.testGroup, 'innerloop'), ne(parameters.testGroup, 'clrinterpreter')) }}: + # 'gc-standalone' is forced to run pri0 as well to start with. + - ${{ if and(ne(parameters.testGroup, 'innerloop'), ne(parameters.testGroup, 'clrinterpreter'), ne(parameters.testGroup, 'gc-standalone')) }}: - ${{ if ne(parameters.osGroup, 'windows') }}: - name: priorityArg value: 'priority1' diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index fdeeecdaa82936..2ae339930ef6bb 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -899,25 +899,6 @@ jobs: - windows_x86 - Linux_x64 -# -# Crossgen-comparison jobs -# Only when CoreCLR is changed -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/crossgen-comparison-job.yml - buildConfig: checked - platforms: - - Linux_arm - helixQueueGroup: pr - helixQueuesTemplate: /eng/pipelines/coreclr/templates/helix-queues-setup.yml - jobParameters: - liveLibrariesBuildConfig: Release - condition: >- - or( - eq(dependencies.evaluate_paths.outputs['SetPathVars_coreclr.containsChange'], true), - eq(variables['isFullMatrix'], true)) - # # CoreCLR Test builds using live libraries release build # Only when CoreCLR is changed diff --git a/eng/testing/ILLinkDescriptors/ILLink.Descriptors.Castle.xml b/eng/testing/ILLinkDescriptors/ILLink.Descriptors.Castle.xml index d9aa664cee8d81..7a8084b4d633bc 100644 --- a/eng/testing/ILLinkDescriptors/ILLink.Descriptors.Castle.xml +++ b/eng/testing/ILLinkDescriptors/ILLink.Descriptors.Castle.xml @@ -2,7 +2,6 @@ - diff --git a/src/coreclr/.nuget/coreclr-packages.proj b/src/coreclr/.nuget/coreclr-packages.proj index 1347e06d5ff0a0..5d6391b6124e22 100644 --- a/src/coreclr/.nuget/coreclr-packages.proj +++ b/src/coreclr/.nuget/coreclr-packages.proj @@ -5,12 +5,15 @@ - + - + + + + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs index cb8800994aa787..a31f67b4ebbff2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Numerics; using System.Runtime.InteropServices; using System.Threading; @@ -45,10 +46,10 @@ private static int KeyToBucket(ref int tableData, nuint source, nuint target) int hashShift = HashShift(ref tableData); #if TARGET_64BIT - ulong hash = (((ulong)source << 32) | ((ulong)source >> 32)) ^ (ulong)target; + ulong hash = BitOperations.RotateLeft((ulong)source, 32) ^ (ulong)target; return (int)((hash * 11400714819323198485ul) >> hashShift); #else - uint hash = (((uint)source >> 16) | ((uint)source << 16)) ^ (uint)target; + uint hash = BitOperations.RotateLeft((uint)source, 16) ^ (uint)target; return (int)((hash * 2654435769u) >> hashShift); #endif } diff --git a/src/coreclr/classlibnative/bcltype/system.cpp b/src/coreclr/classlibnative/bcltype/system.cpp index eece2220c63a91..bf5cd0fc75309a 100644 --- a/src/coreclr/classlibnative/bcltype/system.cpp +++ b/src/coreclr/classlibnative/bcltype/system.cpp @@ -195,6 +195,8 @@ INT32 QCALLTYPE SystemNative::GetProcessorCount() // managed string object buffer. This buffer is not always used, see comments in // the method below. WCHAR g_szFailFastBuffer[256]; +WCHAR *g_pFailFastBuffer = g_szFailFastBuffer; + #define FAIL_FAST_STATIC_BUFFER_LENGTH (sizeof(g_szFailFastBuffer) / sizeof(WCHAR)) // This is the common code for FailFast processing that is wrapped by the two @@ -243,7 +245,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce // Another option would seem to be to implement a new frame type that // protects object references as pinned, but that seems like overkill for // just this problem. - WCHAR *pszMessage = NULL; + WCHAR *pszMessageBuffer = NULL; DWORD cchMessage = (gc.refMesgString == NULL) ? 0 : gc.refMesgString->GetStringLength(); WCHAR * errorSourceString = NULL; @@ -262,32 +264,44 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce if (cchMessage < FAIL_FAST_STATIC_BUFFER_LENGTH) { - pszMessage = g_szFailFastBuffer; + // The static buffer can be used only once to avoid race condition with other threads + pszMessageBuffer = InterlockedExchangeT(&g_pFailFastBuffer, NULL); } - else + + if (pszMessageBuffer == NULL) { // We can fail here, but we can handle the fault. CONTRACT_VIOLATION(FaultViolation); - pszMessage = new (nothrow) WCHAR[cchMessage + 1]; - if (pszMessage == NULL) + pszMessageBuffer = new (nothrow) WCHAR[cchMessage + 1]; + if (pszMessageBuffer == NULL) { // Truncate the message to what will fit in the static buffer. cchMessage = FAIL_FAST_STATIC_BUFFER_LENGTH - 1; - pszMessage = g_szFailFastBuffer; + pszMessageBuffer = InterlockedExchangeT(&g_pFailFastBuffer, NULL); } } - if (cchMessage > 0) - memcpyNoGCRefs(pszMessage, gc.refMesgString->GetBuffer(), cchMessage * sizeof(WCHAR)); - pszMessage[cchMessage] = W('\0'); + const WCHAR *pszMessage; + if (pszMessageBuffer != NULL) + { + if (cchMessage > 0) + memcpyNoGCRefs(pszMessageBuffer, gc.refMesgString->GetBuffer(), cchMessage * sizeof(WCHAR)); + pszMessageBuffer[cchMessage] = W('\0'); + pszMessage = pszMessageBuffer; + } + else + { + pszMessage = W("There is not enough memory to print the supplied FailFast message."); + cchMessage = (DWORD)wcslen(pszMessage); + } if (cchMessage == 0) { WszOutputDebugString(W("CLR: Managed code called FailFast without specifying a reason.\r\n")); } else { - WszOutputDebugString(W("CLR: Managed code called FailFast, saying \"")); + WszOutputDebugString(W("CLR: Managed code called FailFast.\r\n")); WszOutputDebugString(pszMessage); - WszOutputDebugString(W("\"\r\n")); + WszOutputDebugString(W("\r\n")); } LPCWSTR argExceptionString = NULL; diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index 6d675a2a59e247..655fc3281eb2ef 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -66,6 +66,7 @@ $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', 'dotnet-pgo', 'dotnet-pgo.dll')) merge $(DotNetPgoCmd) -o:$(MergedMibcPath) $(DotNetPgoCmd) @(OptimizationMibcFiles->'-i:%(Identity)', ' ') + $(DotNetPgoCmd) --inherit-timestamp diff --git a/src/coreclr/debug/createdump/crashinfo.cpp b/src/coreclr/debug/createdump/crashinfo.cpp index 807036fd200822..151a11d68a75d7 100644 --- a/src/coreclr/debug/createdump/crashinfo.cpp +++ b/src/coreclr/debug/createdump/crashinfo.cpp @@ -361,8 +361,9 @@ bool CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess) { ReleaseHolder pSos = nullptr; - pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); - + if (pClrDataProcess != nullptr) { + pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); + } // For each native and managed thread for (ThreadInfo* thread : m_threads) { diff --git a/src/coreclr/debug/createdump/crashinfo.h b/src/coreclr/debug/createdump/crashinfo.h index 199144e17540ec..ca8f77994ac751 100644 --- a/src/coreclr/debug/createdump/crashinfo.h +++ b/src/coreclr/debug/createdump/crashinfo.h @@ -106,12 +106,12 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback, inline const std::string& Name() const { return m_name; } inline const ModuleInfo* MainModule() const { return m_mainModule; } - inline const std::vector Threads() const { return m_threads; } - inline const std::set ModuleMappings() const { return m_moduleMappings; } - inline const std::set OtherMappings() const { return m_otherMappings; } - inline const std::set MemoryRegions() const { return m_memoryRegions; } + inline const std::vector& Threads() const { return m_threads; } + inline const std::set& ModuleMappings() const { return m_moduleMappings; } + inline const std::set& OtherMappings() const { return m_otherMappings; } + inline const std::set& MemoryRegions() const { return m_memoryRegions; } #ifndef __APPLE__ - inline const std::vector AuxvEntries() const { return m_auxvEntries; } + inline const std::vector& AuxvEntries() const { return m_auxvEntries; } inline size_t GetAuxvSize() const { return m_auxvEntries.size() * sizeof(elf_aux_entry); } #endif diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 65cf32b02020d5..6a78dc35154394 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1155,7 +1155,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX void genConsumeHWIntrinsicOperands(GenTreeHWIntrinsic* tree); #endif // FEATURE_HW_INTRINSICS void genEmitGSCookieCheck(bool pushReg); - void genSetRegToIcon(regNumber reg, ssize_t val, var_types type = TYP_INT, insFlags flags = INS_FLAGS_DONT_CARE); + void genSetRegToIcon(regNumber reg, + ssize_t val, + var_types type = TYP_INT, + insFlags flags = INS_FLAGS_DONT_CARE DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void genCodeForShift(GenTree* tree); #if defined(TARGET_X86) || defined(TARGET_ARM) @@ -1490,7 +1493,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX regNumber reg, ssize_t imm, insFlags flags = INS_FLAGS_DONT_CARE DEBUGARG(size_t targetHandle = 0) - DEBUGARG(unsigned gtFlags = 0)); + DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void instGen_Compare_Reg_To_Zero(emitAttr size, regNumber reg); diff --git a/src/coreclr/jit/codegenarm.cpp b/src/coreclr/jit/codegenarm.cpp index 4cd5f03b57e513..7c8c75ff9a6208 100644 --- a/src/coreclr/jit/codegenarm.cpp +++ b/src/coreclr/jit/codegenarm.cpp @@ -159,7 +159,7 @@ void CodeGen::genEHCatchRet(BasicBlock* block) void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, - insFlags flags DEBUGARG(size_t targetHandle) DEBUGARG(unsigned gtFlags)) + insFlags flags DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { // reg cannot be a FP register assert(!genIsValidFloatReg(reg)); diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index fa7e8fd99fd17e..2428c33fee6f78 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -1594,7 +1594,7 @@ void CodeGen::genEHCatchRet(BasicBlock* block) void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, - insFlags flags DEBUGARG(size_t targetHandle) DEBUGARG(unsigned gtFlags)) + insFlags flags DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { // reg cannot be a FP register assert(!genIsValidFloatReg(reg)); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 292df210aab226..1953f93e076b9a 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -560,7 +560,7 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) //------------------------------------------------------------------------ // genSetRegToIcon: Generate code that will set the given register to the integer constant. // -void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags) +void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags DEBUGARG(GenTreeFlags gtFlags)) { // Reg cannot be a FP reg assert(!genIsValidFloatReg(reg)); @@ -603,7 +603,7 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) else { instGen_Set_Reg_To_Imm(EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr, - INS_FLAGS_DONT_CARE DEBUGARG((size_t)THT_SetGSCookie) DEBUGARG(0)); + INS_FLAGS_DONT_CARE DEBUGARG((size_t)THT_SetGSCookie) DEBUGARG(GTF_EMPTY)); GetEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, initReg, initReg, 0); regSet.verifyRegUsed(initReg); GetEmitter()->emitIns_S_R(INS_str, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index b0f6aab8e30e3b..dcf1768c60a7dd 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1799,7 +1799,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) { // Ngen case - GS cookie constant needs to be accessed through an indirection. instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr, - INS_FLAGS_DONT_CARE DEBUGARG((size_t)THT_GSCookieCheck) DEBUGARG(0)); + INS_FLAGS_DONT_CARE DEBUGARG((size_t)THT_GSCookieCheck) DEBUGARG(GTF_EMPTY)); GetEmitter()->emitIns_R_R_I(INS_ldr, EA_PTRSIZE, regGSConst, regGSConst, 0); } // Load this method's GS value from the stack frame diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index e361a56ae4747c..529d1fe948799c 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -28,7 +28,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX * Generate code that will set the given register to the integer constant. */ -void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags) +void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags DEBUGARG(GenTreeFlags gtFlags)) { // Reg cannot be a FP reg assert(!genIsValidFloatReg(reg)); @@ -45,7 +45,7 @@ void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFla else { // TODO-XArch-CQ: needs all the optimized cases - GetEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(type), reg, val); + GetEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(type), reg, val DEBUGARG(gtFlags)); } } @@ -440,7 +440,7 @@ void CodeGen::genEHFinallyOrFilterRet(BasicBlock* block) void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, - insFlags flags DEBUGARG(size_t targetHandle) DEBUGARG(unsigned gtFlags)) + insFlags flags DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { // reg cannot be a FP register assert(!genIsValidFloatReg(reg)); @@ -505,7 +505,7 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre } else { - genSetRegToIcon(targetReg, cnsVal, targetType); + genSetRegToIcon(targetReg, cnsVal, targetType, INS_FLAGS_DONT_CARE DEBUGARG(tree->gtFlags)); } } break; diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 21e37879eed7ec..36dbf617a70f86 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -3904,6 +3904,114 @@ void emitter::emitRecomputeIGoffsets() #endif } +//---------------------------------------------------------------------------------------- +// emitDispCommentForHandle: +// Displays a comment for a handle, e.g. displays a raw string for GTF_ICON_STR_HDL +// or a class name for GTF_ICON_CLASS_HDL +// +// Arguments: +// handle - a constant value to display a comment for +// flags - a flag that the describes the handle +// +void emitter::emitDispCommentForHandle(size_t handle, GenTreeFlags flag) +{ +#ifdef DEBUG + if (handle == 0) + { + return; + } + +#ifdef TARGET_XARCH + const char* commentPrefix = " ;"; +#else + const char* commentPrefix = " //"; +#endif + + flag &= GTF_ICON_HDL_MASK; + const char* str = nullptr; + + if (flag == GTF_ICON_STR_HDL) + { + const WCHAR* wstr = emitComp->eeGetCPString(handle); + // NOTE: eGetCPString always returns nullptr on Linux/ARM + if (wstr == nullptr) + { + str = "string handle"; + } + else + { + const size_t actualLen = wcslen(wstr); + const size_t maxLength = 63; + const size_t newLen = min(maxLength, actualLen); + + // +1 for null terminator + WCHAR buf[maxLength + 1] = {0}; + wcsncpy(buf, wstr, newLen); + for (size_t i = 0; i < newLen; i++) + { + // Escape \n and \r symbols + if (buf[i] == L'\n' || buf[i] == L'\r') + { + buf[i] = L' '; + } + } + if (actualLen > maxLength) + { + // Append "..." for long strings + buf[maxLength - 3] = L'.'; + buf[maxLength - 2] = L'.'; + buf[maxLength - 1] = L'.'; + } + printf("%s \"%S\"", commentPrefix, buf); + } + } + else if (flag == GTF_ICON_CLASS_HDL) + { + str = emitComp->eeGetClassName(reinterpret_cast(handle)); + } +#ifndef TARGET_XARCH + // These are less useful for xarch: + else if (flag == GTF_ICON_CONST_PTR) + { + str = "const ptr"; + } + else if (flag == GTF_ICON_GLOBAL_PTR) + { + str = "global ptr"; + } + else if (flag == GTF_ICON_FIELD_HDL) + { + str = emitComp->eeGetFieldName(reinterpret_cast(handle)); + } + else if (flag == GTF_ICON_STATIC_HDL) + { + str = "static handle"; + } + else if (flag == GTF_ICON_METHOD_HDL) + { + str = emitComp->eeGetMethodFullName(reinterpret_cast(handle)); + } + else if (flag == GTF_ICON_FTN_ADDR) + { + str = "function address"; + } + else if (flag == GTF_ICON_TOKEN_HDL) + { + str = "token handle"; + } + else + { + str = "unknown"; + } +#endif // TARGET_XARCH + + if (str != nullptr) + { + printf("%s %s", commentPrefix, str); + } +#endif // DEBUG +} + /***************************************************************************** * Bind targets of relative jumps to choose the smallest possible encoding. * X86 and AMD64 have a small and large encoding. @@ -8735,11 +8843,7 @@ const char* emitter::emitOffsetToLabel(unsigned offs) if (ig->igOffs == offs) { - // Found it! - sprintf_s(buf[curBuf], TEMP_BUFFER_LEN, "%s", emitLabelString(ig)); - retbuf = buf[curBuf]; - curBuf = (curBuf + 1) % 4; - return retbuf; + return emitLabelString(ig); } else if (ig->igOffs > offs) { diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index ef67148ea962a3..4776f14648221c 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -510,6 +510,8 @@ class emitter void emitRecomputeIGoffsets(); + void emitDispCommentForHandle(size_t handle, GenTreeFlags flags); + /************************************************************************/ /* The following describes a single instruction */ /************************************************************************/ @@ -545,7 +547,7 @@ class emitter size_t idSize; // size of the instruction descriptor unsigned idVarRefOffs; // IL offset for LclVar reference size_t idMemCookie; // for display of method name (also used by switch table) - unsigned idFlags; // for determining type of handle in idMemCookie + GenTreeFlags idFlags; // for determining type of handle in idMemCookie bool idFinallyCall; // Branch instruction is a call to finally bool idCatchRet; // Instruction is for a catch 'return' CORINFO_SIG_INFO* idCallSig; // Used to report native call site signatures to the EE diff --git a/src/coreclr/jit/emitarm.cpp b/src/coreclr/jit/emitarm.cpp index 387b0bb8f5d573..b8a730a31a7488 100644 --- a/src/coreclr/jit/emitarm.cpp +++ b/src/coreclr/jit/emitarm.cpp @@ -1711,8 +1711,11 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) * Add an instruction referencing a register and a constant. */ -void emitter::emitIns_R_I( - instruction ins, emitAttr attr, regNumber reg, target_ssize_t imm, insFlags flags /* = INS_FLAGS_DONT_CARE */) +void emitter::emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + target_ssize_t imm, + insFlags flags /* = INS_FLAGS_DONT_CARE */ DEBUGARG(GenTreeFlags gtFlags)) { insFormat fmt = IF_NONE; @@ -2016,6 +2019,7 @@ void emitter::emitIns_R_I( id->idInsSize(isz); id->idInsFlags(sf); id->idReg1(reg); + INDEBUG(id->idDebugOnlyInfo()->idFlags = gtFlags); dispIns(id); appendToCurIG(id); diff --git a/src/coreclr/jit/emitarm.h b/src/coreclr/jit/emitarm.h index ac6e4f139db7f8..6da0ceaa7ee262 100644 --- a/src/coreclr/jit/emitarm.h +++ b/src/coreclr/jit/emitarm.h @@ -220,8 +220,11 @@ void emitIns_I(instruction ins, emitAttr attr, target_ssize_t imm); void emitIns_R(instruction ins, emitAttr attr, regNumber reg); -void emitIns_R_I( - instruction ins, emitAttr attr, regNumber reg, target_ssize_t imm, insFlags flags = INS_FLAGS_DONT_CARE); +void emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + target_ssize_t imm, + insFlags flags = INS_FLAGS_DONT_CARE DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_MovRelocatableImmediate(instruction ins, emitAttr attr, regNumber reg, BYTE* addr); void emitIns_Mov(instruction ins, diff --git a/src/coreclr/jit/emitarm64.cpp b/src/coreclr/jit/emitarm64.cpp index 84c49d94723d43..771ce88ec849cb 100644 --- a/src/coreclr/jit/emitarm64.cpp +++ b/src/coreclr/jit/emitarm64.cpp @@ -3715,7 +3715,11 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) * Add an instruction referencing a register and a constant. */ -void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t imm, insOpts opt /* = INS_OPTS_NONE */) +void emitter::emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + ssize_t imm, + insOpts opt /* = INS_OPTS_NONE */ DEBUGARG(GenTreeFlags gtFlags)) { emitAttr size = EA_SIZE(attr); emitAttr elemsize = EA_UNKNOWN; @@ -3965,6 +3969,7 @@ void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t id->idInsOpt(opt); id->idReg1(reg); + INDEBUG(id->idDebugOnlyInfo()->idFlags = gtFlags); dispIns(id); appendToCurIG(id); @@ -8098,7 +8103,7 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu void emitter::emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, - ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(unsigned gtFlags)) + ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { assert(EA_IS_RELOC(attr)); emitAttr size = EA_SIZE(attr); @@ -12257,7 +12262,6 @@ void emitter::emitDispIns( ssize_t index2; unsigned registerListSize; const char* targetName; - const WCHAR* stringLiteral; case IF_BI_0A: // BI_0A ......iiiiiiiiii iiiiiiiiiiiiiiii simm26:00 case IF_BI_0B: // BI_0B ......iiiiiiiiii iiiiiiiiiii..... simm19:00 @@ -12363,9 +12367,8 @@ void emitter::emitDispIns( case IF_LARGEADR: assert(insOptsNone(id->idInsOpt())); emitDispReg(id->idReg1(), size, true); - imm = emitGetInsSC(id); - targetName = nullptr; - stringLiteral = nullptr; + imm = emitGetInsSC(id); + targetName = nullptr; /* Is this actually a reference to a data section? */ if (fmt == IF_LARGEADR) @@ -12398,8 +12401,7 @@ void emitter::emitDispIns( { printf("HIGH RELOC "); emitDispImm((ssize_t)id->idAddr()->iiaAddr, false); - size_t targetHandle = id->idDebugOnlyInfo()->idMemCookie; - unsigned idFlags = id->idDebugOnlyInfo()->idFlags & GTF_ICON_HDL_MASK; + size_t targetHandle = id->idDebugOnlyInfo()->idMemCookie; if (targetHandle == THT_IntializeArrayIntrinsics) { @@ -12413,53 +12415,6 @@ void emitter::emitDispIns( { targetName = "SetGlobalSecurityCookie"; } - else if (idFlags == GTF_ICON_CONST_PTR) - { - targetName = "const ptr"; - } - else if (idFlags == GTF_ICON_GLOBAL_PTR) - { - targetName = "global ptr"; - } - else if (idFlags == GTF_ICON_STR_HDL) - { - stringLiteral = emitComp->eeGetCPString(targetHandle); - // Note that eGetCPString isn't currently implemented on Linux/ARM - // and instead always returns nullptr. However, use it here, so in - // future, once it is is implemented, no changes will be needed here. - if (stringLiteral == nullptr) - { - targetName = "String handle"; - } - } - else if (idFlags == GTF_ICON_FIELD_HDL) - { - targetName = emitComp->eeGetFieldName((CORINFO_FIELD_HANDLE)targetHandle); - } - else if (idFlags == GTF_ICON_STATIC_HDL) - { - targetName = "Static handle"; - } - else if (idFlags == GTF_ICON_METHOD_HDL) - { - targetName = emitComp->eeGetMethodFullName((CORINFO_METHOD_HANDLE)targetHandle); - } - else if (idFlags == GTF_ICON_FTN_ADDR) - { - targetName = "Function address"; - } - else if (idFlags == GTF_ICON_CLASS_HDL) - { - targetName = emitComp->eeGetClassName((CORINFO_CLASS_HANDLE)targetHandle); - } - else if (idFlags == GTF_ICON_TOKEN_HDL) - { - targetName = "Token handle"; - } - else - { - targetName = "Unknown"; - } } else if (id->idIsBound()) { @@ -12475,9 +12430,9 @@ void emitter::emitDispIns( { printf(" // [%s]", targetName); } - else if (stringLiteral != nullptr) + else { - printf(" // [%S]", stringLiteral); + emitDispCommentForHandle(id->idDebugOnlyInfo()->idMemCookie, id->idDebugOnlyInfo()->idFlags); } break; diff --git a/src/coreclr/jit/emitarm64.h b/src/coreclr/jit/emitarm64.h index b25659368c1d12..a5e98d24b569aa 100644 --- a/src/coreclr/jit/emitarm64.h +++ b/src/coreclr/jit/emitarm64.h @@ -728,7 +728,11 @@ void emitIns_I(instruction ins, emitAttr attr, ssize_t imm); void emitIns_R(instruction ins, emitAttr attr, regNumber reg); -void emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t imm, insOpts opt = INS_OPTS_NONE); +void emitIns_R_I(instruction ins, + emitAttr attr, + regNumber reg, + ssize_t imm, + insOpts opt = INS_OPTS_NONE DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_R_F(instruction ins, emitAttr attr, regNumber reg, double immDbl, insOpts opt = INS_OPTS_NONE); @@ -827,7 +831,7 @@ void emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, void emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, - ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(unsigned gtFlags = 0)); + ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_AR_R(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs); diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 6e0de0d59e195b..dc55403d6dcf30 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -3965,7 +3965,7 @@ void emitter::emitIns_R(instruction ins, emitAttr attr, regNumber reg) * Add an instruction referencing a register and a constant. */ -void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t val) +void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t val DEBUGARG(GenTreeFlags gtFlags)) { emitAttr size = EA_SIZE(attr); @@ -4089,8 +4089,8 @@ void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t id->idIns(ins); id->idInsFmt(fmt); id->idReg1(reg); - id->idCodeSize(sz); + INDEBUG(id->idDebugOnlyInfo()->idFlags = gtFlags); dispIns(id); emitCurIGsize += sz; @@ -8851,6 +8851,7 @@ void emitter::emitDispIns( else { PRINT_CONSTANT: + ssize_t srcVal = val; // Munge any pointers if we want diff-able disassembly if (emitComp->opts.disDiffable) { @@ -8872,6 +8873,7 @@ void emitter::emitDispIns( { // (val < 0) printf("-0x%IX", -val); } + emitDispCommentForHandle(srcVal, id->idDebugOnlyInfo()->idFlags & GTF_ICON_HDL_MASK); } break; diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index f952a6f649f440..632fd95bcb1406 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -327,7 +327,7 @@ void emitIns_R(instruction ins, emitAttr attr, regNumber reg); void emitIns_C(instruction ins, emitAttr attr, CORINFO_FIELD_HANDLE fdlHnd, int offs); -void emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t val); +void emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t val DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); void emitIns_Mov(instruction ins, emitAttr attr, regNumber dstReg, regNumber srgReg, bool canSkip); diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 8263c7db2bf946..30c38ff3d5550d 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -6117,7 +6117,7 @@ void Compiler::optRecordLoopMemoryDependence(GenTree* tree, BasicBlock* block, V // we know of. Update the map. // JITDUMP(" ==> Updating loop memory dependence of [%06u] to " FMT_LP "\n", dspTreeID(tree), updateLoopNum); - map->Set(tree, optLoopTable[updateLoopNum].lpEntry); + map->Set(tree, optLoopTable[updateLoopNum].lpEntry, NodeToLoopMemoryBlockMap::Overwrite); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 86d0d5dae10de6..bd5821697a824e 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2125,7 +2125,7 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN, V // // Return Value: - Returns the ValueNum associated with 'func'('arg0VN','arg1VN','arg2VN','arg3VN') // -// Note: Currently the only four operand func is the VNF_PtrToArrElem operation +// Note: Currently the only four operand funcs are VNF_PtrToArrElem and VNF_MapStore. // ValueNum ValueNumStore::VNForFunc( var_types typ, VNFunc func, ValueNum arg0VN, ValueNum arg1VN, ValueNum arg2VN, ValueNum arg3VN) @@ -2282,7 +2282,7 @@ ValueNum ValueNumStore::VNForMapSelectWork( else { // Give up if we've run out of budget. - if (--(*pBudget) <= 0) + if (*pBudget == 0) { // We have to use 'nullptr' for the basic block here, because subsequent expressions // in different blocks may find this result in the VNFunc2Map -- other expressions in @@ -2293,6 +2293,9 @@ ValueNum ValueNumStore::VNForMapSelectWork( return res; } + // Reduce our budget by one + (*pBudget)--; + // If it's recursive, stop the recursion. if (SelectIsBeingEvaluatedRecursively(arg0VN, arg1VN)) { @@ -2329,9 +2332,9 @@ ValueNum ValueNumStore::VNForMapSelectWork( assert(funcApp.m_args[1] != arg1VN); // we already checked this above. #if FEATURE_VN_TRACE_APPLY_SELECTORS JITDUMP(" AX2: " FMT_VN " != " FMT_VN " ==> select([" FMT_VN "]store(" FMT_VN ", " FMT_VN - ", " FMT_VN "), " FMT_VN ") ==> select(" FMT_VN ", " FMT_VN ").\n", + ", " FMT_VN "), " FMT_VN ") ==> select(" FMT_VN ", " FMT_VN ") remaining budget is %d.\n", arg1VN, funcApp.m_args[1], arg0VN, funcApp.m_args[0], funcApp.m_args[1], funcApp.m_args[2], - arg1VN, funcApp.m_args[0], arg1VN); + arg1VN, funcApp.m_args[0], arg1VN, *pBudget); #endif // This is the equivalent of the recursive tail call: // return VNForMapSelect(vnk, typ, funcApp.m_args[0], arg1VN); @@ -4351,8 +4354,9 @@ var_types ValueNumStore::TypeOfVN(ValueNum vn) } //------------------------------------------------------------------------ -// LoopOfVN: If the given value number is VNF_MemOpaque or VNF_MapStore, return -// the loop number where the memory update occurs, otherwise returns MAX_LOOP_NUM. +// LoopOfVN: If the given value number is VNF_MemOpaque, VNF_MapStore, or +// VNF_MemoryPhiDef, return the loop number where the memory update occurs, +// otherwise returns MAX_LOOP_NUM. // // Arguments: // vn - Value number to query @@ -4374,6 +4378,11 @@ BasicBlock::loopNumber ValueNumStore::LoopOfVN(ValueNum vn) { return (BasicBlock::loopNumber)funcApp.m_args[3]; } + else if (funcApp.m_func == VNF_PhiMemoryDef) + { + BasicBlock* const block = reinterpret_cast(ConstantValue(funcApp.m_args[0])); + return block->bbNatLoopNum; + } } return BasicBlock::MAX_LOOP_NUM; diff --git a/src/coreclr/pal/src/cruntime/filecrt.cpp b/src/coreclr/pal/src/cruntime/filecrt.cpp index 6a14a9cd6f5677..78aab0c4d16181 100644 --- a/src/coreclr/pal/src/cruntime/filecrt.cpp +++ b/src/coreclr/pal/src/cruntime/filecrt.cpp @@ -229,11 +229,16 @@ CorUnix::InternalOpen( va_end(ap); } + do + { #if OPEN64_IS_USED_INSTEAD_OF_OPEN nRet = open64(szPath, nFlags, mode); #else nRet = open(szPath, nFlags, mode); #endif + } + while ((nRet == -1) && (errno == EINTR)); + return nRet; } diff --git a/src/coreclr/tools/Common/Internal/Text/Utf8String.cs b/src/coreclr/tools/Common/Internal/Text/Utf8String.cs index cc1b045bb19a96..9b44102c582c5a 100644 --- a/src/coreclr/tools/Common/Internal/Text/Utf8String.cs +++ b/src/coreclr/tools/Common/Internal/Text/Utf8String.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Numerics; using System.Runtime.CompilerServices; using System.Text; @@ -42,38 +43,31 @@ public override bool Equals(object obj) return (obj is Utf8String) && Equals((Utf8String)obj); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int _rotl(int value, int shift) - { - // This is expected to be optimized into a single rotl instruction - return (int)(((uint)value << shift) | ((uint)value >> (32 - shift))); - } - public unsafe override int GetHashCode() { int length = _value.Length; - int hash = length; + uint hash = (uint)length; fixed (byte* ap = _value) { byte* a = ap; while (length >= 4) { - hash = (hash + _rotl(hash, 5)) ^ *(int*)a; + hash = (hash + BitOperations.RotateLeft(hash, 5)) ^ *(uint*)a; a += 4; length -= 4; } if (length >= 2) { - hash = (hash + _rotl(hash, 5)) ^ *(short*)a; + hash = (hash + BitOperations.RotateLeft(hash, 5)) ^ *(ushort*)a; a += 2; length -= 2; } if (length > 0) { - hash = (hash + _rotl(hash, 5)) ^ *a; + hash = (hash + BitOperations.RotateLeft(hash, 5)) ^ *a; } - hash += _rotl(hash, 7); - hash += _rotl(hash, 15); - return hash; + hash += BitOperations.RotateLeft(hash, 7); + hash += BitOperations.RotateLeft(hash, 15); + return (int)hash; } } diff --git a/src/coreclr/tools/Directory.Build.props b/src/coreclr/tools/Directory.Build.props index e699ed222e7072..b8e4a4e912df23 100644 --- a/src/coreclr/tools/Directory.Build.props +++ b/src/coreclr/tools/Directory.Build.props @@ -4,7 +4,6 @@ false false false - true 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 079ef257595a70..b9671aa4850223 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 @@ -230,8 +230,8 @@ internal struct ArgLocDesc public bool m_fRequires64BitAlignment; // ARM - True if the argument should always be aligned (in registers or on the stack - public int m_idxStack; // First stack slot used (or -1) - public int m_cStack; // Count of stack slots used (or 0) + public int m_byteStackIndex; // Stack offset in bytes (or -1) + public int m_byteStackSize; // Stack size in bytes // Initialize to represent a non-placed argument (no register or stack slots referenced). public void Init() @@ -240,8 +240,8 @@ public void Init() m_cFloatReg = 0; m_idxGenReg = -1; m_cGenReg = 0; - m_idxStack = -1; - m_cStack = 0; + m_byteStackIndex = -1; + m_byteStackSize = 0; m_fRequires64BitAlignment = false; } @@ -531,6 +531,7 @@ private uint SizeOfArgStack() if (!_SIZE_OF_ARG_STACK_COMPUTED) ForceSigWalk(); Debug.Assert(_SIZE_OF_ARG_STACK_COMPUTED); + Debug.Assert((_nSizeOfArgStack % _transitionBlock.PointerSize) == 0); return (uint)_nSizeOfArgStack; } @@ -550,6 +551,7 @@ public int SizeOfFrameArgumentArray() size += (uint)_transitionBlock.SizeOfArgumentRegisters; } + Debug.Assert((size % _transitionBlock.PointerSize) == 0); return (int)size; } @@ -784,14 +786,14 @@ public int GetNextOffset() { case CallingConventions.StdCall: _numRegistersUsed = ArchitectureConstants.NUM_ARGUMENT_REGISTERS; - _curOfs = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * _transitionBlock.PointerSize + initialArgOffset; + _ofsStack = TransitionBlock.GetOffsetOfArgs() + numRegistersUsed * _transitionBlock.PointerSize + initialArgOffset; break; case CallingConventions.ManagedStatic: case CallingConventions.ManagedInstance: _numRegistersUsed = numRegistersUsed; // DESKTOP BEHAVIOR _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + SizeOfArgStack()); - _curOfs = (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); + _ofsStack= (int)(TransitionBlock.GetOffsetOfArgs() + initialArgOffset); break; default: @@ -801,9 +803,9 @@ public int GetNextOffset() #endif _x86NumRegistersUsed = numRegistersUsed; #if PROJECTN - _x86CurOfs = (int)(_transitionBlock.OffsetOfArgs + initialArgOffset); + _x86OfsStack = (int)(_transitionBlock.OffsetOfArgs + initialArgOffset); #else - _x86CurOfs = (int)(_transitionBlock.OffsetOfArgs + SizeOfArgStack()); + _x86OfsStack = (int)(_transitionBlock.OffsetOfArgs + SizeOfArgStack()); #endif break; @@ -822,14 +824,14 @@ public int GetNextOffset() case TargetArchitecture.ARM: _armIdxGenReg = numRegistersUsed; - _armIdxStack = 0; + _armOfsStack = 0; _armWFPRegs = 0; break; case TargetArchitecture.ARM64: _arm64IdxGenReg = numRegistersUsed; - _arm64IdxStack = 0; + _arm64OfsStack = 0; _arm64IdxFPReg = 0; break; @@ -879,11 +881,11 @@ public int GetNextOffset() } #if PROJECTN - argOfs = _x86CurOfs; - _x86CurOfs += _transitionBlock.StackElemSize(argSize); + argOfs = _x86OfsStack; + _x86OfsStack += _transitionBlock.StackElemSize(argSize); #else - _x86CurOfs -= _transitionBlock.StackElemSize(argSize); - argOfs = _x86CurOfs; + _x86OfsStack -= _transitionBlock.StackElemSize(argSize); + argOfs = _x86OfsStack; #endif Debug.Assert(argOfs >= _transitionBlock.OffsetOfArgs); return argOfs; @@ -988,7 +990,7 @@ public int GetNextOffset() _fX64UnixArgInRegisters = false; argOfs = _transitionBlock.OffsetOfArgs + _x64UnixIdxStack * 8; - int cArgSlots = cbArg / _transitionBlock.StackElemSize(); + int cArgSlots = cbArg / _transitionBlock.PointerSize; _x64UnixIdxStack += cArgSlots; return argOfs; @@ -1081,7 +1083,7 @@ public int GetNextOffset() _armRequires64BitAlignment = fRequiresAlign64Bit; int cbArg = _transitionBlock.StackElemSize(argSize); - int cArgSlots = cbArg / 4; + Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0); // 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). @@ -1127,13 +1129,13 @@ public int GetNextOffset() // Doubles or HFAs containing doubles need the stack aligned appropriately. if (fRequiresAlign64Bit) - _armIdxStack = ALIGN_UP(_armIdxStack, 2); + _armOfsStack = ALIGN_UP(_armOfsStack, _transitionBlock.PointerSize * 2); // Indicate the stack location of the argument to the caller. - int argOfsInner = _transitionBlock.OffsetOfArgs + _armIdxStack * 4; + int argOfsInner = _transitionBlock.OffsetOfArgs + _armOfsStack; // Record the stack usage. - _armIdxStack += cArgSlots; + _armOfsStack += cbArg; return argOfsInner; } @@ -1154,10 +1156,10 @@ public int GetNextOffset() int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _armIdxGenReg * 4; int cRemainingRegs = 4 - _armIdxGenReg; - if (cArgSlots <= cRemainingRegs) + if (cbArg <= cRemainingRegs * _transitionBlock.PointerSize) { // Mark the registers just allocated as used. - _armIdxGenReg += cArgSlots; + _armIdxGenReg += ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize; return argOfsInner; } @@ -1168,9 +1170,9 @@ public int GetNextOffset() _armIdxGenReg = 4; - if (_armIdxStack == 0) + if (_armOfsStack == 0) { - _armIdxStack += cArgSlots - cRemainingRegs; + _armOfsStack += cbArg - cRemainingRegs * _transitionBlock.PointerSize; return argOfsInner; } } @@ -1179,13 +1181,13 @@ public int GetNextOffset() { // The argument requires 64-bit alignment. If it is going to be passed on the stack, align // the next stack slot. See step C.6 in the algorithm in the ABI spec. - _armIdxStack = ALIGN_UP(_armIdxStack, 2); + _armOfsStack = ALIGN_UP(_armOfsStack, _transitionBlock.PointerSize * 2); } - argOfs = _transitionBlock.OffsetOfArgs + _armIdxStack * 4; + argOfs = _transitionBlock.OffsetOfArgs + _armOfsStack; // Advance the stack pointer over the argument just placed. - _armIdxStack += cArgSlots; + _armOfsStack += cbArg; return argOfs; } @@ -1193,6 +1195,7 @@ public int GetNextOffset() case TargetArchitecture.ARM64: { int cFPRegs = 0; + bool isFloatHFA = false; switch (argType) { @@ -1216,6 +1219,10 @@ public int GetNextOffset() _argLocDescForStructInRegs.m_idxFloatReg = _arm64IdxFPReg; int haElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize(); + if (haElementSize == 4) + { + isFloatHFA = true; + } cFPRegs = argSize / haElementSize; _argLocDescForStructInRegs.m_cFloatReg = cFPRegs; @@ -1241,8 +1248,8 @@ public int GetNextOffset() break; } - int cbArg = _transitionBlock.StackElemSize(argSize); - int cArgSlots = cbArg / _transitionBlock.StackElemSize(); + bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE); + int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, isFloatHFA); if (cFPRegs > 0 && !IsVarArg) { @@ -1260,12 +1267,15 @@ public int GetNextOffset() } else { + Debug.Assert(_transitionBlock.IsAppleArm64ABI || (cbArg % _transitionBlock.PointerSize) == 0); + + int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize; // Only x0-x7 are valid argument registers (x8 is always the return buffer) - if (_arm64IdxGenReg + cArgSlots <= 8) + if (_arm64IdxGenReg + regSlots <= 8) { // The entirety of the arg fits in the register slots. int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _arm64IdxGenReg * 8; - _arm64IdxGenReg += cArgSlots; + _arm64IdxGenReg += regSlots; return argOfsInner; } else if (_context.Target.IsWindows && IsVarArg && (_arm64IdxGenReg < 8)) @@ -1275,9 +1285,9 @@ public int GetNextOffset() // into x0-x7, and any remaining stack arguments are placed normally. int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _arm64IdxGenReg * 8; - // Increase m_idxStack to account for the space used for the remainder of the arg after - // register slots are filled. - _arm64IdxStack += (_arm64IdxGenReg + cArgSlots - 8); + // Increase m_ofsStack to account for the space used for the remainder of the arg after + // registers are filled. + _arm64OfsStack += cbArg + (_arm64IdxGenReg - 8) * _transitionBlock.PointerSize; // We used up the remaining reg slots. _arm64IdxGenReg = 8; @@ -1291,8 +1301,27 @@ public int GetNextOffset() } } - argOfs = _transitionBlock.OffsetOfArgs + _arm64IdxStack * 8; - _arm64IdxStack += cArgSlots; + if (_transitionBlock.IsAppleArm64ABI) + { + int alignment; + if (!isValueType) + { + Debug.Assert((cbArg & (cbArg - 1)) == 0); + alignment = cbArg; + } + else if (isFloatHFA) + { + alignment = 4; + } + else + { + alignment = 8; + } + _arm64OfsStack = ALIGN_UP(_arm64OfsStack, alignment); + } + + argOfs = _transitionBlock.OffsetOfArgs + _arm64OfsStack; + _arm64OfsStack += cbArg; return argOfs; } @@ -1321,6 +1350,21 @@ public int GetArgSize() return _argSize; } + public bool IsValueType() + { + return (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE); + } + + public bool IsFloatHfa() + { + if (IsValueType() && !IsVarArg && _argTypeHandle.IsHomogeneousAggregate()) + { + int hfaElementSize = _argTypeHandle.GetHomogeneousAggregateElementSize(); + return hfaElementSize == 4; + } + return false; + } + private void ForceSigWalk() { // This can be only used before the actual argument iteration started @@ -1445,14 +1489,15 @@ private void ForceSigWalk() { // All stack arguments take just one stack slot on AMD64 because of arguments bigger // than a stack slot are passed by reference. - stackElemSize = _transitionBlock.StackElemSize(); + stackElemSize = _transitionBlock.PointerSize; } } else { - stackElemSize = _transitionBlock.StackElemSize(GetArgSize()); + stackElemSize = _transitionBlock.StackElemSize(GetArgSize(), IsValueType(), IsFloatHfa()); + if (IsArgPassedByRef()) - stackElemSize = _transitionBlock.StackElemSize(); + stackElemSize = _transitionBlock.PointerSize; } int endOfs = ofs + stackElemSize; @@ -1477,6 +1522,9 @@ private void ForceSigWalk() } } + // arg stack size is rounded to the pointer size on all platforms. + nSizeOfArgStack = ALIGN_UP(nSizeOfArgStack, _transitionBlock.PointerSize); + // Cache the result _nSizeOfArgStack = nSizeOfArgStack; _SIZE_OF_ARG_STACK_COMPUTED = true; @@ -1497,12 +1545,14 @@ private void ForceSigWalk() pLoc.m_fRequires64BitAlignment = _armRequires64BitAlignment; - int cSlots = (GetArgSize() + 3) / 4; + int byteArgSize = GetArgSize(); if (_transitionBlock.IsFloatArgumentRegisterOffset(argOffset)) { - pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 4; - pLoc.m_cFloatReg = cSlots; + int floatRegOfsInBytes = argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters; + Debug.Assert((floatRegOfsInBytes % _transitionBlock.FloatRegisterSize) == 0); + pLoc.m_idxFloatReg = floatRegOfsInBytes / _transitionBlock.FloatRegisterSize; + pLoc.m_cFloatReg = ALIGN_UP(byteArgSize, _transitionBlock.FloatRegisterSize) / _transitionBlock.FloatRegisterSize; return pLoc; } @@ -1510,22 +1560,22 @@ private void ForceSigWalk() { pLoc.m_idxGenReg = _transitionBlock.GetArgumentIndexFromOffset(argOffset); - if (cSlots <= (4 - pLoc.m_idxGenReg)) + if (byteArgSize <= (4 - pLoc.m_idxGenReg) * _transitionBlock.PointerSize) { - pLoc.m_cGenReg = (short)cSlots; + pLoc.m_cGenReg = (short)(ALIGN_UP(byteArgSize, _transitionBlock.PointerSize) / _transitionBlock.PointerSize); } else { pLoc.m_cGenReg = (short)(4 - pLoc.m_idxGenReg); - pLoc.m_idxStack = 0; - pLoc.m_cStack = cSlots - pLoc.m_cGenReg; + pLoc.m_byteStackIndex = 0; + pLoc.m_byteStackSize = _transitionBlock.StackElemSize(byteArgSize) - pLoc.m_cGenReg * _transitionBlock.PointerSize; } } else { - pLoc.m_idxStack = _transitionBlock.GetArgumentIndexFromOffset(argOffset) - 4; - pLoc.m_cStack = cSlots; + pLoc.m_byteStackIndex = _transitionBlock.GetStackArgumentByteIndexFromOffset(argOffset); + pLoc.m_byteStackSize = _transitionBlock.StackElemSize(byteArgSize); } return pLoc; } @@ -1538,8 +1588,9 @@ private void ForceSigWalk() if (_transitionBlock.IsFloatArgumentRegisterOffset(argOffset)) { - // Dividing by 16 as size of each register in FloatArgumentRegisters is 16 bytes. - pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 16; + int floatRegOfsInBytes = argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters; + Debug.Assert((floatRegOfsInBytes % _transitionBlock.FloatRegisterSize) == 0); + pLoc.m_idxFloatReg = floatRegOfsInBytes / _transitionBlock.FloatRegisterSize; if (!_argTypeHandle.IsNull() && _argTypeHandle.IsHomogeneousAggregate()) { @@ -1553,24 +1604,24 @@ private void ForceSigWalk() return pLoc; } - int cSlots = (GetArgSize() + 7) / 8; + int byteArgSize = GetArgSize(); // Composites greater than 16bytes are passed by reference TypeHandle dummy; if (GetArgType(out dummy) == CorElementType.ELEMENT_TYPE_VALUETYPE && GetArgSize() > _transitionBlock.EnregisteredParamTypeMaxSize) { - cSlots = 1; + byteArgSize = _transitionBlock.PointerSize; } if (!_transitionBlock.IsStackArgumentOffset(argOffset)) { pLoc.m_idxGenReg = _transitionBlock.GetArgumentIndexFromOffset(argOffset); - pLoc.m_cGenReg = (short)cSlots; + pLoc.m_cGenReg = (short)(ALIGN_UP(byteArgSize, _transitionBlock.PointerSize) / _transitionBlock.PointerSize); } else { - pLoc.m_idxStack = _transitionBlock.GetStackArgumentIndexFromOffset(argOffset); - pLoc.m_cStack = cSlots; + pLoc.m_byteStackIndex = _transitionBlock.GetStackArgumentByteIndexFromOffset(argOffset); + pLoc.m_byteStackSize = _transitionBlock.StackElemSize(byteArgSize, IsValueType(), IsFloatHfa()); } return pLoc; } @@ -1595,8 +1646,9 @@ private void ForceSigWalk() if (_transitionBlock.IsFloatArgumentRegisterOffset(argOffset)) { - // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes. - pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 8; + int floatRegOfsInBytes = argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters; + Debug.Assert((floatRegOfsInBytes % _transitionBlock.FloatRegisterSize) == 0); + pLoc.m_idxFloatReg = floatRegOfsInBytes / _transitionBlock.FloatRegisterSize; pLoc.m_cFloatReg = 1; } else if (!_transitionBlock.IsStackArgumentOffset(argOffset)) @@ -1606,14 +1658,13 @@ private void ForceSigWalk() } else { - pLoc.m_idxStack = (argOffset - _transitionBlock.OffsetOfArgs) / 8; - int argOnStackSize; - int stackElemSize = _transitionBlock.StackElemSize(); + pLoc.m_byteStackIndex = _transitionBlock.GetStackArgumentByteIndexFromOffset(argOffset); + int argSizeInBytes; if (IsArgPassedByRef()) - argOnStackSize = stackElemSize; + argSizeInBytes = _transitionBlock.PointerSize; else - argOnStackSize = GetArgSize(); - pLoc.m_cStack = (argOnStackSize + stackElemSize - 1) / stackElemSize; + argSizeInBytes = GetArgSize(); + pLoc.m_byteStackSize = _transitionBlock.StackElemSize(argSizeInBytes); } return pLoc; } @@ -1641,7 +1692,7 @@ private void ForceSigWalk() private TypeHandle _argTypeHandleOfByRefParam; private bool _argForceByRef; - private int _x86CurOfs; // Current position of the stack iterator + private int _x86OfsStack; // Current position of the stack iterator private int _x86NumRegistersUsed; private int _x64UnixIdxGenReg; @@ -1651,13 +1702,13 @@ private void ForceSigWalk() private int _x64WindowsCurOfs; // Current position of the stack iterator private int _armIdxGenReg; // Next general register to be assigned a value - private int _armIdxStack; // Next stack slot to be assigned a value + private int _armOfsStack; // Offset of next stack location to be assigned a value private ushort _armWFPRegs; // Bitmask of available floating point argument registers (s0-s15/d0-d7) private bool _armRequires64BitAlignment; // Cached info about the current arg private int _arm64IdxGenReg; // Next general register to be assigned a value - private int _arm64IdxStack; // Next stack slot to be assigned a value + private int _arm64OfsStack; // Offset of next stack location to be assigned a value private int _arm64IdxFPReg; // Next FP register to be assigned a value // These are enum flags in CallingConventions.h, but that's really ugly in C#, so I've changed them to bools. 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 667bae1742abf6..a044a42b1d2dd1 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 @@ -39,7 +39,9 @@ public static TransitionBlock FromTarget(TargetDetails target) } case TargetArchitecture.ARM64: - return Arm64TransitionBlock.Instance; + return target.OperatingSystem == TargetOS.OSX ? + AppleArm64TransitionBlock.Instance : + Arm64TransitionBlock.Instance; default: throw new NotImplementedException(target.Architecture.ToString()); @@ -66,12 +68,13 @@ public static TransitionBlock FromTarget(TargetDetails target) public virtual bool IsArmelABI => false; public virtual bool IsArmhfABI => false; + public virtual bool IsAppleArm64ABI => false; public abstract int PointerSize { get; } - public int StackElemSize() => PointerSize; + public abstract int FloatRegisterSize { get; } - public int StackElemSize(int size) => (((size) + StackElemSize() - 1) & -StackElemSize()); + public abstract int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false); public abstract int NumArgumentRegisters { get; } @@ -108,7 +111,7 @@ public static TransitionBlock FromTarget(TargetDetails target) /// /// Default implementation of ThisOffset; X86TransitionBlock provides a slightly different implementation. /// - public virtual int ThisOffset { get { return OffsetOfArgumentRegisters; } } + public virtual int ThisOffset { get { return OffsetOfArgumentRegisters; } } /// /// Recalculate pos in GC ref map to actual offset. This is the default implementation for all architectures @@ -134,6 +137,7 @@ public bool IsStackArgumentOffset(int offset) public bool IsArgumentRegisterOffset(int offset) { + Debug.Assert(!IsX64UnixABI || offset != StructInRegsOffset); int ofsArgRegs = OffsetOfArgumentRegisters; return offset >= ofsArgRegs && offset < (int)(ofsArgRegs + SizeOfArgumentRegisters); @@ -142,13 +146,21 @@ public bool IsArgumentRegisterOffset(int offset) public int GetArgumentIndexFromOffset(int offset) { Debug.Assert(!IsX86); - return ((offset - OffsetOfArgumentRegisters) / PointerSize); + offset -= OffsetOfArgumentRegisters; + Debug.Assert((offset % PointerSize) == 0); + return offset / PointerSize; } public int GetStackArgumentIndexFromOffset(int offset) { Debug.Assert(!IsX86); - return (offset - OffsetOfArgs) / StackElemSize(); + return (offset - OffsetOfArgs) / PointerSize; + } + + public int GetStackArgumentByteIndexFromOffset(int offset) + { + Debug.Assert(!IsX86); + return offset - OffsetOfArgs; } /// @@ -397,6 +409,11 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp } } + public static int ALIGN_UP(int input, int align_to) + { + return (input + (align_to - 1)) & ~(align_to - 1); + } + public const int InvalidOffset = -1; public sealed class X86Constants @@ -412,6 +429,7 @@ private sealed class X86TransitionBlock : TransitionBlock public override TargetArchitecture Architecture => TargetArchitecture.X86; public override int PointerSize => 4; + public override int FloatRegisterSize => throw new NotImplementedException(); public override int NumArgumentRegisters => 2; public override int NumCalleeSavedRegisters => 4; @@ -450,6 +468,12 @@ public override int GetRetBuffArgOffset(bool hasThis) return hasThis ? X86Constants.OffsetOfEdx : X86Constants.OffsetOfEcx; #endif } + + public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) + { + int stackSlotSize = 4; + return ALIGN_UP(parmSize, stackSlotSize); + } } public const int SizeOfM128A = 16; @@ -461,6 +485,7 @@ internal abstract class X64TransitionBlock : TransitionBlock { public override TargetArchitecture Architecture => TargetArchitecture.X64; public override int PointerSize => 8; + public override int FloatRegisterSize => 16; public override bool IsArgPassedByRef(TypeHandle th) { @@ -475,6 +500,11 @@ public override bool IsVarArgPassedByRef(int size) } public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0); + public sealed override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) + { + int stackSlotSize = 8; + return ALIGN_UP(parmSize, stackSlotSize); + } } private sealed class X64WindowsTransitionBlock : X64TransitionBlock @@ -521,6 +551,7 @@ private class Arm32TransitionBlock : TransitionBlock public sealed override TargetArchitecture Architecture => TargetArchitecture.ARM; public sealed override int PointerSize => 4; + public override int FloatRegisterSize => 4; // R0, R1, R2, R3 public sealed override int NumArgumentRegisters => 4; // R4, R5, R6, R7, R8, R9, R10, R11, R14 @@ -538,6 +569,12 @@ private class Arm32TransitionBlock : TransitionBlock public sealed override bool IsArgPassedByRef(TypeHandle th) => false; public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0); + + public sealed override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) + { + int stackSlotSize = 4; + return ALIGN_UP(parmSize, stackSlotSize); + } } private class Arm32ElTransitionBlock : Arm32TransitionBlock @@ -548,12 +585,12 @@ private class Arm32ElTransitionBlock : Arm32TransitionBlock public override bool IsArmelABI => true; } - private sealed class Arm64TransitionBlock : TransitionBlock + private class Arm64TransitionBlock : TransitionBlock { public static TransitionBlock Instance = new Arm64TransitionBlock(); - public override TargetArchitecture Architecture => TargetArchitecture.ARM64; public override int PointerSize => 8; + public override int FloatRegisterSize => 16; // X0 .. X7 public override int NumArgumentRegisters => 8; // X29, X30, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28 @@ -581,6 +618,37 @@ public override bool IsArgPassedByRef(TypeHandle th) public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfX8Register; public override bool IsRetBuffPassedAsFirstArg => false; + + public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) + { + int stackSlotSize = 8; + return ALIGN_UP(parmSize, stackSlotSize); + } + } + + private sealed class AppleArm64TransitionBlock : Arm64TransitionBlock + { + public new static TransitionBlock Instance = new AppleArm64TransitionBlock(); + public override bool IsAppleArm64ABI => true; + + public sealed override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false) + { + if (!isValueType) + { + // The primitive types' sizes are expected to be powers of 2. + Debug.Assert((parmSize & (parmSize - 1)) == 0); + // No padding/alignment for primitive types. + return parmSize; + } + if (isFloatHfa) + { + Debug.Assert((parmSize % 4) == 0); + // float hfa is not considered a struct type and passed with 4-byte alignment. + return parmSize; + } + + return base.StackElemSize(parmSize, isValueType, isFloatHfa); + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileDataManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileDataManager.cs index 0685f3e00c97da..07dfaa54da8ef4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileDataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileDataManager.cs @@ -45,7 +45,11 @@ public ProfileDataManager(Logger logger, { // Parse MIbc Data - string onlyParseItemsDefinedInAssembly = nonLocalGenericsHome == null ? inputModules.First().Assembly.GetName().Name : null; + string onlyParseItemsDefinedInAssembly = null; + if (nonLocalGenericsHome == null && !compilationGroup.IsCompositeBuildMode) + { + onlyParseItemsDefinedInAssembly = inputModules.First().Assembly.GetName().Name; + } HashSet versionBubbleModuleStrings = new HashSet(); foreach (ModuleDesc versionBubbleModule in versionBubble) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs index 19a643476ecd1d..e506bb8b3b6ef8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Numerics; using System.Text; using Internal.TypeSystem; @@ -219,7 +220,7 @@ public static int ModuleNameHashCode(ModuleDesc module) /// Number of bits private static int RotateLeft(int value, int bitCount) { - return unchecked((int)(((uint)value << bitCount) | ((uint)value >> (32 - bitCount)))); + return (int)BitOperations.RotateLeft((uint)value, bitCount); } private static uint XXHash32_MixEmptyState() @@ -231,7 +232,7 @@ private static uint XXHash32_MixEmptyState() private static uint XXHash32_QueueRound(uint hash, uint queuedValue) { - return ((uint)RotateLeft((int)(hash + queuedValue * 3266489917U/*Prime3*/), 17)) * 668265263U/*Prime4*/; + return (BitOperations.RotateLeft((hash + queuedValue * 3266489917U/*Prime3*/), 17)) * 668265263U/*Prime4*/; } private static uint XXHash32_MixFinal(uint hash) 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 e3da2dc734fef5..6a325a5844c816 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1142,6 +1142,10 @@ private CorInfoHelpFunc getNewHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, pHasSideEffects = type.HasFinalizer; + // If the type isn't within the version bubble, it could gain a finalizer. Always treat it as if it has a finalizer + if (!pHasSideEffects && !_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(type)) + pHasSideEffects = true; + return CorInfoHelpFunc.CORINFO_HELP_NEWFAST; } diff --git a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs index 3da9ded70632f7..a2e3a82f37fdb6 100644 --- a/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs +++ b/src/coreclr/tools/dotnet-pgo/CommandLineOptions.cs @@ -45,6 +45,7 @@ internal class CommandLineOptions public bool DumpMibc = false; public FileInfo InputFileToDump; public List CompareMibc; + public bool InheritTimestamp; public string[] HelpArgs = Array.Empty(); @@ -265,6 +266,8 @@ void HelpOption() } } + syntax.DefineOption(name: "inherit-timestamp", value: ref InheritTimestamp, help: "If specified, set the output's timestamp to the max timestamp of the input files"); + VerbosityOption(); CompressedOption(); HelpOption(); diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index 32b42394b6afe9..77113377c478dd 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -378,7 +378,14 @@ static int InnerMergeMain(CommandLineOptions commandLineOptions) ProfileData.MergeProfileData(ref partialNgen, mergedProfileData, MIbcProfileParser.ParseMIbcFile(tsc, peReader, assemblyNamesInBubble, onlyDefinedInAssembly: null)); } - return MibcEmitter.GenerateMibcFile(tsc, commandLineOptions.OutputFileName, mergedProfileData.Values, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed); + int result = MibcEmitter.GenerateMibcFile(tsc, commandLineOptions.OutputFileName, mergedProfileData.Values, commandLineOptions.ValidateOutputFile, commandLineOptions.Uncompressed); + if (result == 0 && commandLineOptions.InheritTimestamp) + { + commandLineOptions.OutputFileName.CreationTimeUtc = commandLineOptions.InputFilesToMerge.Max(fi => fi.CreationTimeUtc); + commandLineOptions.OutputFileName.LastWriteTimeUtc = commandLineOptions.InputFilesToMerge.Max(fi => fi.LastWriteTimeUtc); + } + + return result; } finally { @@ -999,13 +1006,23 @@ static int InnerProcessTraceFileMain(CommandLineOptions commandLineOptions) { foreach (FileInfo fileReference in commandLineOptions.Reference) { - if (!File.Exists(fileReference.FullName)) + try { - PrintError($"Unable to find reference '{fileReference.FullName}'"); - filePathError = true; + if (!File.Exists(fileReference.FullName)) + { + PrintError($"Unable to find reference '{fileReference.FullName}'"); + filePathError = true; + } + else + tsc.GetModuleFromPath(fileReference.FullName); + } + catch (Internal.TypeSystem.TypeSystemException.BadImageFormatException) + { + // Ignore BadImageFormat in order to allow users to use '-r *.dll' + // in a folder with native dynamic libraries (which have the same extension on Windows). + + // We don't need to log a warning here - it's already logged in GetModuleFromPath } - else - tsc.GetModuleFromPath(fileReference.FullName); } } diff --git a/src/coreclr/vm/ClrEtwAll.man b/src/coreclr/vm/ClrEtwAll.man index 1841981ba3d7c5..826c790a6953f9 100644 --- a/src/coreclr/vm/ClrEtwAll.man +++ b/src/coreclr/vm/ClrEtwAll.man @@ -7249,7 +7249,7 @@ diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index 172b800f3feac2..0fd77a277f58b3 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -432,13 +432,15 @@ endif ; _DEBUG NESTED_ENTRY OnHijackTripThread, _TEXT ; Don't fiddle with this unless you change HijackFrame::UpdateRegDisplay - ; and HijackObjectArgs + ; and HijackArgs + mov rdx, rsp push rax ; make room for the real return address (Rip) + push rdx PUSH_CALLEE_SAVED_REGISTERS push_vol_reg rax mov rcx, rsp - alloc_stack 30h ; make extra room for xmm0 + alloc_stack 38h ; make extra room for xmm0, argument home slots and align the SP save_xmm128_postrsp xmm0, 20h @@ -448,9 +450,10 @@ NESTED_ENTRY OnHijackTripThread, _TEXT movdqa xmm0, [rsp + 20h] - add rsp, 30h + add rsp, 38h pop rax POP_CALLEE_SAVED_REGISTERS + pop rdx ret ; return to the correct place, adjusted by our caller NESTED_END OnHijackTripThread, _TEXT diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index d00f7b74df0d48..af91cc3a525f37 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -286,7 +286,11 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD) pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. pRD->pCurrentContext->Rip = m_ReturnAddress; +#ifdef TARGET_WINDOWS + pRD->pCurrentContext->Rsp = m_Args->Rsp; +#else pRD->pCurrentContext->Rsp = PTR_TO_MEMBER_TADDR(HijackArgs, m_Args, Rip) + sizeof(void *); +#endif UpdateRegDisplayFromCalleeSavedRegisters(pRD, &(m_Args->Regs)); diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index 6300876fa330e3..93797a4e84910e 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -465,7 +465,7 @@ struct HijackArgs ULONG64 Rax; ULONG64 ReturnValue[1]; }; -#else // FEATURE_MULTIREG_RETURN +#else // !FEATURE_MULTIREG_RETURN union { struct @@ -475,8 +475,11 @@ struct HijackArgs }; ULONG64 ReturnValue[2]; }; -#endif // TARGET_UNIX +#endif // !FEATURE_MULTIREG_RETURN CalleeSavedRegisters Regs; +#ifdef TARGET_WINDOWS + ULONG64 Rsp; +#endif union { ULONG64 Rip; diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 7ee6baad9a4a63..74a1f9f6be7822 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -7186,6 +7186,52 @@ LONG WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) } } +#ifdef TARGET_AMD64 + +#ifndef STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT +#define STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT ((DWORD)0x80000033L) +#endif + + if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_RETURN_ADDRESS_HIJACK_ATTEMPT) + { + HijackArgs hijackArgs; + hijackArgs.Rax = pExceptionInfo->ContextRecord->Rax; + hijackArgs.Rsp = pExceptionInfo->ContextRecord->Rsp; + + bool areCetShadowStacksEnabled = Thread::AreCetShadowStacksEnabled(); + if (areCetShadowStacksEnabled) + { + // When the CET is enabled, the return address is still on stack, so we need to set the Rsp as + // if it was popped. + hijackArgs.Rsp += 8; + } + hijackArgs.Rip = 0 ; // The OnHijackWorker sets this + #define CALLEE_SAVED_REGISTER(regname) hijackArgs.Regs.regname = pExceptionInfo->ContextRecord->regname; + ENUM_CALLEE_SAVED_REGISTERS(); + #undef CALLEE_SAVED_REGISTER + + OnHijackWorker(&hijackArgs); + + #define CALLEE_SAVED_REGISTER(regname) pExceptionInfo->ContextRecord->regname = hijackArgs.Regs.regname; + ENUM_CALLEE_SAVED_REGISTERS(); + #undef CALLEE_SAVED_REGISTER + pExceptionInfo->ContextRecord->Rax = hijackArgs.Rax; + + if (areCetShadowStacksEnabled) + { + // The context refers to the return instruction + // Set the return address on the stack to the original one + *(size_t *)pExceptionInfo->ContextRecord->Rsp = hijackArgs.ReturnAddress; + } + else + { + // The context refers to the location after the return was processed + pExceptionInfo->ContextRecord->Rip = hijackArgs.ReturnAddress; + } + + return EXCEPTION_CONTINUE_EXECUTION; + } +#endif // We need to unhijack the thread here if it is not unhijacked already. On x86 systems, // we do this in Thread::StackWalkFramesEx, but on amd64 systems we have the OS walk the diff --git a/src/coreclr/vm/interoplibinterface.cpp b/src/coreclr/vm/interoplibinterface.cpp index b027d6b9d4bbdc..b9b02ebb5c6a60 100644 --- a/src/coreclr/vm/interoplibinterface.cpp +++ b/src/coreclr/vm/interoplibinterface.cpp @@ -161,7 +161,10 @@ namespace ~ExternalWrapperResultHolder() { if (Result.Context != NULL) + { + GCX_PREEMP(); InteropLib::Com::DestroyWrapperForExternal(Result.Context); + } } InteropLib::Com::ExternalWrapperResult* operator&() { @@ -285,9 +288,6 @@ namespace // The collection should respect the supplied arguments. // withFlags - If Flag_None, then ignore. Otherwise objects must have these flags. // threadContext - The object must be associated with the supplied thread context. - // - // [TODO] Performance improvement should be made here to provide a custom IEnumerable - // instead of a managed array. OBJECTREF CreateManagedEnumerable(_In_ DWORD withFlags, _In_opt_ void* threadContext) { CONTRACT(OBJECTREF) @@ -300,64 +300,61 @@ namespace } CONTRACT_END; - DWORD objCount; - DWORD objCountMax; - struct { PTRARRAYREF arrRef; - PTRARRAYREF arrRefTmp; } gc; ::ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - { - LockHolder lock(this); - objCountMax = _hashMap.GetCount(); - } + CQuickArrayList localList; - // Allocate the max number of objects needed. - gc.arrRef = (PTRARRAYREF)AllocateObjectArray(objCountMax, g_pObjectClass); - - // Populate the array + // Determine objects to return { LockHolder lock(this); - Iterator curr = _hashMap.Begin(); Iterator end = _hashMap.End(); - - ExternalObjectContext* inst; - for (objCount = 0; curr != end && objCount < objCountMax; objCount++, curr++) + for (Iterator curr = _hashMap.Begin(); curr != end; ++curr) { - inst = *curr; + ExternalObjectContext* inst = *curr; // Only add objects that are in the correct thread // context and have the appropriate flags set. if (inst->ThreadContext == threadContext && (withFlags == ExternalObjectContext::Flags_None || inst->IsSet(withFlags))) { - // Separate the wrapper from the tracker runtime prior to - // passing this onto the caller. This call is okay to make - // even if the instance isn't from the tracker runtime. - InteropLib::Com::SeparateWrapperFromTrackerRuntime(inst); - gc.arrRef->SetAt(objCount, inst->GetObjectRef()); + localList.Push(inst); STRESS_LOG1(LF_INTEROP, LL_INFO100, "Add EOC to Enumerable: 0x%p\n", inst); } } } - // Make the array the correct size - if (objCount < objCountMax) - { - gc.arrRefTmp = (PTRARRAYREF)AllocateObjectArray(objCount, g_pObjectClass); - - SIZE_T elementSize = gc.arrRef->GetComponentSize(); + // Allocate enumerable type to return. + gc.arrRef = (PTRARRAYREF)AllocateObjectArray((DWORD)localList.Size(), g_pObjectClass); - void *src = gc.arrRef->GetDataPtr(); - void *dest = gc.arrRefTmp->GetDataPtr(); + // Insert objects into enumerable. + // The ExternalObjectContexts in the hashmap are only + // removed and associated objects collected during a GC. Since + // this code is running in Cooperative mode they will never + // be null. + for (SIZE_T i = 0; i < localList.Size(); i++) + { + ExternalObjectContext* inst = localList[i]; + gc.arrRef->SetAt(i, inst->GetObjectRef()); + } - _ASSERTE(sizeof(Object*) == elementSize && "Assumption invalidated in memmoveGCRefs() usage"); - memmoveGCRefs(dest, src, objCount * elementSize); - gc.arrRef = gc.arrRefTmp; + { + // Separate the wrapper from the tracker runtime prior to + // passing them onto the caller. This call is okay to make + // even if the instance isn't from the tracker runtime. + // We switch to Preemptive mode since seperating a wrapper + // requires us to call out to non-runtime code which could + // call back into the runtime and/or trigger a GC. + GCX_PREEMP(); + for (SIZE_T i = 0; i < localList.Size(); i++) + { + ExternalObjectContext* inst = localList[i]; + InteropLib::Com::SeparateWrapperFromTrackerRuntime(inst); + } } GCPROTECT_END(); @@ -656,12 +653,15 @@ namespace OBJECTHANDLE instHandle = GetAppDomain()->CreateTypedHandle(gc.instRef, InstanceHandleType); // Call the InteropLib and create the associated managed object wrapper. - hr = InteropLib::Com::CreateWrapperForObject( - instHandle, - vtableCount, - vtables, - flags, - &newWrapper); + { + GCX_PREEMP(); + hr = InteropLib::Com::CreateWrapperForObject( + instHandle, + vtableCount, + vtables, + flags, + &newWrapper); + } if (FAILED(hr)) { DestroyHandleCommon(instHandle, InstanceHandleType); @@ -809,7 +809,6 @@ namespace sizeof(ExternalObjectContext), &resultHolder); } - if (FAILED(hr)) COMPlusThrowHR(hr); @@ -1461,14 +1460,18 @@ void ComWrappersNative::DestroyManagedObjectComWrapper(_In_ void* wrapper) CONTRACTL { NOTHROW; - GC_NOTRIGGER; + GC_TRIGGERS; MODE_ANY; PRECONDITION(wrapper != NULL); } CONTRACTL_END; STRESS_LOG1(LF_INTEROP, LL_INFO100, "Destroying MOW: 0x%p\n", wrapper); - InteropLib::Com::DestroyWrapperForObject(wrapper); + + { + GCX_PREEMP(); + InteropLib::Com::DestroyWrapperForObject(wrapper); + } } void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw) @@ -1476,7 +1479,7 @@ void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw) CONTRACTL { NOTHROW; - GC_NOTRIGGER; + GC_TRIGGERS; MODE_ANY; PRECONDITION(contextRaw != NULL); } @@ -1488,7 +1491,11 @@ void ComWrappersNative::DestroyExternalComObjectContext(_In_ void* contextRaw) #endif STRESS_LOG1(LF_INTEROP, LL_INFO100, "Destroying EOC: 0x%p\n", contextRaw); - InteropLib::Com::DestroyWrapperForExternal(contextRaw); + + { + GCX_PREEMP(); + InteropLib::Com::DestroyWrapperForExternal(contextRaw); + } } void ComWrappersNative::MarkExternalComObjectContextCollected(_In_ void* contextRaw) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 8bdc5f0c2a892b..1007f0b28c6e63 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -7950,7 +7950,8 @@ MethodTable::MethodData::ProcessMap( UINT32 cTypeIDs, MethodTable * pMT, UINT32 iCurrentChainDepth, - MethodDataEntry * rgWorkingData) + MethodDataEntry * rgWorkingData, + size_t cWorkingData) { LIMITED_METHOD_CONTRACT; @@ -7961,10 +7962,8 @@ MethodTable::MethodData::ProcessMap( if (it.Entry()->GetTypeID() == rgTypeIDs[nTypeIDIndex]) { UINT32 curSlot = it.Entry()->GetSlotNumber(); - // If we're processing an interface, or it's for a virtual, or it's for a non-virtual - // for the most derived type, we want to process the entry. In other words, we - // want to ignore non-virtuals for parent classes. - if ((curSlot < pMT->GetNumVirtuals()) || (iCurrentChainDepth == 0)) + // This if check is defensive, and should never fail + if (curSlot < cWorkingData) { MethodDataEntry * pCurEntry = &rgWorkingData[curSlot]; if (!pCurEntry->IsDeclInit() && !pCurEntry->IsImplInit()) @@ -8331,7 +8330,7 @@ MethodTable::MethodDataInterfaceImpl::PopulateNextLevel() if (m_cDeclTypeIDs != 0) { // We got the TypeIDs from TypeLoader, use them - ProcessMap(m_rgDeclTypeIDs, m_cDeclTypeIDs, pMTCur, iChainDepth, GetEntryData()); + ProcessMap(m_rgDeclTypeIDs, m_cDeclTypeIDs, pMTCur, iChainDepth, GetEntryData(), GetNumVirtuals()); } else { // We should decode all interface duplicates of code:m_pDecl @@ -8348,7 +8347,7 @@ MethodTable::MethodDataInterfaceImpl::PopulateNextLevel() INDEBUG(dbg_fInterfaceFound = TRUE); DispatchMapTypeID declTypeID = DispatchMapTypeID::InterfaceClassID(it.GetIndex()); - ProcessMap(&declTypeID, 1, pMTCur, iChainDepth, GetEntryData()); + ProcessMap(&declTypeID, 1, pMTCur, iChainDepth, GetEntryData(), GetNumVirtuals()); } } // The interface code:m_Decl should be found at least once in the interface map of code:m_pImpl, diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index df712a378d4ece..816da7ee0f339b 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -3242,7 +3242,8 @@ public : UINT32 cTypeIDs, MethodTable * pMT, UINT32 cCurrentChainDepth, - MethodDataEntry * rgWorkingData); + MethodDataEntry * rgWorkingData, + size_t cWorkingData); }; // class MethodData typedef ::Holder < MethodData *, MethodData::HolderAcquire, MethodData::HolderRelease > MethodDataHolder; diff --git a/src/coreclr/vm/syncblk.h b/src/coreclr/vm/syncblk.h index 6fb565c1aa4018..3b79753069976e 100644 --- a/src/coreclr/vm/syncblk.h +++ b/src/coreclr/vm/syncblk.h @@ -823,19 +823,24 @@ class InteropSyncBlockInfo if (m_managedObjectComWrapperMap == NULL) return; - CrstHolder lock(&m_managedObjectComWrapperLock); - - if (callback != NULL) + CQuickArrayList localList; { - ManagedObjectComWrapperByIdMap::Iterator iter = m_managedObjectComWrapperMap->Begin(); - while (iter != m_managedObjectComWrapperMap->End()) + CrstHolder lock(&m_managedObjectComWrapperLock); + if (callback != NULL) { - callback(iter->Value()); - ++iter; + ManagedObjectComWrapperByIdMap::Iterator iter = m_managedObjectComWrapperMap->Begin(); + while (iter != m_managedObjectComWrapperMap->End()) + { + localList.Push(iter->Value()); + ++iter; + } } + + m_managedObjectComWrapperMap->RemoveAll(); } - m_managedObjectComWrapperMap->RemoveAll(); + for (SIZE_T i = 0; i < localList.Size(); i++) + callback(localList[i]); } using EnumWrappersCallback = bool(void* mocw, void* cxt); diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 02e4747fceaf10..8359706d948f70 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -28,6 +28,10 @@ CLREvent* ThreadSuspend::g_pGCSuspendEvent = NULL; ThreadSuspend::SUSPEND_REASON ThreadSuspend::m_suspendReason; +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) +void* ThreadSuspend::g_returnAddressHijackTarget = NULL; +#endif + // 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 // code:DacDbiInterfaceImpl.RetrieveHijackedContext. @@ -4678,7 +4682,17 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) CONTRACTL_END; _ASSERTE(IsValidReturnKind(returnKind)); + VOID *pvHijackAddr = reinterpret_cast(OnHijackTripThread); + +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + void* returnAddressHijackTarget = ThreadSuspend::GetReturnAddressHijackTarget(); + if (returnAddressHijackTarget != NULL) + { + pvHijackAddr = returnAddressHijackTarget; + } +#endif // TARGET_WINDOWS && TARGET_AMD64 + #ifdef TARGET_X86 if (returnKind == RT_Float) { @@ -5975,9 +5989,26 @@ bool Thread::InjectActivation(ActivationReason reason) // Initialize thread suspension support void ThreadSuspend::Initialize() { -#if defined(FEATURE_HIJACK) && defined(TARGET_UNIX) +#ifdef FEATURE_HIJACK +#if defined(TARGET_UNIX) ::PAL_SetActivationFunction(HandleSuspensionForInterruptedThread, CheckActivationSafePoint); -#endif +#elif defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + // Only versions of Windows that have the special user mode APC have a correct implementation of the return address hijack handling + if (Thread::UseSpecialUserModeApc()) + { + HMODULE hModNtdll = WszLoadLibrary(W("ntdll.dll")); + if (hModNtdll != NULL) + { + typedef ULONG_PTR (NTAPI *PFN_RtlGetReturnAddressHijackTarget)(); + PFN_RtlGetReturnAddressHijackTarget pfnRtlGetReturnAddressHijackTarget = (PFN_RtlGetReturnAddressHijackTarget)GetProcAddress(hModNtdll, "RtlGetReturnAddressHijackTarget"); + if (pfnRtlGetReturnAddressHijackTarget != NULL) + { + g_returnAddressHijackTarget = (void*)pfnRtlGetReturnAddressHijackTarget(); + } + } + } +#endif // TARGET_WINDOWS && TARGET_AMD64 +#endif // FEATURE_HIJACK } #ifdef _DEBUG diff --git a/src/coreclr/vm/threadsuspend.h b/src/coreclr/vm/threadsuspend.h index f36b39f4f7f7a8..9274327682a6b9 100644 --- a/src/coreclr/vm/threadsuspend.h +++ b/src/coreclr/vm/threadsuspend.h @@ -194,6 +194,10 @@ class ThreadSuspend private: static CLREvent * g_pGCSuspendEvent; +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + static void* g_returnAddressHijackTarget; +#endif // TARGET_WINDOWS && TARGET_AMD64 + // This is true iff we're currently in the process of suspending threads. Once the // threads have been suspended, this is false. This is set via an instance of // SuspendRuntimeInProgressHolder placed in SuspendRuntime, SysStartSuspendForDebug, @@ -247,6 +251,13 @@ class ThreadSuspend return g_pSuspensionThread; } +#if defined(TARGET_WINDOWS) && defined(TARGET_AMD64) + static void* GetReturnAddressHijackTarget() + { + return g_returnAddressHijackTarget; + } +#endif // TARGET_WINDOWS && TARGET_AMD64 + private: static LONG m_DebugWillSyncCount; }; diff --git a/src/installer/pkg/projects/Microsoft.NETCore.DotNetAppHost/Microsoft.NETCore.DotNetAppHost.pkgproj b/src/installer/pkg/projects/Microsoft.NETCore.DotNetAppHost/Microsoft.NETCore.DotNetAppHost.pkgproj index 73768455ceddc5..8f078c7c1bb6e9 100644 --- a/src/installer/pkg/projects/Microsoft.NETCore.DotNetAppHost/Microsoft.NETCore.DotNetAppHost.pkgproj +++ b/src/installer/pkg/projects/Microsoft.NETCore.DotNetAppHost/Microsoft.NETCore.DotNetAppHost.pkgproj @@ -6,13 +6,15 @@ - + + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.targets b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.targets index c570a22277456e..61c12cb71a854d 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.targets +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.targets @@ -8,7 +8,7 @@ - + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Host.sfxproj b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Host.sfxproj index ab948eb5ddbae7..f32e7eb5e853bc 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Host.sfxproj +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Host.sfxproj @@ -2,7 +2,7 @@ - true + true AppHostPack true dotnet-apphost-pack @@ -21,13 +21,15 @@ - + + + diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props index 3fe16b05bacc05..87aaf9d263ced2 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Runtime.props @@ -15,7 +15,7 @@ The .NET Shared Framework - + Mono 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 c69491f5044a0b..90f8d4ad2a28f0 100644 --- a/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj +++ b/src/installer/pkg/sfx/bundle/Microsoft.NETCore.App.Bundle.bundleproj @@ -5,7 +5,7 @@ Name, used to generate the bundle upgrade code. Must stay the same to allow bundles in a given product band to upgrade in place. --> - true + true false .NET Core Shared Framework Bundle Installer $(MSBuildProjectDirectory) diff --git a/src/installer/pkg/sfx/installers/dotnet-host.proj b/src/installer/pkg/sfx/installers/dotnet-host.proj index 0963a7b89d8cca..953b0f4f8522f9 100644 --- a/src/installer/pkg/sfx/installers/dotnet-host.proj +++ b/src/installer/pkg/sfx/installers/dotnet-host.proj @@ -1,6 +1,6 @@ - true + true true dotnet-host dotnet-host-internal diff --git a/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj b/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj index 0edd1eaa9853d2..2c5fa5d8188867 100644 --- a/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj +++ b/src/installer/pkg/sfx/installers/dotnet-hostfxr.proj @@ -1,6 +1,6 @@ - true + true true dotnet-hostfxr-internal dotnet-hostfxr diff --git a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs index 3206cd74d15593..120b899cff451b 100644 --- a/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs +++ b/src/installer/tests/HostActivation.Tests/MultiArchInstallLocation.cs @@ -205,6 +205,38 @@ public void InstallLocationFile_ReallyLongInstallPathIsParsedCorrectly() } } + [Fact] + [SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")] + public void InstallLocationFile_MissingFile() + { + var fixture = sharedTestState.PortableAppFixture.Copy(); + + var appExe = fixture.TestProject.AppExe; + string testArtifactsPath = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "missingInstallLocation")); + using (new TestArtifact(testArtifactsPath)) + using (var testOnlyProductBehavior = TestOnlyProductBehavior.Enable(appExe)) + { + Directory.CreateDirectory(testArtifactsPath); + + string directory = Path.Combine(testArtifactsPath, "installLocationOverride"); + Directory.CreateDirectory(directory); + string nonExistentLocationFile = Path.Combine(directory, "install_location"); + string defaultInstallLocation = Path.Combine(testArtifactsPath, "defaultInstallLocation"); + + Command.Create(appExe) + .CaptureStdErr() + .EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.InstallLocationFilePath, + nonExistentLocationFile) + .EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + defaultInstallLocation) + .DotNetRoot(null) + .Execute() + .Should().NotHaveStdErrContaining("The install_location file"); + } + } + public class SharedTestState : IDisposable { public string BaseDirectory { get; } diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index fa3e03bd972016..1abb3ad75b9697 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -340,6 +340,37 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere } } + [Fact] + public void RuntimeConfig_FilePath_Breaks_MAX_PATH_Threshold() + { + var project = sharedTestState.PortableAppFixture_Published + .Copy(); + + var appExeName = Path.GetFileName(project.TestProject.AppExe); + var outputDir = project.TestProject.OutputDirectory; + + // Move the portable app to a path such that the length of the executable's fullpath + // is just 1 char behind MAX_PATH (260) so that the runtimeconfig(.dev).json files + // break this threshold. This will cause hostfxr to normalize these paths -- here we + // are checking that the updated paths are used. + var tmp = Path.GetTempPath(); + var dirName = new string('a', 259 - tmp.Length - appExeName.Length - 1); + var newDir = Path.Combine(tmp, dirName); + var appExe = Path.Combine(newDir, appExeName); + Debug.Assert(appExe.Length == 259); + Directory.CreateDirectory(newDir); + foreach (var file in Directory.GetFiles(outputDir, "*.*", SearchOption.TopDirectoryOnly)) + File.Copy(file, Path.Combine(newDir, Path.GetFileName(file)), true); + + Command.Create(appExe) + .DotNetRoot(project.BuiltDotnet.BinPath) + .EnableTracingAndCaptureOutputs() + .MultilevelLookup(false) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + [Fact] public void ComputedTPADoesntEndWithPathSeparator() { @@ -707,7 +738,7 @@ public SharedTestState() PortableAppFixture_Published = new TestProjectFixture("PortableApp", RepoDirectories) .EnsureRestored() - .PublishProject(); + .PublishProject(extraArgs: "/p:UseAppHost=true"); MockApp = new TestApp(SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "portableAppActivation")), "App"); Directory.CreateDirectory(MockApp.Location); 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 1f2275ebb15ff3..fb6e690b01ddc6 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 @@ -317,7 +317,7 @@ private static void EncodeValueStringPart(string s, Span buffer) if (ch > 127) { - throw new QPackEncodingException("ASCII header value."); + throw new QPackEncodingException(SR.net_http_request_invalid_char_encoding); } buffer[i] = (byte)ch; @@ -402,4 +402,4 @@ private static bool EncodeHeaderBlockPrefix(Span destination, out int byte return true; } } -} \ No newline at end of file +} diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/SR.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/SR.cs index 2e67f3463b02d9..3f1f91e905554c 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/SR.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/SR.cs @@ -1,5 +1,5 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// 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; diff --git a/src/libraries/Common/src/System/Net/Security/CertificateValidation.Unix.cs b/src/libraries/Common/src/System/Net/Security/CertificateValidation.Unix.cs index e03cd4088d5b49..57bc4b9e23220e 100644 --- a/src/libraries/Common/src/System/Net/Security/CertificateValidation.Unix.cs +++ b/src/libraries/Common/src/System/Net/Security/CertificateValidation.Unix.cs @@ -12,7 +12,7 @@ internal static class CertificateValidation { private static readonly IdnMapping s_idnMapping = new IdnMapping(); - internal static SslPolicyErrors BuildChainAndVerifyProperties(X509Chain chain, X509Certificate2 remoteCertificate, bool checkCertName, string? hostName) + internal static SslPolicyErrors BuildChainAndVerifyProperties(X509Chain chain, X509Certificate2 remoteCertificate, bool checkCertName, bool isServer, string? hostName) { SslPolicyErrors errors = chain.Build(remoteCertificate) ? SslPolicyErrors.None : diff --git a/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs b/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs new file mode 100644 index 00000000000000..41d127639484b8 --- /dev/null +++ b/src/libraries/Common/src/System/Net/Security/CertificateValidation.Windows.cs @@ -0,0 +1,90 @@ +// 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.Net.Security; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; + +namespace System.Net +{ + internal static partial class CertificateValidation + { + internal static SslPolicyErrors BuildChainAndVerifyProperties(X509Chain chain, X509Certificate2 remoteCertificate, bool checkCertName, bool isServer, string? hostName) + { + SslPolicyErrors sslPolicyErrors = SslPolicyErrors.None; + + bool chainBuildResult = chain.Build(remoteCertificate); + if (!chainBuildResult // Build failed on handle or on policy. + && chain.SafeHandle!.DangerousGetHandle() == IntPtr.Zero) // Build failed to generate a valid handle. + { + throw new CryptographicException(Marshal.GetLastPInvokeError()); + } + + if (checkCertName) + { + unsafe + { + uint status = 0; + + var eppStruct = new Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA() + { + cbSize = (uint)sizeof(Interop.Crypt32.SSL_EXTRA_CERT_CHAIN_POLICY_PARA), + // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). + dwAuthType = isServer ? Interop.Crypt32.AuthType.AUTHTYPE_CLIENT : Interop.Crypt32.AuthType.AUTHTYPE_SERVER, + fdwChecks = 0, + pwszServerName = null + }; + + var cppStruct = new Interop.Crypt32.CERT_CHAIN_POLICY_PARA() + { + cbSize = (uint)sizeof(Interop.Crypt32.CERT_CHAIN_POLICY_PARA), + dwFlags = 0, + pvExtraPolicyPara = &eppStruct + }; + + fixed (char* namePtr = hostName) + { + eppStruct.pwszServerName = namePtr; + cppStruct.dwFlags |= + (Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_ALL & + ~Interop.Crypt32.CertChainPolicyIgnoreFlags.CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG); + + SafeX509ChainHandle chainContext = chain.SafeHandle!; + status = Verify(chainContext, ref cppStruct); + if (status == Interop.Crypt32.CertChainPolicyErrors.CERT_E_CN_NO_MATCH) + { + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNameMismatch; + } + } + } + } + + if (!chainBuildResult) + { + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; + } + + return sslPolicyErrors; + } + + private static unsafe uint Verify(SafeX509ChainHandle chainContext, ref Interop.Crypt32.CERT_CHAIN_POLICY_PARA cpp) + { + Interop.Crypt32.CERT_CHAIN_POLICY_STATUS status = default; + status.cbSize = (uint)sizeof(Interop.Crypt32.CERT_CHAIN_POLICY_STATUS); + + bool errorCode = + Interop.Crypt32.CertVerifyCertificateChainPolicy( + (IntPtr)Interop.Crypt32.CertChainPolicy.CERT_CHAIN_POLICY_SSL, + chainContext, + ref cpp, + ref status); + + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(chainContext, $"CertVerifyCertificateChainPolicy returned: {errorCode}. Status: {status.dwError}"); + return status.dwError; + } + } +} diff --git a/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs b/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs index 80e7bc26ae36da..8694a785ad3a58 100644 --- a/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs +++ b/src/libraries/Common/src/System/Text/Json/PooledByteBufferWriter.cs @@ -86,8 +86,9 @@ public void Dispose() } ClearHelper(); - ArrayPool.Shared.Return(_rentedBuffer); + byte[] toReturn = _rentedBuffer; _rentedBuffer = null!; + ArrayPool.Shared.Return(toReturn); } public void Advance(int count) diff --git a/src/libraries/Common/tests/StreamConformanceTests/StreamConformanceTests.csproj b/src/libraries/Common/tests/StreamConformanceTests/StreamConformanceTests.csproj index b5dfa854512bfc..6959f41279b178 100644 --- a/src/libraries/Common/tests/StreamConformanceTests/StreamConformanceTests.csproj +++ b/src/libraries/Common/tests/StreamConformanceTests/StreamConformanceTests.csproj @@ -4,7 +4,7 @@ $(NetCoreAppCurrent) - + diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs index 555a54895e7e6f..ca53bbe2068257 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackConnection.cs @@ -189,13 +189,36 @@ public override async Task HandleRequestAsync(HttpStatusCode st await stream.SendResponseAsync(statusCode, headers, content).ConfigureAwait(false); - // closing the connection here causes bytes written to streams to go missing. - // Regardless, we told the client we are closing so it shouldn't matter -- they should not use this connection anymore. - //await CloseAsync(H3_NO_ERROR).ConfigureAwait(false); + await WaitForClientDisconnectAsync(); return request; } + // Wait for the client to close the connection, e.g. after we send a GOAWAY, or after the HttpClient is disposed. + public async Task WaitForClientDisconnectAsync() + { + while (true) + { + Http3LoopbackStream stream; + + try + { + stream = await AcceptRequestStreamAsync().ConfigureAwait(false); + } + catch (QuicConnectionAbortedException abortException) when (abortException.ErrorCode == H3_NO_ERROR) + { + break; + } + + using (stream) + { + await stream.AbortAndWaitForShutdownAsync(H3_REQUEST_REJECTED); + } + } + + await CloseAsync(H3_NO_ERROR); + } + public override async Task WaitForCancellationAsync(bool ignoreIncomingData = true, int requestId = 0) { await GetOpenRequest(requestId).WaitForCancellationAsync(ignoreIncomingData).ConfigureAwait(false); diff --git a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs index b93807a0321466..a3d2c9a4215c2b 100644 --- a/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs +++ b/src/libraries/Common/tests/System/Net/Http/Http3LoopbackStream.cs @@ -359,6 +359,13 @@ public async Task WaitForCancellationAsync(bool ignoreIncomingData = true) } } + public async Task AbortAndWaitForShutdownAsync(long errorCode) + { + _stream.AbortRead(errorCode); + _stream.AbortWrite(errorCode); + await _stream.ShutdownCompleted(); + } + public async Task<(long? frameType, byte[] payload)> ReadFrameAsync() { long? frameType = await ReadIntegerAsync().ConfigureAwait(false); diff --git a/src/libraries/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs b/src/libraries/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs index 2efde1fef9ad58..2efa7af0d0e188 100644 --- a/src/libraries/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs +++ b/src/libraries/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs @@ -12,6 +12,9 @@ public static class TaskTimeoutExtensions #region WaitAsync polyfills // Test polyfills when targeting a platform that doesn't have these ConfigureAwait overloads on Task + public static Task WaitAsync(this Task task, int millisecondsTimeout) => + WaitAsync(task, TimeSpan.FromMilliseconds(millisecondsTimeout), default); + public static Task WaitAsync(this Task task, TimeSpan timeout) => WaitAsync(task, timeout, default); @@ -28,6 +31,9 @@ public async static Task WaitAsync(this Task task, TimeSpan timeout, Cancellatio } } + public static Task WaitAsync(this Task task, int millisecondsTimeout) => + WaitAsync(task, TimeSpan.FromMilliseconds(millisecondsTimeout), default); + public static Task WaitAsync(this Task task, TimeSpan timeout) => WaitAsync(task, timeout, default); @@ -48,6 +54,9 @@ public static async Task WaitAsync(this Task task, Ti public static async Task WhenAllOrAnyFailed(this Task[] tasks, int millisecondsTimeout) => await tasks.WhenAllOrAnyFailed().WaitAsync(TimeSpan.FromMilliseconds(millisecondsTimeout)); + public static async Task WhenAllOrAnyFailed(Task t1, Task t2, int millisecondsTimeout) => + await new Task[] {t1, t2}.WhenAllOrAnyFailed(millisecondsTimeout); + public static async Task WhenAllOrAnyFailed(this Task[] tasks) { try diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs index 096616937019eb..cff5e2284cfb36 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs @@ -20,6 +20,7 @@ public static partial class PlatformDetection public static bool IsDebian => IsDistroAndVersion("debian"); public static bool IsAlpine => IsDistroAndVersion("alpine"); public static bool IsDebian8 => IsDistroAndVersion("debian", 8); + public static bool IsDebian9 => IsDistroAndVersion("debian", 9); public static bool IsDebian10 => IsDistroAndVersion("debian", 10); public static bool IsUbuntu1604 => IsDistroAndVersion("ubuntu", 16, 4); public static bool IsUbuntu1704 => IsDistroAndVersion("ubuntu", 17, 4); diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 87dda78fb5bc1e..5e28db1deeb28d 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -59,10 +59,6 @@ '$(DisableImplicitFrameworkReferences)' != 'true' and '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and ('$(IsReferenceAssembly)' == 'true' or '$(IsSourceProject)' == 'true')">true - - - true diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj index f85823f03e54cd..75ea735a625eae 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/ref/Microsoft.Extensions.DependencyInjection.Abstractions.csproj @@ -11,6 +11,6 @@ - + diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/DependencyInjectionSpecificationTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/DependencyInjectionSpecificationTests.cs index 88235f9e50d755..8a4d73684b4188 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/DependencyInjectionSpecificationTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src/DependencyInjectionSpecificationTests.cs @@ -434,6 +434,14 @@ public void ScopedServices_FromCachedScopeFactory_CanBeResolvedAndDisposed() } } + [Fact] + public void ServiceProviderIsDisposable() + { + var provider = CreateServiceProvider(new TestServiceCollection()); + + Assert.IsAssignableFrom(provider); + } + [Fact] public void DisposingScopeDisposesService() { @@ -469,13 +477,10 @@ public void DisposingScopeDisposesService() Assert.True(transient2.Disposed); Assert.False(singleton.Disposed); - var disposableProvider = provider as IDisposable; - if (disposableProvider != null) - { - disposableProvider.Dispose(); - Assert.True(singleton.Disposed); - Assert.True(transient3.Disposed); - } + (provider as IDisposable).Dispose(); + + Assert.True(singleton.Disposed); + Assert.True(transient3.Disposed); } [Fact] @@ -490,7 +495,7 @@ public void SelfResolveThenDispose() // Assert Assert.NotNull(serviceProvider); - (provider as IDisposable)?.Dispose(); + (provider as IDisposable).Dispose(); } [Fact] @@ -790,7 +795,7 @@ public void DisposesInReverseOrderOfCreation() var multipleServices = outer.MultipleServices.ToArray(); // Act - ((IDisposable)serviceProvider).Dispose(); + (serviceProvider as IDisposable).Dispose(); // Assert Assert.Equal(outer, callback.Disposed[0]); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StructureMap.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StructureMap.cs index f73bf039220e8f..59a6e00160ec56 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StructureMap.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StructureMap.cs @@ -12,6 +12,9 @@ public class StructureMapDependencyInjectionSpecificationTests: SkippableDepende public override string[] SkippedTests => new[] { + "ServiceProviderIsDisposable", + "SelfResolveThenDispose", + "DisposingScopeDisposesService", "DisposesInReverseOrderOfCreation", "ResolvesMixedOpenClosedGenericsAsEnumerable" }; diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index b4e37df4f11636..96bf27b7b5acab 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1515,6 +1515,7 @@ public void UsePollingFileWatcher_FileWatcherNotNull_ReturnsFalse() [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) { // Arrange @@ -1541,6 +1542,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWild [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) { // Arrange diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index d8b332b6409f56..7ccfafb28180cd 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -14,6 +14,7 @@ public partial class PhysicalFileProviderTests [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) { // Arrange @@ -44,6 +45,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard) { // Arrange @@ -69,6 +71,7 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe [InlineData(false, true)] [InlineData(true, false)] [InlineData(true, true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool linkWasBroken) { // Arrange @@ -108,6 +111,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)] public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) { // Arrange diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs index 1f35e1c15f4802..abe332ab9b9dd8 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostBuilderTests.cs @@ -310,7 +310,6 @@ public void HostConfigParametersReadCorrectly() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void RelativeContentRootIsResolved() { using (var host = new HostBuilder() diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs index 345fcbb7866507..7fe9ceca422bb2 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/HostTests.cs @@ -21,7 +21,6 @@ namespace Microsoft.Extensions.Hosting.Tests public partial class HostTests { [Fact] - [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.MacCatalyst | TestPlatforms.tvOS, "Not supported on iOS, MacCatalyst, or tvOS.")] public async Task StopAsyncWithCancellation() { var builder = new HostBuilder(); diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs index 989ea8c1caf46f..e98aa45666ad81 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/Internal/HostTests.cs @@ -19,7 +19,6 @@ namespace Microsoft.Extensions.Hosting.Internal { - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public class HostTests { [Fact] @@ -678,7 +677,7 @@ public async Task HostPropagatesExceptionsThrownWithBackgroundServiceExceptionBe [Fact] public async Task HostStopsApplicationWithOneBackgroundServiceErrorAndOthersWithoutError() - { + { var wasOtherServiceStarted = false; TaskCompletionSource throwingTcs = new(); diff --git a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs index 53153ac7f2909d..793bedf2003db9 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/OptionsBuilderExtensionsTests.cs @@ -26,7 +26,6 @@ public void ValidateOnStart_NullOptionsBuilder_Throws() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_ConfigureAndValidateThenCallValidateOnStart_ValidatesFailure() { var hostBuilder = CreateHostBuilder(services => @@ -50,7 +49,6 @@ public async Task ValidateOnStart_ConfigureAndValidateThenCallValidateOnStart_Va [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_CallFirstThenConfigureAndValidate_ValidatesFailure() { var hostBuilder = CreateHostBuilder(services => @@ -74,7 +72,6 @@ public async Task ValidateOnStart_CallFirstThenConfigureAndValidate_ValidatesFai [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_ErrorMessageSpecified_FailsWithCustomError() { var hostBuilder = CreateHostBuilder(services => @@ -105,7 +102,6 @@ internal class FakeSettings [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_NamedOptions_ValidatesFailureOnStart() { var hostBuilder = CreateHostBuilder(services => @@ -134,7 +130,6 @@ public async Task ValidateOnStart_NamedOptions_ValidatesFailureOnStart() [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] private async Task ValidateOnStart_AddNamedOptionsMultipleTimesForSameType_BothGetTriggered() { bool firstOptionsBuilderTriggered = false; @@ -181,7 +176,6 @@ private async Task ValidateOnStart_AddNamedOptionsMultipleTimesForSameType_BothG [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] private async Task ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStartsWithNoFailure() { bool validateCalled = false; @@ -193,7 +187,7 @@ private async Task ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStar .Configure(o => o.Boolean = true) .Validate(o => { - validateCalled = true; + validateCalled = true; return o.Boolean; }, "correct_configuration") .ValidateOnStart(); @@ -209,7 +203,6 @@ private async Task ValidateOnStart_AddEagerValidation_DoesValidationWhenHostStar [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] private async Task ValidateOnStart_AddLazyValidation_SkipsValidationWhenHostStarts() { bool validateCalled = false; @@ -227,7 +220,7 @@ private async Task ValidateOnStart_AddLazyValidation_SkipsValidationWhenHostStar .Configure(o => o.Boolean = false) .Validate(o => { - validateCalled = true; + validateCalled = true; return o.Boolean; }, "bad_configuration"); }); @@ -243,7 +236,6 @@ private async Task ValidateOnStart_AddLazyValidation_SkipsValidationWhenHostStar [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_AddBothLazyAndEagerValidationOnDifferentTypes_ValidatesWhenHostStartsOnlyForEagerValidations() { bool validateCalledForNested = false; @@ -254,7 +246,7 @@ public async Task ValidateOnStart_AddBothLazyAndEagerValidationOnDifferentTypes_ // Lazy validation for NestedOptions services.AddOptions() .Configure(o => o.Integer = 11) - .Validate(o => + .Validate(o => { validateCalledForNested = true; return o.Integer > 12; @@ -263,7 +255,7 @@ public async Task ValidateOnStart_AddBothLazyAndEagerValidationOnDifferentTypes_ // Eager validation for ComplexOptions services.AddOptions() .Configure(o => o.Boolean = false) - .Validate(o => + .Validate(o => { validateCalledForComplexOptions = true; return o.Boolean; @@ -287,7 +279,6 @@ public async Task ValidateOnStart_AddBothLazyAndEagerValidationOnDifferentTypes_ [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_MultipleErrorsInOneValidationCall_ValidatesFailureWithMultipleErrors() { var hostBuilder = CreateHostBuilder(services => @@ -316,7 +307,6 @@ public async Task ValidateOnStart_MultipleErrorsInOneValidationCall_ValidatesFai [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/34582", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/52114", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public async Task ValidateOnStart_MultipleErrorsInOneValidationCallUsingCustomErrors_FailuresContainCustomErrors() { var hostBuilder = CreateHostBuilder(services => 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 index d39b20d6d9f5e2..a2a273ed6e042f 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/tests/Microsoft.NETCore.Platforms.Tests.csproj @@ -8,6 +8,8 @@ + + diff --git a/src/libraries/System.ComponentModel.EventBasedAsync/tests/AsyncOperationFinalizerTests.cs b/src/libraries/System.ComponentModel.EventBasedAsync/tests/AsyncOperationFinalizerTests.cs index 06707dcbec13b1..e7a42d50bdc20f 100644 --- a/src/libraries/System.ComponentModel.EventBasedAsync/tests/AsyncOperationFinalizerTests.cs +++ b/src/libraries/System.ComponentModel.EventBasedAsync/tests/AsyncOperationFinalizerTests.cs @@ -34,7 +34,9 @@ private void Completed() Assert.True(tracker.OperationDidComplete); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + private static bool IsPreciseGcSupportedAndRemoteExecutorSupported => PlatformDetection.IsPreciseGcSupported && RemoteExecutor.IsSupported; + + [ConditionalFact(nameof(IsPreciseGcSupportedAndRemoteExecutorSupported))] public void Finalizer_OperationNotCompleted_CompletesOperation() { RemoteExecutor.Invoke(() => diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ILLink.Descriptors.xml b/src/libraries/System.ComponentModel.TypeConverter/tests/ILLink.Descriptors.xml index 2c51716e8b55b2..50dadf97ed098b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/ILLink.Descriptors.xml +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ILLink.Descriptors.xml @@ -8,7 +8,6 @@ - diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs index ce68dcf98f8155..cc846b56efb4d8 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricEventSourceTests.cs @@ -376,7 +376,7 @@ public void EventSourcePublishesMissingDataPoints() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] [OuterLoop("Slow and has lots of console spew")] - public void EventSourcePublishesEndEventsOnNewListener() + public void EventSourceRejectsNewListener() { using Meter meter = new Meter("TestMeter7"); Counter c = meter.CreateCounter("counter1", "hat", "Fooz!!"); @@ -395,11 +395,13 @@ public void EventSourcePublishesEndEventsOnNewListener() listener.WaitForCollectionStop(s_waitForEventTimeout, 2); c.Add(12); h.Record(26); - listener.WaitForCollectionStop(s_waitForEventTimeout, 3); - // some alternate listener starts listening + // some alternate listener attempts to listen in the middle using MetricsEventListener listener2 = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "ADifferentMeter"); - listener.WaitForEndInstrumentReporting(s_waitForEventTimeout, 4); + listener2.WaitForMultipleSessionsNotSupportedError(s_waitForEventTimeout); + + + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); events = listener.Events.ToArray(); } @@ -409,7 +411,6 @@ public void EventSourcePublishesEndEventsOnNewListener() AssertGaugeEventsPresent(events, meter.Name, og.Name, "", og.Unit, "9", "18", "27"); AssertHistogramEventsPresent(events, meter.Name, h.Name, "", h.Unit, "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); - AssertEndInstrumentReportingEventsPresent(events, c, oc, og, h); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] @@ -646,6 +647,63 @@ public void EventSourceHandlesObservableCallbackException() AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))] + [OuterLoop("Slow and has lots of console spew")] + public void EventSourceWorksWithSequentialListeners() + { + using Meter meter = new Meter("TestMeter16"); + Counter c = meter.CreateCounter("counter1"); + int counterState = 3; + ObservableCounter oc = meter.CreateObservableCounter("observableCounter1", () => { counterState += 7; return counterState; }); + int gaugeState = 0; + ObservableGauge og = meter.CreateObservableGauge("observableGauge1", () => { gaugeState += 9; return gaugeState; }); + Histogram h = meter.CreateHistogram("histogram1"); + + EventWrittenEventArgs[] events; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter16")) + { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "9", "18"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); + + // Now create a new listener and do everything a 2nd time. Because the listener above has been disposed the source should be + // free to accept a new connection. + events = null; + using (MetricsEventListener listener = new MetricsEventListener(_output, MetricsEventListener.TimeSeriesValues, IntervalSecs, "TestMeter16")) + { + listener.WaitForCollectionStop(s_waitForEventTimeout, 1); + c.Add(5); + h.Record(19); + listener.WaitForCollectionStop(s_waitForEventTimeout, 2); + c.Add(12); + h.Record(26); + listener.WaitForCollectionStop(s_waitForEventTimeout, 3); + events = listener.Events.ToArray(); + } + + AssertBeginInstrumentReportingEventsPresent(events, c, oc, og, h); + AssertInitialEnumerationCompleteEventPresent(events); + AssertCounterEventsPresent(events, meter.Name, c.Name, "", "", "5", "12"); + AssertCounterEventsPresent(events, meter.Name, oc.Name, "", "", "", "7"); + AssertGaugeEventsPresent(events, meter.Name, og.Name, "", "", "36", "45"); + AssertHistogramEventsPresent(events, meter.Name, h.Name, "", "", "0.5=19;0.95=19;0.99=19", "0.5=26;0.95=26;0.99=26"); + AssertCollectStartStopEventsPresent(events, IntervalSecs, 3); + } + private void AssertBeginInstrumentReportingEventsPresent(EventWrittenEventArgs[] events, params Instrument[] expectedInstruments) { var beginReportEvents = events.Where(e => e.EventName == "BeginInstrumentReporting").Select(e => @@ -961,10 +1019,20 @@ protected override void OnEventSourceCreated(EventSource eventSource) } } + public override void Dispose() + { + if (_source != null) + { + // workaround for https://github.com/dotnet/runtime/issues/56378 + DisableEvents(_source); + } + base.Dispose(); + } + protected override void OnEventWritten(EventWrittenEventArgs eventData) { string sessionId = eventData.Payload[0].ToString(); - if(sessionId != "" && sessionId != _sessionId) + if (eventData.EventName != "MultipleSessionsNotSupportedError" && sessionId != "" && sessionId != _sessionId) { return; } @@ -988,68 +1056,36 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) _autoResetEvent.Set(); } - public void WaitForCollectionStop(TimeSpan timeout, int numStops) - { - DateTime startTime = DateTime.Now; - DateTime stopTime = startTime + timeout; - int initialStopCount = GetCountCollectionStops(); - while (true) - { - if (GetCountCollectionStops() >= numStops) - { - return; - } - TimeSpan remainingTime = stopTime - DateTime.Now; - if (remainingTime.TotalMilliseconds < 0 || !_autoResetEvent.WaitOne(remainingTime)) - { - int currentStopCount = GetCountCollectionStops(); - throw new TimeoutException("Timed out waiting for a StopCollection event. " + - $"StartTime={startTime} stopTime={stopTime} initialStopCount={initialStopCount} currentStopCount={currentStopCount} targetStopCount={numStops}"); - } - } - } + public void WaitForCollectionStop(TimeSpan timeout, int numEvents) => WaitForEvent(timeout, numEvents, "CollectionStop"); + + public void WaitForEndInstrumentReporting(TimeSpan timeout, int numEvents) => WaitForEvent(timeout, numEvents, "EndInstrumentReporting"); + + public void WaitForEnumerationComplete(TimeSpan timeout) => WaitForEvent(timeout, 1, "InitialInstrumentEnumerationComplete"); - public void WaitForEndInstrumentReporting(TimeSpan timeout, int numEvents) + public void WaitForMultipleSessionsNotSupportedError(TimeSpan timeout) => WaitForEvent(timeout, 1, "MultipleSessionsNotSupportedError"); + + void WaitForEvent(TimeSpan timeout, int numEvents, string eventName) { DateTime startTime = DateTime.Now; DateTime stopTime = startTime + timeout; - int initialEventCount = GetCountEndInstrumentReporting(); + int initialEventCount = GetCountEvents(eventName); while (true) { - if (GetCountEndInstrumentReporting() >= numEvents) + if (GetCountEvents(eventName) >= numEvents) { return; } TimeSpan remainingTime = stopTime - DateTime.Now; if (remainingTime.TotalMilliseconds < 0 || !_autoResetEvent.WaitOne(remainingTime)) { - int currentEventCount = GetCountEndInstrumentReporting(); - throw new TimeoutException("Timed out waiting for a EndInstrumentReporting event. " + + int currentEventCount = GetCountEvents(eventName); + throw new TimeoutException($"Timed out waiting for a {eventName} event. " + $"StartTime={startTime} stopTime={stopTime} initialEventCount={initialEventCount} currentEventCount={currentEventCount} targetEventCount={numEvents}"); } } } - public void WaitForEnumerationComplete(TimeSpan timeout) - { - DateTime startTime = DateTime.Now; - DateTime stopTime = startTime + timeout; - int initialEventCount = GetCountEnumerationComplete(); - while (true) - { - if (GetCountEnumerationComplete() >= 1) - { - return; - } - TimeSpan remainingTime = stopTime - DateTime.Now; - if (remainingTime.TotalMilliseconds < 0 || !_autoResetEvent.WaitOne(remainingTime)) - { - int currentEventCount = GetCountEnumerationComplete(); - throw new TimeoutException("Timed out waiting for a EndInstrumentReporting event. " + - $"StartTime={startTime} stopTime={stopTime} initialEventCount={initialEventCount} currentEventCount={currentEventCount}"); - } - } - } + private void AssertOnError() { @@ -1064,29 +1100,12 @@ private void AssertOnError() } } - private int GetCountCollectionStops() + private int GetCountEvents(string eventName) { lock (this) { AssertOnError(); - return Events.Where(e => e.EventName == "CollectionStop").Count(); - } - } - - private int GetCountEndInstrumentReporting() - { - lock (this) - { - AssertOnError(); - return Events.Where(e => e.EventName == "EndInstrumentReporting").Count(); - } - } - - private int GetCountEnumerationComplete() - { - lock (this) - { - return Events.Where(e => e.EventName == "InitialInstrumentEnumerationComplete").Count(); + return Events.Where(e => e.EventName == eventName).Count(); } } } diff --git a/src/libraries/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.csproj b/src/libraries/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.csproj index 2631550443e3a7..ca4c77c50b5d52 100644 --- a/src/libraries/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.csproj +++ b/src/libraries/System.Diagnostics.EventLog/ref/System.Diagnostics.EventLog.csproj @@ -10,7 +10,7 @@ - + @@ -19,4 +19,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj index abc273f110fa35..1bf0c43270e42e 100644 --- a/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj +++ b/src/libraries/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj @@ -12,7 +12,7 @@ true - + diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs index 223972940e800c..38062773cb3671 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessStartInfoTests.cs @@ -30,10 +30,13 @@ private static bool IsAdmin_IsNotNano_RemoteExecutorIsSupported => PlatformDetection.IsWindowsAndElevated && PlatformDetection.IsNotWindowsNanoServer && RemoteExecutor.IsSupported; [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51386", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void TestEnvironmentProperty() { - Assert.NotEqual(0, new Process().StartInfo.Environment.Count); + // Whole list of environment variables can no longer be accessed on non-OSX apple platforms + if (!PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst) + { + Assert.NotEqual(0, new Process().StartInfo.Environment.Count); + } ProcessStartInfo psi = new ProcessStartInfo(); @@ -41,7 +44,11 @@ public void TestEnvironmentProperty() // with current environmental variables. IDictionary environment = psi.Environment; - Assert.NotEqual(0, environment.Count); + // Whole list of environment variables can no longer be accessed on non-OSX apple platforms + if (!PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst) + { + Assert.NotEqual(0, environment.Count); + } int countItems = environment.Count; @@ -772,7 +779,6 @@ public void Verbs_GetUnix_ReturnsEmpty() [PlatformSpecific(TestPlatforms.AnyUnix)] // Test case is specific to Unix [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/51386", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void TestEnvironmentVariablesPropertyUnix() { ProcessStartInfo psi = new ProcessStartInfo(); @@ -782,7 +788,11 @@ public void TestEnvironmentVariablesPropertyUnix() StringDictionary environmentVariables = psi.EnvironmentVariables; - Assert.NotEqual(0, environmentVariables.Count); + // Whole list of environment variables can no longer be accessed on non-OSX apple platforms + if (!PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst) + { + Assert.NotEqual(0, environmentVariables.Count); + } int CountItems = environmentVariables.Count; diff --git a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs index a35237468cf297..d7c7046ab693ed 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/BasicEventSourceTest/TestsWriteEventToListener.cs @@ -448,5 +448,49 @@ public void Test_EventSourceCreatedEvents_AfterListener() TestUtilities.CheckNoEventSourcesRunning("Stop"); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Test_EventListenerThrows_ExceptionIsNotRethrownToCaller(bool setThrowOnEventWriteErrorsFlag) + { + TestUtilities.CheckNoEventSourcesRunning("Start"); + + using (var log = new EventSourceTest(throwOnEventWriteErrors: setThrowOnEventWriteErrorsFlag)) + { + using (var listener = new EventListenerListener()) + { + listener.EventSourceSynchronousEnable(log); + + var thrownException = new Exception("Oops"); + string outOfBandMessage = null; + + listener.EventWritten += (_, e) => + { + if (e.EventId == 0) + { + outOfBandMessage = e.Message; + } + + throw thrownException; + }; + + try + { + log.Event0(); + Assert.False(setThrowOnEventWriteErrorsFlag); + } + catch (EventSourceException ex) + { + Assert.True(setThrowOnEventWriteErrorsFlag); + Assert.Same(thrownException, ex.InnerException); + } + + Assert.Contains(thrownException.Message, outOfBandMessage); + } + } + + TestUtilities.CheckNoEventSourcesRunning("Stop"); + } } } diff --git a/src/libraries/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs b/src/libraries/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs index 5cbd28b2a13328..7a6734e33ca3de 100644 --- a/src/libraries/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs +++ b/src/libraries/System.Diagnostics.Tracing/tests/CustomEventSources/EventSourceTest.cs @@ -26,8 +26,9 @@ namespace SdtEventSources [EventSource(Guid = "69e2aa3e-083b-5014-cad4-3e511a0b94cf", Name = "EventSourceTest")] public sealed class EventSourceTest : EventSource { - public EventSourceTest(bool useSelfDescribingEvents = false) - : base(true) + public EventSourceTest(bool useSelfDescribingEvents = false, bool throwOnEventWriteErrors = false) + : base((useSelfDescribingEvents ? EventSourceSettings.EtwSelfDescribingEventFormat : EventSourceSettings.EtwManifestEventFormat) + | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0)) { } protected override void OnEventCommand(EventCommandEventArgs command) 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 4acd6376e4c437..73e65b379fe573 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 @@ -188,14 +188,17 @@ void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) filePath.CopyTo(0, buffer, 0, filePath.Length); buffer[filePath.Length] = '\0'; + IntPtr hIcon; fixed (char* b = buffer) { - IntPtr hIcon = Interop.Shell32.ExtractAssociatedIcon(NativeMethods.NullHandleRef, b, ref index); - ArrayPool.Shared.Return(buffer); - if (hIcon != IntPtr.Zero) - { - return new Icon(hIcon, true); - } + hIcon = Interop.Shell32.ExtractAssociatedIcon(NativeMethods.NullHandleRef, b, ref index); + } + + ArrayPool.Shared.Return(buffer); + + if (hIcon != IntPtr.Zero) + { + return new Icon(hIcon, true); } return null; diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs index 49ce7c0b598584..fdc6d751440a3d 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs @@ -35,6 +35,12 @@ namespace System.Drawing /// public sealed partial class ImageAnimator { + // We use a timer to apply an animation tick speeds of something a bit shorter than 50ms + // such that if the requested frame rate is about 20 frames per second, we will rarely skip + // a frame entirely. Sometimes we'll show a few more frames if available, but we will never + // show more than 25 frames a second and that's OK. + internal const int AnimationDelayMS = 40; + /// /// A list of images to be animated. /// @@ -387,7 +393,7 @@ private static void AnimateImages() while (true) { - Thread.Sleep(40); + Thread.Sleep(AnimationDelayMS); // Because Thread.Sleep is not accurate, capture how much time has actually elapsed during the animation long timeElapsed = stopwatch.ElapsedMilliseconds; 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 24b041395580a0..a5eed455409732 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/ImageInfo.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/ImageInfo.cs @@ -29,6 +29,7 @@ private sealed class ImageInfo private readonly bool _animated; private EventHandler? _onFrameChangedHandler; private readonly long[]? _frameEndTimes; + private long _totalAnimationTime; private long _frameTimer; public ImageInfo(Image image) @@ -66,7 +67,21 @@ public ImageInfo(Image image) } // Frame delays are stored in 1/100ths of a second; convert to milliseconds while accumulating - _frameEndTimes[f] = (lastEndTime += (BitConverter.ToInt32(values, i) * 10)); + // Per spec, a frame delay can be 0 which is treated as a single animation tick + int delay = BitConverter.ToInt32(values, i) * 10; + lastEndTime += delay > 0 ? delay : ImageAnimator.AnimationDelayMS; + + // Guard against overflows + if (lastEndTime < _totalAnimationTime) + { + lastEndTime = _totalAnimationTime; + } + else + { + _totalAnimationTime = lastEndTime; + } + + _frameEndTimes[f] = lastEndTime; } } @@ -95,24 +110,12 @@ public ImageInfo(Image image) /// /// Whether the image supports animation. /// - public bool Animated - { - get - { - return _animated; - } - } + public bool Animated => _animated; /// /// The current frame has changed but the image has not yet been updated. /// - public bool FrameDirty - { - get - { - return _frameDirty; - } - } + public bool FrameDirty => _frameDirty; public EventHandler? FrameChangedHandler { @@ -127,15 +130,15 @@ public EventHandler? FrameChangedHandler } /// - /// The total animation time of the image, in milliseconds. + /// The total animation time of the image in milliseconds, or 0 if not animated. /// - private long TotalAnimationTime => Animated ? _frameEndTimes![_frameCount - 1] : 0; + private long TotalAnimationTime => Animated ? _totalAnimationTime : 0; /// /// Whether animation should progress, respecting the image's animation support /// and if there are animation frames or loops remaining. /// - private bool ShouldAnimate => Animated ? (_loopCount == 0 || _loop <= _loopCount) : false; + private bool ShouldAnimate => TotalAnimationTime > 0 ? (_loopCount == 0 || _loop <= _loopCount) : false; /// /// Advance the animation by the specified number of milliseconds. If the advancement @@ -195,13 +198,7 @@ public void AdvanceAnimationBy(long milliseconds) /// /// The image this object wraps. /// - internal Image Image - { - get - { - return _image; - } - } + internal Image Image => _image; /// /// Selects the current frame as the active frame in the image. diff --git a/src/libraries/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs b/src/libraries/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs index 63cb4e3f9329ed..84d9da6d2c1a38 100644 --- a/src/libraries/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs +++ b/src/libraries/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs @@ -43,6 +43,7 @@ public void AnimateAndCaptureFrames() "animated-timer-10fps-repeat-infinite.gif", "animated-timer-100fps-repeat-2.gif", "animated-timer-100fps-repeat-infinite.gif", + "animated-timer-0-delay-all-frames.gif", }; Dictionary handlers = new(); diff --git a/src/libraries/System.Globalization/tests/Invariant/Invariant.Tests.csproj b/src/libraries/System.Globalization/tests/Invariant/Invariant.Tests.csproj index 6979cd759d0ec1..b142e220e9d433 100644 --- a/src/libraries/System.Globalization/tests/Invariant/Invariant.Tests.csproj +++ b/src/libraries/System.Globalization/tests/Invariant/Invariant.Tests.csproj @@ -4,6 +4,7 @@ true true true + false diff --git a/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json b/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json deleted file mode 100644 index 076d8498455740..00000000000000 --- a/src/libraries/System.Globalization/tests/Invariant/runtimeconfig.template.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "configProperties": { - "System.Globalization.Invariant": true, - "System.Globalization.PredefinedCulturesOnly": false - } -} \ No newline at end of file diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Extract.cs b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Extract.cs index 50e1717fc08a86..eb908ed66f0179 100644 --- a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Extract.cs +++ b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFile.Extract.cs @@ -28,8 +28,7 @@ public static partial class ZipFile /// and file names must be less than 260 characters. /// The path specified by sourceArchive or destinationDirectoryName is invalid, /// (for example, it is on an unmapped drive). - /// The directory specified by destinationDirectoryName already exists. - /// -or- An I/O error has occurred. -or- An archive entry?s name is zero-length, contains only whitespace, or contains one or + /// An I/O error has occurred. -or- An archive entry's name is zero-length, contains only whitespace, or contains one or /// more invalid characters as defined by InvalidPathChars. -or- Extracting an archive entry would result in a file destination that is outside the destination directory (for example, because of parent directory accessors). -or- An archive entry has the same name as an already extracted entry from the same archive. /// The caller does not have the required permission. /// sourceArchive or destinationDirectoryName is in an invalid format. @@ -61,8 +60,7 @@ public static void ExtractToDirectory(string sourceArchiveFileName, string desti /// and file names must be less than 260 characters. /// The path specified by sourceArchive or destinationDirectoryName is invalid, /// (for example, it is on an unmapped drive). - /// The directory specified by destinationDirectoryName already exists. - /// -or- An I/O error has occurred. -or- An archive entry?s name is zero-length, contains only whitespace, or contains one or + /// An I/O error has occurred. -or- An archive entry's name is zero-length, contains only whitespace, or contains one or /// more invalid characters as defined by InvalidPathChars. -or- Extracting an archive entry would result in a file destination that is outside the destination directory (for example, because of parent directory accessors). -or- An archive entry has the same name as an already extracted entry from the same archive. /// The caller does not have the required permission. /// sourceArchive or destinationDirectoryName is in an invalid format. @@ -95,8 +93,7 @@ public static void ExtractToDirectory(string sourceArchiveFileName, string desti /// and file names must be less than 260 characters. /// The path specified by sourceArchive or destinationDirectoryName is invalid, /// (for example, it is on an unmapped drive). - /// The directory specified by destinationDirectoryName already exists. - /// -or- An I/O error has occurred. -or- An archive entry?s name is zero-length, contains only whitespace, or contains one or + /// An I/O error has occurred. -or- An archive entry's name is zero-length, contains only whitespace, or contains one or /// more invalid characters as defined by InvalidPathChars. -or- Extracting an archive entry would result in a file destination that is outside the destination directory (for example, because of parent directory accessors). -or- An archive entry has the same name as an already extracted entry from the same archive. /// The caller does not have the required permission. /// sourceArchive or destinationDirectoryName is in an invalid format. @@ -150,8 +147,7 @@ public static void ExtractToDirectory(string sourceArchiveFileName, string desti /// and file names must be less than 260 characters. /// The path specified by sourceArchive or destinationDirectoryName is invalid, /// (for example, it is on an unmapped drive). - /// The directory specified by destinationDirectoryName already exists. - /// -or- An I/O error has occurred. -or- An archive entry?s name is zero-length, contains only whitespace, or contains one or + /// An I/O error has occurred. -or- An archive entry's name is zero-length, contains only whitespace, or contains one or /// more invalid characters as defined by InvalidPathChars. -or- Extracting an archive entry would result in a file destination that is outside the destination directory (for example, because of parent directory accessors). -or- An archive entry has the same name as an already extracted entry from the same archive. /// The caller does not have the required permission. /// sourceArchive or destinationDirectoryName is in an invalid format. diff --git a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs index a74aca915faaf6..5d800c1f62ca71 100644 --- a/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs +++ b/src/libraries/System.IO.Compression.ZipFile/src/System/IO/Compression/ZipFileExtensions.ZipArchiveEntry.Extract.cs @@ -19,8 +19,7 @@ public static partial class ZipFileExtensions /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. /// The path specified in destinationFileName is invalid (for example, it is on /// an unmapped drive). - /// destinationFileName already exists. - /// -or- An I/O error has occurred. -or- The entry is currently open for writing. + /// An I/O error has occurred. -or- The entry is currently open for writing. /// -or- The entry has been deleted from the archive. /// destinationFileName is in an invalid format /// -or- The ZipArchive that this entry belongs to was opened in a write-only mode. @@ -48,8 +47,7 @@ public static void ExtractToFile(this ZipArchiveEntry source, string destination /// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters. /// The path specified in destinationFileName is invalid /// (for example, it is on an unmapped drive). - /// destinationFileName exists and overwrite is false. - /// -or- An I/O error has occurred. + /// An I/O error has occurred. /// -or- The entry is currently open for writing. /// -or- The entry has been deleted from the archive. /// destinationFileName is in an invalid format @@ -81,7 +79,14 @@ public static void ExtractToFile(this ZipArchiveEntry source, string destination ExtractExternalAttributes(fs, source); } - File.SetLastWriteTime(destinationFileName, source.LastWriteTime.DateTime); + try + { + File.SetLastWriteTime(destinationFileName, source.LastWriteTime.DateTime); + } + catch (UnauthorizedAccessException) + { + // some OSes like Android (#35374) might not support setting the last write time, the extraction should not fail because of that + } } static partial void ExtractExternalAttributes(FileStream fs, ZipArchiveEntry entry); diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs index 9559e7cebc12f5..509914ecd4b0d0 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Read.cs @@ -51,6 +51,20 @@ public void CanUseStackAllocatedMemory(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadFromBeyondEndOfFileReturnsZero(FileOptions options) + { + string filePath = GetTestFilePath(); + File.WriteAllBytes(filePath, new byte[100]); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) + { + long eof = RandomAccess.GetLength(handle); + Assert.Equal(0, RandomAccess.Read(handle, new byte[1], fileOffset: eof + 1)); + } + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public void ReadsBytesFromGivenFileAtGivenOffset(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs index f23cd8f92f6ac9..fe8f8b1111f2db 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadAsync.cs @@ -56,6 +56,20 @@ public async Task ReadToAnEmptyBufferReturnsZeroAsync(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ReadFromBeyondEndOfFileReturnsZeroAsync(FileOptions options) + { + string filePath = GetTestFilePath(); + File.WriteAllBytes(filePath, new byte[100]); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) + { + long eof = RandomAccess.GetLength(handle); + Assert.Equal(0, await RandomAccess.ReadAsync(handle, new byte[1], fileOffset: eof + 1)); + } + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public async Task HappyPath(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs index 7f51ea6e8478e2..0aa60632696188 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatter.cs @@ -46,6 +46,20 @@ public void ReadToAnEmptyBufferReturnsZero(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void ReadFromBeyondEndOfFileReturnsZero(FileOptions options) + { + string filePath = GetTestFilePath(); + File.WriteAllBytes(filePath, new byte[100]); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) + { + long eof = RandomAccess.GetLength(handle); + Assert.Equal(0, RandomAccess.Read(handle, new Memory[] { new byte[1] }, fileOffset: eof + 1)); + } + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public void ReadsBytesFromGivenFileAtGivenOffset(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs index 250d9c316777da..0a9be6c433678e 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/ReadScatterAsync.cs @@ -66,6 +66,20 @@ public async Task ReadToAnEmptyBufferReturnsZeroAsync(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task ReadFromBeyondEndOfFileReturnsZeroAsync(FileOptions options) + { + string filePath = GetTestFilePath(); + File.WriteAllBytes(filePath, new byte[100]); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Open, options: options)) + { + long eof = RandomAccess.GetLength(handle); + Assert.Equal(0, await RandomAccess.ReadAsync(handle, new Memory[] { new byte[1] }, fileOffset: eof + 1)); + } + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public async Task ReadsBytesFromGivenFileAtGivenOffsetAsync(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs index 8a687dc6012dec..d78bd0c686d1d6 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/Write.cs @@ -50,6 +50,22 @@ public void CanUseStackAllocatedMemory(FileOptions options) Assert.Equal(stackAllocated.ToArray(), File.ReadAllBytes(filePath)); } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void WriteBeyondEndOfFileExtendsTheFile(FileOptions options) + { + string filePath = GetTestFilePath(); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, options: options)) + { + Assert.Equal(0, RandomAccess.GetLength(handle)); + RandomAccess.Write(handle, new byte[1] { 1 }, fileOffset: 1); + Assert.Equal(2, RandomAccess.GetLength(handle)); + } + + Assert.Equal(new byte[] { 0, 1 }, File.ReadAllBytes(filePath)); + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public void WritesBytesFromGivenBufferToGivenFileAtGivenOffset(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs index 37003283982060..b816bc8541592e 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteAsync.cs @@ -52,6 +52,22 @@ public async Task WriteUsingEmptyBufferReturnsAsync(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task WriteBeyondEndOfFileExtendsTheFileAsync(FileOptions options) + { + string filePath = GetTestFilePath(); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, options: options)) + { + Assert.Equal(0, RandomAccess.GetLength(handle)); + await RandomAccess.WriteAsync(handle, new byte[1] { 1 }, fileOffset: 1); + Assert.Equal(2, RandomAccess.GetLength(handle)); + } + + Assert.Equal(new byte[] { 0, 1 }, await File.ReadAllBytesAsync(filePath)); + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs index 0f28b7c9c68b65..2edc72eb867c52 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGather.cs @@ -47,6 +47,22 @@ public void WriteUsingEmptyBufferReturns(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public void WriteBeyondEndOfFileExtendsTheFile(FileOptions options) + { + string filePath = GetTestFilePath(); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, options: options)) + { + Assert.Equal(0, RandomAccess.GetLength(handle)); + RandomAccess.Write(handle, new ReadOnlyMemory[] { new byte[1] { 1 } }, fileOffset: 1); + Assert.Equal(2, RandomAccess.GetLength(handle)); + } + + Assert.Equal(new byte[] { 0, 1 }, File.ReadAllBytes(filePath)); + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public void WritesBytesFromGivenBuffersToGivenFileAtGivenOffset(FileOptions options) diff --git a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs index f89cfd3b4fc623..bf995a318fd9a4 100644 --- a/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs +++ b/src/libraries/System.IO.FileSystem/tests/RandomAccess/WriteGatherAsync.cs @@ -64,6 +64,22 @@ public async Task WriteUsingEmptyBufferReturnsAsync(FileOptions options) } } + [Theory] + [MemberData(nameof(GetSyncAsyncOptions))] + public async Task WriteBeyondEndOfFileExtendsTheFileAsync(FileOptions options) + { + string filePath = GetTestFilePath(); + + using (SafeFileHandle handle = File.OpenHandle(filePath, FileMode.CreateNew, FileAccess.Write, options: options)) + { + Assert.Equal(0, RandomAccess.GetLength(handle)); + await RandomAccess.WriteAsync(handle, new ReadOnlyMemory[] { new byte[1] { 1 } }, fileOffset: 1); + Assert.Equal(2, RandomAccess.GetLength(handle)); + } + + Assert.Equal(new byte[] { 0, 1 }, await File.ReadAllBytesAsync(filePath)); + } + [Theory] [MemberData(nameof(GetSyncAsyncOptions))] public async Task WritesBytesFromGivenBufferToGivenFileAtGivenOffsetAsync(FileOptions options) diff --git a/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj b/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj index abb185806210a8..c9a5e30a03c67b 100644 --- a/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj +++ b/src/libraries/System.IO.Packaging/src/System.IO.Packaging.csproj @@ -48,6 +48,7 @@ + diff --git a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackageXmlStringTable.cs b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackageXmlStringTable.cs index cf76091a95d16f..2b1ca4d24560b4 100644 --- a/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackageXmlStringTable.cs +++ b/src/libraries/System.IO.Packaging/src/System/IO/Packaging/PackageXmlStringTable.cs @@ -12,63 +12,63 @@ namespace System.IO.Packaging internal static class PackageXmlStringTable { // Fields - private static readonly NameTable s_nameTable = new NameTable(); + private static readonly ThreadSafeNameTable s_nameTable = new ThreadSafeNameTable(); private static readonly XmlStringTableStruct[] s_xmlstringtable = new XmlStringTableStruct[0x1b]; // Methods static PackageXmlStringTable() { - object nameString = s_nameTable.Add("http://www.w3.org/2001/XMLSchema-instance"); + object nameString = s_nameTable.AddNoLock("http://www.w3.org/2001/XMLSchema-instance"); s_xmlstringtable[1] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("xsi"); + nameString = s_nameTable.AddNoLock("xsi"); s_xmlstringtable[2] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("xmlns"); + nameString = s_nameTable.AddNoLock("xmlns"); s_xmlstringtable[3] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); + nameString = s_nameTable.AddNoLock("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); s_xmlstringtable[4] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("http://purl.org/dc/elements/1.1/"); + nameString = s_nameTable.AddNoLock("http://purl.org/dc/elements/1.1/"); s_xmlstringtable[5] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("http://purl.org/dc/terms/"); + nameString = s_nameTable.AddNoLock("http://purl.org/dc/terms/"); s_xmlstringtable[6] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("dc"); + nameString = s_nameTable.AddNoLock("dc"); s_xmlstringtable[7] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("dcterms"); + nameString = s_nameTable.AddNoLock("dcterms"); s_xmlstringtable[8] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, null); - nameString = s_nameTable.Add("coreProperties"); + nameString = s_nameTable.AddNoLock("coreProperties"); s_xmlstringtable[9] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "NotSpecified"); - nameString = s_nameTable.Add("type"); + nameString = s_nameTable.AddNoLock("type"); s_xmlstringtable[10] = new XmlStringTableStruct(nameString, PackageXmlEnum.NotDefined, "NotSpecified"); - nameString = s_nameTable.Add("creator"); + nameString = s_nameTable.AddNoLock("creator"); s_xmlstringtable[11] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("identifier"); + nameString = s_nameTable.AddNoLock("identifier"); s_xmlstringtable[12] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("title"); + nameString = s_nameTable.AddNoLock("title"); s_xmlstringtable[13] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("subject"); + nameString = s_nameTable.AddNoLock("subject"); s_xmlstringtable[14] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("description"); + nameString = s_nameTable.AddNoLock("description"); s_xmlstringtable[15] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("language"); + nameString = s_nameTable.AddNoLock("language"); s_xmlstringtable[0x10] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("created"); + nameString = s_nameTable.AddNoLock("created"); s_xmlstringtable[0x11] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCoreTermsNamespace, "DateTime"); - nameString = s_nameTable.Add("modified"); + nameString = s_nameTable.AddNoLock("modified"); s_xmlstringtable[0x12] = new XmlStringTableStruct(nameString, PackageXmlEnum.DublinCoreTermsNamespace, "DateTime"); - nameString = s_nameTable.Add("contentType"); + nameString = s_nameTable.AddNoLock("contentType"); s_xmlstringtable[0x13] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("keywords"); + nameString = s_nameTable.AddNoLock("keywords"); s_xmlstringtable[20] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("category"); + nameString = s_nameTable.AddNoLock("category"); s_xmlstringtable[0x15] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("version"); + nameString = s_nameTable.AddNoLock("version"); s_xmlstringtable[0x16] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("lastModifiedBy"); + nameString = s_nameTable.AddNoLock("lastModifiedBy"); s_xmlstringtable[0x17] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("contentStatus"); + nameString = s_nameTable.AddNoLock("contentStatus"); s_xmlstringtable[0x18] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("revision"); + nameString = s_nameTable.AddNoLock("revision"); s_xmlstringtable[0x19] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "String"); - nameString = s_nameTable.Add("lastPrinted"); + nameString = s_nameTable.AddNoLock("lastPrinted"); s_xmlstringtable[0x1a] = new XmlStringTableStruct(nameString, PackageXmlEnum.PackageCorePropertiesNamespace, "DateTime"); } @@ -161,5 +161,43 @@ internal string? ValueType } } } + + private sealed class ThreadSafeNameTable : NameTable + { + public override string Add(char[] array, int offset, int length) + { + lock (this) + { + return base.Add(array, offset, length); + } + } + + public override string Add(string array) + { + lock (this) + { + return base.Add(array); + } + } + + // can be used only from static ctor (which is always executed by a single thread) + internal string AddNoLock(string array) => base.Add(array); + + public override string? Get(char[] array, int offset, int length) + { + lock (this) + { + return base.Get(array, offset, length); + } + } + + public override string? Get(string array) + { + lock (this) + { + return base.Get(array); + } + } + } } } diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingReadStream.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingReadStream.cs index 894ea8c7860cd6..cc8ef875b9d71b 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingReadStream.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingReadStream.cs @@ -208,17 +208,20 @@ protected override void Dispose(bool disposing) { _disposed = true; - Debug.Assert(_charBuffer.Array != null); - ArrayPool.Shared.Return(_charBuffer.Array); + char[]? charBuffer = _charBuffer.Array; + Debug.Assert(charBuffer != null); _charBuffer = default; + ArrayPool.Shared.Return(charBuffer); - Debug.Assert(_byteBuffer.Array != null); - ArrayPool.Shared.Return(_byteBuffer.Array); + byte[]? byteBuffer = _byteBuffer.Array; + Debug.Assert(byteBuffer != null); _byteBuffer = default; + ArrayPool.Shared.Return(byteBuffer); - Debug.Assert(_overflowBuffer.Array != null); - ArrayPool.Shared.Return(_overflowBuffer.Array); + byte[]? overflowBuffer = _overflowBuffer.Array; + Debug.Assert(overflowBuffer != null); _overflowBuffer = default; + ArrayPool.Shared.Return(overflowBuffer); _stream.Dispose(); } diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingWriteStream.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingWriteStream.cs index 51c73d00634c39..2217baefb6ea40 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingWriteStream.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/TranscodingWriteStream.cs @@ -133,8 +133,9 @@ protected override void Dispose(bool disposing) if (!_disposed) { _disposed = true; - ArrayPool.Shared.Return(_charBuffer); + char[] toReturn = _charBuffer; _charBuffer = null!; + ArrayPool.Shared.Return(toReturn); } } diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.mobile.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.mobile.xml new file mode 100644 index 00000000000000..5b705abeb24bda --- /dev/null +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.mobile.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml index 8af1dcbdfa8a6a..5e70f6fbb5d7d5 100644 --- a/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml +++ b/src/libraries/System.Net.Http/src/ILLink/ILLink.Substitutions.xml @@ -3,8 +3,5 @@ - - - diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 330c6cf1614468..77ed5b9c436c64 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -1,4 +1,4 @@ - + win true @@ -18,6 +18,8 @@ + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs index 2e1434b0b49935..3b77aeafc0467b 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClient.cs @@ -815,16 +815,6 @@ private void PrepareRequestMessage(HttpRequestMessage request) return (pendingRequestsCts, DisposeTokenSource: false, pendingRequestsCts); } - private static bool IsNativeHandlerEnabled() - { - if (!AppContext.TryGetSwitch("System.Net.Http.UseNativeHttpHandler", out bool isEnabled)) - { - return false; - } - - return isEnabled; - } - private Uri? CreateUri(string? uri) => string.IsNullOrEmpty(uri) ? null : new Uri(uri, UriKind.RelativeOrAbsolute); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs index 309e60f65a9156..cfe17837ce96c3 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpClientHandler.AnyMobile.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Net.Security; using System.Reflection; +using System.Runtime.ExceptionServices; using System.Runtime.Versioning; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -424,7 +425,15 @@ private object InvokeNativeHandlerMethod(string name, params object?[] parameter s_cachedMethods[name] = method; } - return method!.Invoke(_nativeHandler, parameters)!; + try + { + return method!.Invoke(_nativeHandler, parameters)!; + } + catch (TargetInvocationException e) + { + ExceptionDispatchInfo.Capture(e.InnerException!).Throw(); + throw; + } } private static bool IsNativeHandlerEnabled => RuntimeSettingParser.QueryRuntimeSettingSwitch( 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 d3e6a90c4305b5..6b2012d6df384c 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 @@ -283,7 +283,7 @@ private void OnServerGoAway(long lastProcessedStreamId) lock (SyncObj) { - if (lastProcessedStreamId > _lastProcessedStreamId) + if (_lastProcessedStreamId != -1 && lastProcessedStreamId > _lastProcessedStreamId) { // Server can send multiple GOAWAY frames. // Spec says a server MUST NOT increase the stream ID in subsequent GOAWAYs, 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 19d8eb20262039..45b74472b402cb 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 @@ -141,7 +141,7 @@ public async Task SendAsync(CancellationToken cancellationT // If we don't have content, or we are doing Expect 100 Continue, then we can't rely on // this and must send our headers immediately. - await _stream.WriteAsync(_sendBuffer.ActiveMemory, requestCancellationSource.Token).ConfigureAwait(false); + await _stream.WriteAsync(_sendBuffer.ActiveMemory, endStream: _expect100ContinueCompletionSource == null, requestCancellationSource.Token).ConfigureAwait(false); _sendBuffer.Discard(_sendBuffer.ActiveLength); if (_expect100ContinueCompletionSource != null) @@ -150,10 +150,6 @@ public async Task SendAsync(CancellationToken cancellationT // TODO: MsQuic may not need any flushing. await _stream.FlushAsync(cancellationToken).ConfigureAwait(false); } - else - { - _stream.Shutdown(); - } } // If using duplex content, the content will continue sending after this method completes. diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs index 9f03c1750e448f..c736eb6bb2a6bd 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Headers.cs @@ -407,6 +407,7 @@ public async Task SendAsync_GetWithInvalidHostHeader_ThrowsException() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56292")] [ActiveIssue("https://github.com/dotnet/runtime/issues/54160", TestPlatforms.Browser)] public async Task SendAsync_WithZeroLengthHeaderName_Throws() { diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs index 22d0370ef10fc7..2f78323b56ec1f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.Http3.cs @@ -458,6 +458,7 @@ public enum CancellationType } [ConditionalTheory(nameof(IsMsQuicSupported))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56194")] [InlineData(CancellationType.Dispose)] [InlineData(CancellationType.CancellationToken)] public async Task ResponseCancellation_ServerReceivesCancellation(CancellationType type) @@ -545,6 +546,7 @@ public async Task ResponseCancellation_ServerReceivesCancellation(CancellationTy } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56265")] public async Task ResponseCancellation_BothCancellationTokenAndDispose_Success() { if (UseQuicImplementationProvider != QuicImplementationProviders.MsQuic) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/LoopbackSocksServer.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/LoopbackSocksServer.cs index 9f22e671dea380..781b7be21f2388 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/LoopbackSocksServer.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/LoopbackSocksServer.cs @@ -1,13 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Net.Sockets; -using System.Net.Test.Common; using System.Runtime.ExceptionServices; using System.Text; -using System.Threading; using System.Threading.Tasks; namespace System.Net.Http.Functional.Tests @@ -15,20 +13,17 @@ namespace System.Net.Http.Functional.Tests /// /// Provides a test-only SOCKS4/5 proxy. /// - internal class LoopbackSocksServer : IDisposable + internal class LoopbackSocksServer : IAsyncDisposable { private readonly Socket _listener; - private readonly ManualResetEvent _serverStopped; - private bool _disposed; - - private int _connections; - public int Connections => _connections; + private readonly List _connectionTasks = new(); + private readonly TaskCompletionSource _serverStopped = new(TaskCreationOptions.RunContinuationsAsynchronously); public int Port { get; } private string? _username, _password; - private LoopbackSocksServer(string? username = null, string? password = null) + public LoopbackSocksServer(string? username = null, string? password = null) { if (password != null && username == null) { @@ -40,74 +35,37 @@ private LoopbackSocksServer(string? username = null, string? password = null) _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); - _listener.Listen(int.MaxValue); + _listener.Listen(); var ep = (IPEndPoint)_listener.LocalEndPoint; Port = ep.Port; - _serverStopped = new ManualResetEvent(false); - } - - private void Start() - { Task.Run(async () => { - var activeTasks = new ConcurrentDictionary(); - - try + while (true) { - while (true) + try { Socket s = await _listener.AcceptAsync().ConfigureAwait(false); - var connectionTask = Task.Run(async () => + _connectionTasks.Add(Task.Run(async () => { - try - { - await ProcessConnection(s).ConfigureAwait(false); - } - catch (Exception ex) + using (var ns = new NetworkStream(s, ownsSocket: true)) { - EventSourceTestLogging.Log.TestAncillaryError(ex); + await ProcessRequest(s, ns).ConfigureAwait(false); } - }); - - activeTasks.TryAdd(connectionTask, 0); - _ = connectionTask.ContinueWith(t => activeTasks.TryRemove(connectionTask, out _), TaskContinuationOptions.ExecuteSynchronously); + })); + } + catch + { + break; } - } - catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted) - { - // caused during Dispose() to cancel the loop. ignore. - } - catch (Exception ex) - { - EventSourceTestLogging.Log.TestAncillaryError(ex); - } - - try - { - await Task.WhenAll(activeTasks.Keys).ConfigureAwait(false); - } - catch (Exception ex) - { - EventSourceTestLogging.Log.TestAncillaryError(ex); } - _serverStopped.Set(); + _serverStopped.SetResult(); }); } - private async Task ProcessConnection(Socket s) - { - Interlocked.Increment(ref _connections); - - using (var ns = new NetworkStream(s, ownsSocket: true)) - { - await ProcessRequest(s, ns).ConfigureAwait(false); - } - } - private async Task ProcessRequest(Socket clientSocket, NetworkStream ns) { int version = await ns.ReadByteAsync().ConfigureAwait(false); @@ -344,21 +302,27 @@ private async ValueTask ReadToFillAsync(Stream stream, Memory buffer) } } - public static LoopbackSocksServer Create(string? username = null, string? password = null) + public async ValueTask DisposeAsync() { - var server = new LoopbackSocksServer(username, password); - server.Start(); + _listener.Dispose(); + await _serverStopped.Task; - return server; - } + List exceptions = new(); + foreach (Task task in _connectionTasks) + { + try + { + await task; + } + catch (Exception ex) + { + exceptions.Add(ex); + } + } - public void Dispose() - { - if (!_disposed) + if (exceptions.Count > 0) { - _listener.Dispose(); - _serverStopped.WaitOne(); - _disposed = true; + throw new AggregateException(exceptions); } } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index 821794e9f82d5e..cb60661313b211 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.IO; using System.Linq; using System.Net.Test.Common; using System.Threading.Tasks; @@ -38,7 +37,7 @@ public async Task TestLoopbackAsync(string scheme, bool useSsl, bool useAuth, st await LoopbackServerFactory.CreateClientAndServerAsync( async uri => { - using LoopbackSocksServer proxy = useAuth ? LoopbackSocksServer.Create("DOTNET", "424242") : LoopbackSocksServer.Create(); + await using var proxy = useAuth ? new LoopbackSocksServer("DOTNET", "424242") : new LoopbackSocksServer(); using HttpClientHandler handler = CreateHttpClientHandler(); using HttpClient client = CreateHttpClient(handler); @@ -93,7 +92,7 @@ public static IEnumerable TestExceptionalAsync_MemberData() [MemberData(nameof(TestExceptionalAsync_MemberData))] public async Task TestExceptionalAsync(string scheme, string host, bool useAuth, ICredentials? credentials, string exceptionMessage) { - using LoopbackSocksServer proxy = useAuth ? LoopbackSocksServer.Create("DOTNET", "424242") : LoopbackSocksServer.Create(); + var proxy = useAuth ? new LoopbackSocksServer("DOTNET", "424242") : new LoopbackSocksServer(); using HttpClientHandler handler = CreateHttpClientHandler(); using HttpClient client = CreateHttpClient(handler); @@ -109,6 +108,12 @@ public async Task TestExceptionalAsync(string scheme, string host, bool useAuth, var innerException = ex.InnerException; Assert.Equal(exceptionMessage, innerException.Message); Assert.Equal("SocksException", innerException.GetType().Name); + + try + { + await proxy.DisposeAsync(); + } + catch { } } } diff --git a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj index 60d0a7259e2e83..d1d9176a5e879f 100644 --- a/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj +++ b/src/libraries/System.Net.Mail/src/System.Net.Mail.csproj @@ -1,8 +1,9 @@ true - $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser + $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-tvOS enable + $(DefineConstants);NO_NTAUTHENTICATION @@ -87,14 +88,10 @@ - - - + + + + + + + - - - + + - + + + + - - + - - - + + + - - - + - + + 0) @@ -107,6 +110,7 @@ internal bool AuthSupported(ISmtpAuthenticationModule module) return true; } } +#endif return false; } diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs index b4ca39ab4536bd..c52a25c7ae3e5d 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs @@ -166,8 +166,8 @@ public static Task GetHostEntryAsync(IPAddress address) throw new ArgumentException(SR.net_invalid_ip_addr, nameof(address)); } - return RunAsync(static s => { - IPHostEntry ipHostEntry = GetHostEntryCore((IPAddress)s, AddressFamily.Unspecified); + return RunAsync(static (s, stopwatch) => { + IPHostEntry ipHostEntry = GetHostEntryCore((IPAddress)s, AddressFamily.Unspecified, stopwatch); if (NetEventSource.Log.IsEnabled()) NetEventSource.Info((IPAddress)s, $"{ipHostEntry} with {ipHostEntry.AddressList.Length} entries"); return ipHostEntry; }, address, CancellationToken.None); @@ -380,17 +380,20 @@ public static IPHostEntry EndResolve(IAsyncResult asyncResult) return ipHostEntry; } - private static IPHostEntry GetHostEntryCore(string hostName, AddressFamily addressFamily) => - (IPHostEntry)GetHostEntryOrAddressesCore(hostName, justAddresses: false, addressFamily); + private static IPHostEntry GetHostEntryCore(string hostName, AddressFamily addressFamily, ValueStopwatch stopwatch = default) => + (IPHostEntry)GetHostEntryOrAddressesCore(hostName, justAddresses: false, addressFamily, stopwatch); - private static IPAddress[] GetHostAddressesCore(string hostName, AddressFamily addressFamily) => - (IPAddress[])GetHostEntryOrAddressesCore(hostName, justAddresses: true, addressFamily); + private static IPAddress[] GetHostAddressesCore(string hostName, AddressFamily addressFamily, ValueStopwatch stopwatch = default) => + (IPAddress[])GetHostEntryOrAddressesCore(hostName, justAddresses: true, addressFamily, stopwatch); - private static object GetHostEntryOrAddressesCore(string hostName, bool justAddresses, AddressFamily addressFamily) + private static object GetHostEntryOrAddressesCore(string hostName, bool justAddresses, AddressFamily addressFamily, ValueStopwatch stopwatch) { ValidateHostName(hostName); - ValueStopwatch stopwatch = NameResolutionTelemetry.Log.BeforeResolution(hostName); + if (!stopwatch.IsActive) + { + stopwatch = NameResolutionTelemetry.Log.BeforeResolution(hostName); + } object result; try @@ -423,21 +426,24 @@ private static object GetHostEntryOrAddressesCore(string hostName, bool justAddr return result; } - private static IPHostEntry GetHostEntryCore(IPAddress address, AddressFamily addressFamily) => - (IPHostEntry)GetHostEntryOrAddressesCore(address, justAddresses: false, addressFamily); + private static IPHostEntry GetHostEntryCore(IPAddress address, AddressFamily addressFamily, ValueStopwatch stopwatch = default) => + (IPHostEntry)GetHostEntryOrAddressesCore(address, justAddresses: false, addressFamily, stopwatch); - private static IPAddress[] GetHostAddressesCore(IPAddress address, AddressFamily addressFamily) => - (IPAddress[])GetHostEntryOrAddressesCore(address, justAddresses: true, addressFamily); + private static IPAddress[] GetHostAddressesCore(IPAddress address, AddressFamily addressFamily, ValueStopwatch stopwatch) => + (IPAddress[])GetHostEntryOrAddressesCore(address, justAddresses: true, addressFamily, stopwatch); // Does internal IPAddress reverse and then forward lookups (for Legacy and current public methods). - private static object GetHostEntryOrAddressesCore(IPAddress address, bool justAddresses, AddressFamily addressFamily) + private static object GetHostEntryOrAddressesCore(IPAddress address, bool justAddresses, AddressFamily addressFamily, ValueStopwatch stopwatch) { // Try to get the data for the host from its address. // We need to call getnameinfo first, because getaddrinfo w/ the ipaddress string // will only return that address and not the full list. // Do a reverse lookup to get the host name. - ValueStopwatch stopwatch = NameResolutionTelemetry.Log.BeforeResolution(address); + if (!stopwatch.IsActive) + { + stopwatch = NameResolutionTelemetry.Log.BeforeResolution(address); + } SocketError errorCode; string? name; @@ -575,23 +581,23 @@ private static Task GetHostEntryOrAddressesCoreAsync(string hostName, bool justR if (justAddresses) { - return RunAsync(static s => s switch + return RunAsync(static (s, stopwatch) => s switch { - string h => GetHostAddressesCore(h, AddressFamily.Unspecified), - KeyValuePair t => GetHostAddressesCore(t.Key, t.Value), - IPAddress a => GetHostAddressesCore(a, AddressFamily.Unspecified), - KeyValuePair t => GetHostAddressesCore(t.Key, t.Value), + string h => GetHostAddressesCore(h, AddressFamily.Unspecified, stopwatch), + KeyValuePair t => GetHostAddressesCore(t.Key, t.Value, stopwatch), + IPAddress a => GetHostAddressesCore(a, AddressFamily.Unspecified, stopwatch), + KeyValuePair t => GetHostAddressesCore(t.Key, t.Value, stopwatch), _ => null }, asyncState, cancellationToken); } else { - return RunAsync(static s => s switch + return RunAsync(static (s, stopwatch) => s switch { - string h => GetHostEntryCore(h, AddressFamily.Unspecified), - KeyValuePair t => GetHostEntryCore(t.Key, t.Value), - IPAddress a => GetHostEntryCore(a, AddressFamily.Unspecified), - KeyValuePair t => GetHostEntryCore(t.Key, t.Value), + string h => GetHostEntryCore(h, AddressFamily.Unspecified, stopwatch), + KeyValuePair t => GetHostEntryCore(t.Key, t.Value, stopwatch), + IPAddress a => GetHostEntryCore(a, AddressFamily.Unspecified, stopwatch), + KeyValuePair t => GetHostEntryCore(t.Key, t.Value, stopwatch), _ => null }, asyncState, cancellationToken); } @@ -668,8 +674,10 @@ private static bool LogFailure(ValueStopwatch stopwatch) /// than having all concurrent requests for the same host share the exact same task, so that any shuffling of the results /// by the OS to enable round robin is still perceived. /// - private static Task RunAsync(Func func, object key, CancellationToken cancellationToken) + private static Task RunAsync(Func func, object key, CancellationToken cancellationToken) { + ValueStopwatch stopwatch = NameResolutionTelemetry.Log.BeforeResolution(key); + Task? task = null; lock (s_tasks) @@ -685,7 +693,7 @@ private static Task RunAsync(Func func, objec Debug.Assert(!Monitor.IsEntered(s_tasks)); try { - return func(key); + return func(key, stopwatch); } finally { diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionTelemetry.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionTelemetry.cs index fd5d2d31a01183..1d837b3ab1a840 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionTelemetry.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionTelemetry.cs @@ -63,9 +63,10 @@ protected override void OnEventCommand(EventCommandEventArgs command) [NonEvent] - public ValueStopwatch BeforeResolution(string hostNameOrAddress) + public ValueStopwatch BeforeResolution(object hostNameOrAddress) { Debug.Assert(hostNameOrAddress != null); + Debug.Assert(hostNameOrAddress is string || hostNameOrAddress is IPAddress); if (IsEnabled()) { @@ -74,28 +75,14 @@ public ValueStopwatch BeforeResolution(string hostNameOrAddress) if (IsEnabled(EventLevel.Informational, EventKeywords.None)) { - ResolutionStart(hostNameOrAddress); - } - - return ValueStopwatch.StartNew(); - } - - return default; - } - - [NonEvent] - public ValueStopwatch BeforeResolution(IPAddress address) - { - Debug.Assert(address != null); - - if (IsEnabled()) - { - Interlocked.Increment(ref _lookupsRequested); - Interlocked.Increment(ref _currentLookups); - - if (IsEnabled(EventLevel.Informational, EventKeywords.None)) - { - WriteEvent(ResolutionStartEventId, FormatIPAddressNullTerminated(address, stackalloc char[MaxIPFormattedLength])); + if (hostNameOrAddress is string s) + { + ResolutionStart(s); + } + else + { + WriteEvent(ResolutionStartEventId, FormatIPAddressNullTerminated((IPAddress)hostNameOrAddress, stackalloc char[MaxIPFormattedLength])); + } } return ValueStopwatch.StartNew(); diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs index 2b62cb6c77192c..9c167f06a2e6a3 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostByNameTest.cs @@ -108,11 +108,12 @@ public void DnsObsoleteGetHostByName_IPv6String_ReturnsOnlyGivenIP() [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void DnsObsoleteGetHostByName_EmptyString_ReturnsHostName() { - if (PlatformDetection.IsSLES) + if (PlatformDetection.IsSLES || // [ActiveIssue("https://github.com/dotnet/runtime/issues/55271")] + PlatformDetection.IsUbuntu1604 || PlatformDetection.IsDebian9) // [ActiveIssue("https://github.com/dotnet/runtime/issues/56295")] { - // See https://github.com/dotnet/runtime/issues/55271 - throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + throw new SkipTestException("Test environment is not configured for this test to work."); } + IPHostEntry entry = Dns.GetHostByName(""); // DNS labels should be compared as case insensitive for ASCII characters. See RFC 4343. @@ -124,10 +125,10 @@ public void DnsObsoleteGetHostByName_EmptyString_ReturnsHostName() [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public void DnsObsoleteBeginEndGetHostByName_EmptyString_ReturnsHostName() { - if (PlatformDetection.IsSLES) + if (PlatformDetection.IsSLES || // [ActiveIssue("https://github.com/dotnet/runtime/issues/55271")] + PlatformDetection.IsUbuntu1604 || PlatformDetection.IsDebian9) // [ActiveIssue("https://github.com/dotnet/runtime/issues/56295")] { - // See https://github.com/dotnet/runtime/issues/55271 - throw new SkipTestException("SLES Tests environment is not configured for this test to work."); + throw new SkipTestException("Test environment is not configured for this test to work."); } IPHostEntry entry = Dns.EndGetHostByName(Dns.BeginGetHostByName("", null, null)); diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs index bfb8b48cc4e70d..ae5cb3b067c752 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/GetHostEntryTest.cs @@ -26,23 +26,19 @@ public async Task Dns_GetHostEntryAsync_IPAddress_Ok() // [ActiveIssue("https://github.com/dotnet/runtime/issues/27622")] PlatformDetection.IsNotArmNorArm64Process && // [ActiveIssue("https://github.com/dotnet/runtime/issues/1488", TestPlatforms.OSX)] - PlatformDetection.IsNotOSX && + !PlatformDetection.IsOSX && // [ActiveIssue("https://github.com/dotnet/runtime/issues/51377", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] !PlatformDetection.IsiOS && !PlatformDetection.IstvOS && !PlatformDetection.IsMacCatalyst && // [ActiveIssue("https://github.com/dotnet/runtime/issues/55271")] - !PlatformDetection.IsSLES; + !PlatformDetection.IsSLES && + // [ActiveIssue("https://github.com/dotnet/runtime/issues/56295")] + !PlatformDetection.IsUbuntu1604 && !PlatformDetection.IsDebian9; [ConditionalTheory(nameof(GetHostEntryWorks))] [InlineData("")] [InlineData(TestSettings.LocalHost)] public async Task Dns_GetHostEntry_HostString_Ok(string hostName) { - if (PlatformDetection.IsSLES) - { - // See https://github.com/dotnet/runtime/issues/55271 - throw new SkipTestException("SLES Tests environment is not configured for this test to work."); - } - try { await TestGetHostEntryAsync(() => Task.FromResult(Dns.GetHostEntry(hostName))); @@ -91,12 +87,6 @@ public async Task Dns_GetHostEntry_HostString_Ok(string hostName) [InlineData(TestSettings.LocalHost)] public async Task Dns_GetHostEntryAsync_HostString_Ok(string hostName) { - if (PlatformDetection.IsSLES) - { - // See https://github.com/dotnet/runtime/issues/55271 - throw new SkipTestException("SLES Tests environment is not configured for this test to work."); - } - await TestGetHostEntryAsync(() => Dns.GetHostEntryAsync(hostName)); } diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/TelemetryTest.cs index 9cc29aa54b31a0..1bd8d41843c82a 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/TelemetryTest.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.Tracing; using System.Linq; using System.Net.Sockets; +using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; using Xunit; +using Xunit.Sdk; namespace System.Net.NameResolution.Tests { @@ -34,9 +37,10 @@ public static void EventSource_ResolveValidHostName_LogsStartStop() const string ValidHostName = "microsoft.com"; using var listener = new TestEventListener("System.Net.NameResolution", EventLevel.Informational); + 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 Dns.GetHostEntryAsync(ValidHostName); await Dns.GetHostAddressesAsync(ValidHostName); @@ -48,16 +52,7 @@ await listener.RunWithCallbackAsync(events.Enqueue, async () => Dns.EndGetHostAddresses(Dns.BeginGetHostAddresses(ValidHostName, null, null)); }); - Assert.DoesNotContain(events, e => e.EventId == 0); // errors from the EventSource itself - - EventWrittenEventArgs[] starts = events.Where(e => e.EventName == "ResolutionStart").ToArray(); - Assert.Equal(6, starts.Length); - Assert.All(starts, s => Assert.Equal(ValidHostName, Assert.Single(s.Payload).ToString())); - - EventWrittenEventArgs[] stops = events.Where(e => e.EventName == "ResolutionStop").ToArray(); - Assert.Equal(6, stops.Length); - - Assert.DoesNotContain(events, e => e.EventName == "ResolutionFailed"); + VerifyEvents(events, ValidHostName, 6); }).Dispose(); } @@ -70,9 +65,10 @@ public static void EventSource_ResolveInvalidHostName_LogsStartFailureStop() const string InvalidHostName = "invalid...example.com"; using var listener = new TestEventListener("System.Net.NameResolution", EventLevel.Informational); + 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 Assert.ThrowsAnyAsync(async () => await Dns.GetHostEntryAsync(InvalidHostName)); await Assert.ThrowsAnyAsync(async () => await Dns.GetHostAddressesAsync(InvalidHostName)); @@ -84,17 +80,7 @@ await listener.RunWithCallbackAsync(events.Enqueue, async () => Assert.ThrowsAny(() => Dns.EndGetHostAddresses(Dns.BeginGetHostAddresses(InvalidHostName, null, null))); }); - Assert.DoesNotContain(events, e => e.EventId == 0); // errors from the EventSource itself - - EventWrittenEventArgs[] starts = events.Where(e => e.EventName == "ResolutionStart").ToArray(); - Assert.Equal(6, starts.Length); - Assert.All(starts, s => Assert.Equal(InvalidHostName, Assert.Single(s.Payload).ToString())); - - EventWrittenEventArgs[] failures = events.Where(e => e.EventName == "ResolutionFailed").ToArray(); - Assert.Equal(6, failures.Length); - - EventWrittenEventArgs[] stops = events.Where(e => e.EventName == "ResolutionStop").ToArray(); - Assert.Equal(6, stops.Length); + VerifyEvents(events, InvalidHostName, 6, shouldHaveFailures: true); }).Dispose(); } @@ -107,9 +93,10 @@ public static void EventSource_GetHostEntryForIP_LogsStartStop() const string ValidIPAddress = "8.8.4.4"; using var listener = new TestEventListener("System.Net.NameResolution", EventLevel.Informational); + 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 () => { IPAddress ipAddress = IPAddress.Parse(ValidIPAddress); @@ -123,17 +110,119 @@ await listener.RunWithCallbackAsync(events.Enqueue, async () => Dns.EndGetHostEntry(Dns.BeginGetHostEntry(ipAddress, null, null)); }); - Assert.DoesNotContain(events, e => e.EventId == 0); // errors from the EventSource itself - // Each GetHostEntry over an IP will yield 2 resolutions - EventWrittenEventArgs[] starts = events.Where(e => e.EventName == "ResolutionStart").ToArray(); - Assert.Equal(12, starts.Length); - Assert.Equal(6, starts.Count(s => Assert.Single(s.Payload).ToString() == ValidIPAddress)); + VerifyEvents(events, ValidIPAddress, 12, isHostEntryForIp: true); + }).Dispose(); + } + + private static void VerifyEvents(ConcurrentQueue<(EventWrittenEventArgs Event, Guid ActivityId)> events, string hostname, int expectedNumber, bool shouldHaveFailures = false, bool isHostEntryForIp = false) + { + Assert.DoesNotContain(events, e => e.Event.EventId == 0); // errors from the EventSource itself - EventWrittenEventArgs[] stops = events.Where(e => e.EventName == "ResolutionStop").ToArray(); - Assert.Equal(12, stops.Length); + (EventWrittenEventArgs Event, Guid ActivityId)[] starts = events.Where(e => e.Event.EventName == "ResolutionStart").ToArray(); + Assert.Equal(expectedNumber, starts.Length); + + int expectedHostnameStarts = isHostEntryForIp ? expectedNumber / 2 : expectedNumber; + Assert.Equal(expectedHostnameStarts, starts.Count(s => Assert.Single(s.Event.Payload).ToString() == hostname)); + + (EventWrittenEventArgs Event, Guid ActivityId)[] stops = events.Where(e => e.Event.EventName == "ResolutionStop").ToArray(); + Assert.Equal(expectedNumber, stops.Length); + + for (int i = 0; i < starts.Length; i++) + { + Assert.NotEqual(Guid.Empty, starts[i].ActivityId); + Assert.Equal(starts[i].ActivityId, stops[i].ActivityId); + } + + if (shouldHaveFailures) + { + (EventWrittenEventArgs Event, Guid ActivityId)[] failures = events.Where(e => e.Event.EventName == "ResolutionFailed").ToArray(); + Assert.Equal(expectedNumber, failures.Length); + + for (int i = 0; i < starts.Length; i++) + { + Assert.Equal(starts[i].ActivityId, failures[i].ActivityId); + } + } + else + { + Assert.DoesNotContain(events, e => e.Event.EventName == "ResolutionFailed"); + } + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public static void ResolutionsWaitingOnQueue_ResolutionStartCalledBeforeEnqueued() + { + // Some platforms (non-Windows) don't have proper support for GetAddrInfoAsync. + // Instead we perform async-over-sync with a per-host queue. + // This test ensures that ResolutionStart events are written before waiting on the queue. + + // We do this by blocking the first ResolutionStart event. + // If the event was logged after waiting on the queue, the second request would never complete. + RemoteExecutor.Invoke(async () => + { + using var listener = new TestEventListener("System.Net.NameResolution", EventLevel.Informational); + listener.AddActivityTracking(); + + TaskCompletionSource firstResolutionStart = new(TaskCreationOptions.RunContinuationsAsynchronously); + TaskCompletionSource secondResolutionStop = new(TaskCreationOptions.RunContinuationsAsynchronously); + + List<(string EventName, Guid ActivityId)> events = new(); + + bool? callbackWaitTimedOut = null; + + await listener.RunWithCallbackAsync(e => + { + if (e.EventName == "ResolutionStart" || e.EventName == "ResolutionStop") + { + events.Add((e.EventName, e.ActivityId)); + } + + if (e.EventName == "ResolutionStart" && firstResolutionStart.TrySetResult()) + { + callbackWaitTimedOut = !secondResolutionStop.Task.Wait(TimeSpan.FromSeconds(15)); + } + }, + async () => + { + Task first = DoResolutionAsync(); + + await firstResolutionStart.Task.WaitAsync(TimeSpan.FromSeconds(30)); + + Task second = DoResolutionAsync(); + + await Task.WhenAny(first, second).WaitAsync(TimeSpan.FromSeconds(30)); + Assert.False(first.IsCompleted); + + await second; + secondResolutionStop.SetResult(); + + await first.WaitAsync(TimeSpan.FromSeconds(30)); + + static Task DoResolutionAsync() + { + return Task.Run(async () => + { + try + { + await Dns.GetHostAddressesAsync("microsoft.com"); + } + catch { } // We don't care if the request failed, just that events were written properly + }); + } + }); - Assert.DoesNotContain(events, e => e.EventName == "ResolutionFailed"); + Assert.Equal(4, events.Count); + Assert.Equal("ResolutionStart", events[0].EventName); + Assert.Equal("ResolutionStart", events[1].EventName); + Assert.Equal("ResolutionStop", events[2].EventName); + Assert.Equal("ResolutionStop", events[3].EventName); + Assert.All(events, e => Assert.NotEqual(Guid.Empty, e.ActivityId)); + Assert.Equal(events[0].ActivityId, events[3].ActivityId); + Assert.Equal(events[1].ActivityId, events[2].ActivityId); + Assert.NotEqual(events[0].ActivityId, events[1].ActivityId); + + Assert.False(callbackWaitTimedOut); }).Dispose(); } } diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs index 50d71d4f7e3f9c..ee87893dfeb0bd 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Linux.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; +using System.Net.Http.Functional.Tests; using System.Net.Sockets; using System.Net.Test.Common; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -15,213 +17,231 @@ public class IPInterfacePropertiesTest_Linux { private readonly ITestOutputHelper _log; - public IPInterfacePropertiesTest_Linux() + public IPInterfacePropertiesTest_Linux(ITestOutputHelper output) { - _log = TestLogging.GetInstance(); + _log = output; } [Fact] - public void IPInfoTest_AccessAllProperties_NoErrors() + public async Task IPInfoTest_AccessAllProperties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); - _log.WriteLine("- Speed:" + nic.Speed); - _log.WriteLine("- Supports IPv4: " + nic.Supports(NetworkInterfaceComponent.IPv4)); - _log.WriteLine("- Supports IPv6: " + nic.Supports(NetworkInterfaceComponent.IPv6)); - Assert.False(nic.IsReceiveOnly); - - IPInterfaceProperties ipProperties = nic.GetIPProperties(); - - Assert.NotNull(ipProperties); - - Assert.Throws(() => ipProperties.AnycastAddresses); - - Assert.NotNull(ipProperties.DhcpServerAddresses); - _log.WriteLine("- Dhcp Server Addresses: " + ipProperties.DhcpServerAddresses.Count); - foreach (IPAddress dhcp in ipProperties.DhcpServerAddresses) - { - _log.WriteLine("-- " + dhcp.ToString()); - } - - Assert.NotNull(ipProperties.DnsAddresses); - _log.WriteLine("- Dns Addresses: " + ipProperties.DnsAddresses.Count); - foreach (IPAddress dns in ipProperties.DnsAddresses) - { - _log.WriteLine("-- " + dns.ToString()); - } - - Assert.NotNull(ipProperties.DnsSuffix); - _log.WriteLine("- Dns Suffix: " + ipProperties.DnsSuffix); - - Assert.NotNull(ipProperties.GatewayAddresses); - _log.WriteLine("- Gateway Addresses: " + ipProperties.GatewayAddresses.Count); - foreach (GatewayIPAddressInformation gateway in ipProperties.GatewayAddresses) - { - _log.WriteLine("-- " + gateway.Address.ToString()); - } - - _log.WriteLine("- Dns Enabled: " + ipProperties.IsDnsEnabled); - - Assert.Throws(() => ipProperties.IsDynamicDnsEnabled); - - Assert.NotNull(ipProperties.MulticastAddresses); - _log.WriteLine("- Multicast Addresses: " + ipProperties.MulticastAddresses.Count); - foreach (IPAddressInformation multi in ipProperties.MulticastAddresses) - { - _log.WriteLine("-- " + multi.Address.ToString()); - Assert.Throws(() => multi.IsDnsEligible); - Assert.Throws(() => multi.IsTransient); - } - - Assert.NotNull(ipProperties.UnicastAddresses); - _log.WriteLine("- Unicast Addresses: " + ipProperties.UnicastAddresses.Count); - foreach (UnicastIPAddressInformation uni in ipProperties.UnicastAddresses) + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { - _log.WriteLine("-- " + uni.Address.ToString()); - Assert.Throws(() => uni.AddressPreferredLifetime); - Assert.Throws(() => uni.AddressValidLifetime); - Assert.Throws(() => uni.DhcpLeaseLifetime); - Assert.Throws(() => uni.DuplicateAddressDetectionState); - - Assert.NotNull(uni.IPv4Mask); - _log.WriteLine("--- IPv4 Mask: " + uni.IPv4Mask); - Assert.Throws(() => uni.IsDnsEligible); - Assert.Throws(() => uni.IsTransient); - Assert.Throws(() => uni.PrefixOrigin); - Assert.Throws(() => uni.SuffixOrigin); - - // Prefix Length - _log.WriteLine("--- PrefixLength: " + uni.PrefixLength); - Assert.True(uni.PrefixLength > 0); - Assert.True((uni.Address.AddressFamily == AddressFamily.InterNetwork ? 33 : 129) > uni.PrefixLength); + _log.WriteLine("Nic: " + nic.Name); + _log.WriteLine("- Speed:" + nic.Speed); + _log.WriteLine("- Supports IPv4: " + nic.Supports(NetworkInterfaceComponent.IPv4)); + _log.WriteLine("- Supports IPv6: " + nic.Supports(NetworkInterfaceComponent.IPv6)); + Assert.False(nic.IsReceiveOnly); + + IPInterfaceProperties ipProperties = nic.GetIPProperties(); + + Assert.NotNull(ipProperties); + + Assert.Throws(() => ipProperties.AnycastAddresses); + + Assert.NotNull(ipProperties.DhcpServerAddresses); + _log.WriteLine("- Dhcp Server Addresses: " + ipProperties.DhcpServerAddresses.Count); + foreach (IPAddress dhcp in ipProperties.DhcpServerAddresses) + { + _log.WriteLine("-- " + dhcp.ToString()); + } + + Assert.NotNull(ipProperties.DnsAddresses); + _log.WriteLine("- Dns Addresses: " + ipProperties.DnsAddresses.Count); + foreach (IPAddress dns in ipProperties.DnsAddresses) + { + _log.WriteLine("-- " + dns.ToString()); + } + + Assert.NotNull(ipProperties.DnsSuffix); + _log.WriteLine("- Dns Suffix: " + ipProperties.DnsSuffix); + + Assert.NotNull(ipProperties.GatewayAddresses); + _log.WriteLine("- Gateway Addresses: " + ipProperties.GatewayAddresses.Count); + foreach (GatewayIPAddressInformation gateway in ipProperties.GatewayAddresses) + { + _log.WriteLine("-- " + gateway.Address.ToString()); + } + + _log.WriteLine("- Dns Enabled: " + ipProperties.IsDnsEnabled); + + Assert.Throws(() => ipProperties.IsDynamicDnsEnabled); + + Assert.NotNull(ipProperties.MulticastAddresses); + _log.WriteLine("- Multicast Addresses: " + ipProperties.MulticastAddresses.Count); + foreach (IPAddressInformation multi in ipProperties.MulticastAddresses) + { + _log.WriteLine("-- " + multi.Address.ToString()); + Assert.Throws(() => multi.IsDnsEligible); + Assert.Throws(() => multi.IsTransient); + } + + Assert.NotNull(ipProperties.UnicastAddresses); + _log.WriteLine("- Unicast Addresses: " + ipProperties.UnicastAddresses.Count); + foreach (UnicastIPAddressInformation uni in ipProperties.UnicastAddresses) + { + _log.WriteLine("-- " + uni.Address.ToString()); + Assert.Throws(() => uni.AddressPreferredLifetime); + Assert.Throws(() => uni.AddressValidLifetime); + Assert.Throws(() => uni.DhcpLeaseLifetime); + Assert.Throws(() => uni.DuplicateAddressDetectionState); + + Assert.NotNull(uni.IPv4Mask); + _log.WriteLine("--- IPv4 Mask: " + uni.IPv4Mask); + Assert.Throws(() => uni.IsDnsEligible); + Assert.Throws(() => uni.IsTransient); + Assert.Throws(() => uni.PrefixOrigin); + Assert.Throws(() => uni.SuffixOrigin); + + // Prefix Length + _log.WriteLine("--- PrefixLength: " + uni.PrefixLength); + Assert.True(uni.PrefixLength > 0); + Assert.True((uni.Address.AddressFamily == AddressFamily.InterNetwork ? 33 : 129) > uni.PrefixLength); + } + + Assert.NotNull(ipProperties.WinsServersAddresses); + _log.WriteLine("- Wins Addresses: " + ipProperties.WinsServersAddresses.Count); + foreach (IPAddress wins in ipProperties.WinsServersAddresses) + { + _log.WriteLine("-- " + wins.ToString()); + } } - - Assert.NotNull(ipProperties.WinsServersAddresses); - _log.WriteLine("- Wins Addresses: " + ipProperties.WinsServersAddresses.Count); - foreach (IPAddress wins in ipProperties.WinsServersAddresses) - { - _log.WriteLine("-- " + wins.ToString()); - } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] - public void IPInfoTest_AccessAllIPv4Properties_NoErrors() + public async Task IPInfoTest_AccessAllIPv4Properties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - _log.WriteLine("IPv4 Properties:"); + _log.WriteLine("IPv4 Properties:"); - IPv4InterfaceProperties ipv4Properties = ipProperties.GetIPv4Properties(); + IPv4InterfaceProperties ipv4Properties = ipProperties.GetIPv4Properties(); - _log.WriteLine("Index: " + ipv4Properties.Index); - Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingActive); - Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingEnabled); - Assert.Throws(() => ipv4Properties.IsDhcpEnabled); - _log.WriteLine("IsForwardingEnabled: " + ipv4Properties.IsForwardingEnabled); - _log.WriteLine("Mtu: " + ipv4Properties.Mtu); - _log.WriteLine("UsesWins: " + ipv4Properties.UsesWins); - } + _log.WriteLine("Index: " + ipv4Properties.Index); + Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingActive); + Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingEnabled); + Assert.Throws(() => ipv4Properties.IsDhcpEnabled); + _log.WriteLine("IsForwardingEnabled: " + ipv4Properties.IsForwardingEnabled); + _log.WriteLine("Mtu: " + ipv4Properties.Mtu); + _log.WriteLine("UsesWins: " + ipv4Properties.UsesWins); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] - public void IPInfoTest_AccessAllIPv6Properties_NoErrors() + public async Task IPInfoTest_AccessAllIPv6Properties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - _log.WriteLine("IPv6 Properties:"); + _log.WriteLine("IPv6 Properties:"); - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); - if (ipv6Properties == null) - { - _log.WriteLine("IPv6Properties is null"); - continue; - } + if (ipv6Properties == null) + { + _log.WriteLine("IPv6Properties is null"); + continue; + } - _log.WriteLine("Index: " + ipv6Properties.Index); - _log.WriteLine("Mtu: " + ipv6Properties.Mtu); - _log.WriteLine("Scope: " + ipv6Properties.GetScopeId(ScopeLevel.Link)); - } + _log.WriteLine("Index: " + ipv6Properties.Index); + _log.WriteLine("Mtu: " + ipv6Properties.Mtu); + _log.WriteLine("Scope: " + ipv6Properties.GetScopeId(ScopeLevel.Link)); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv6", "true")] - public void IPv6ScopeId_AccessAllValues_Success() + public async Task IPv6ScopeId_AccessAllValues_Success() { - Assert.True(Capability.IPv6Support()); - - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + Assert.True(Capability.IPv6Support()); - if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { - continue; - } + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + { + continue; + } - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - Array values = Enum.GetValues(typeof(ScopeLevel)); - foreach (ScopeLevel level in values) - { - _log.WriteLine("-- Level: " + level + "; " + ipv6Properties.GetScopeId(level)); + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + + Array values = Enum.GetValues(typeof(ScopeLevel)); + foreach (ScopeLevel level in values) + { + _log.WriteLine("-- Level: " + level + "; " + ipv6Properties.GetScopeId(level)); + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv4", "true")] - public void IPInfoTest_IPv4Loopback_ProperAddress() + public async Task IPInfoTest_IPv4Loopback_ProperAddress() { - Assert.True(Capability.IPv4Support()); + await Task.Run(() => + { + Assert.True(Capability.IPv4Support()); - _log.WriteLine("Loopback IPv4 index: " + NetworkInterface.LoopbackInterfaceIndex); + _log.WriteLine("Loopback IPv4 index: " + NetworkInterface.LoopbackInterfaceIndex); - NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo"); - Assert.NotNull(loopback); + NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo"); + Assert.NotNull(loopback); - foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) - { - if (unicast.Address.Equals(IPAddress.Loopback)) + foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) { - Assert.Equal(IPAddress.Parse("255.0.0.0"), unicast.IPv4Mask); - Assert.Equal(8, unicast.PrefixLength); - break; + if (unicast.Address.Equals(IPAddress.Loopback)) + { + Assert.Equal(IPAddress.Parse("255.0.0.0"), unicast.IPv4Mask); + Assert.Equal(8, unicast.PrefixLength); + break; + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv6", "true")] - public void IPInfoTest_IPv6Loopback_ProperAddress() + public async Task IPInfoTest_IPv6Loopback_ProperAddress() { - Assert.True(Capability.IPv6Support()); + await Task.Run(() => + { + Assert.True(Capability.IPv6Support()); - _log.WriteLine("Loopback IPv6 index: " + NetworkInterface.IPv6LoopbackInterfaceIndex); + _log.WriteLine("Loopback IPv6 index: " + NetworkInterface.IPv6LoopbackInterfaceIndex); - NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo"); - Assert.NotNull(loopback); + NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo"); + Assert.NotNull(loopback); - foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) - { - if (unicast.Address.Equals(IPAddress.IPv6Loopback)) + foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) { - Assert.Equal(128, unicast.PrefixLength); - break; + if (unicast.Address.Equals(IPAddress.IPv6Loopback)) + { + Assert.Equal(128, unicast.PrefixLength); + break; + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } } } diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_OSX.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_OSX.cs index 6fc852bd6989f7..86964c312c03b3 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_OSX.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_OSX.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; +using System.Net.Http.Functional.Tests; using System.Net.Sockets; using System.Net.Test.Common; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -15,206 +17,224 @@ public class IPInterfacePropertiesTest_OSX { private readonly ITestOutputHelper _log; - public IPInterfacePropertiesTest_OSX() + public IPInterfacePropertiesTest_OSX(ITestOutputHelper output) { - _log = TestLogging.GetInstance(); + _log = output; } [Fact] - public void IPInfoTest_AccessAllProperties_NoErrors() + public async Task IPInfoTest_AccessAllProperties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); - _log.WriteLine("- Supports IPv4: " + nic.Supports(NetworkInterfaceComponent.IPv4)); - _log.WriteLine("- Supports IPv6: " + nic.Supports(NetworkInterfaceComponent.IPv6)); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); + _log.WriteLine("- Supports IPv4: " + nic.Supports(NetworkInterfaceComponent.IPv4)); + _log.WriteLine("- Supports IPv6: " + nic.Supports(NetworkInterfaceComponent.IPv6)); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - Assert.NotNull(ipProperties); + Assert.NotNull(ipProperties); - Assert.Throws(() => ipProperties.AnycastAddresses); + Assert.Throws(() => ipProperties.AnycastAddresses); - Assert.Throws(() => ipProperties.DhcpServerAddresses); + Assert.Throws(() => ipProperties.DhcpServerAddresses); - try - { - Assert.NotNull(ipProperties.DnsAddresses); - _log.WriteLine("- Dns Addresses: " + ipProperties.DnsAddresses.Count); - foreach (IPAddress dns in ipProperties.DnsAddresses) + try { - _log.WriteLine("-- " + dns.ToString()); + Assert.NotNull(ipProperties.DnsAddresses); + _log.WriteLine("- Dns Addresses: " + ipProperties.DnsAddresses.Count); + foreach (IPAddress dns in ipProperties.DnsAddresses) + { + _log.WriteLine("-- " + dns.ToString()); + } + Assert.NotNull(ipProperties.DnsSuffix); + _log.WriteLine("- Dns Suffix: " + ipProperties.DnsSuffix); } - Assert.NotNull(ipProperties.DnsSuffix); - _log.WriteLine("- Dns Suffix: " + ipProperties.DnsSuffix); - } - // If /etc/resolv.conf does not exist, DNS values cannot be retrieved. - catch (PlatformNotSupportedException) { } + // If /etc/resolv.conf does not exist, DNS values cannot be retrieved. + catch (PlatformNotSupportedException) { } - Assert.NotNull(ipProperties.GatewayAddresses); - _log.WriteLine("- Gateway Addresses: " + ipProperties.GatewayAddresses.Count); - foreach (GatewayIPAddressInformation gateway in ipProperties.GatewayAddresses) - { - _log.WriteLine("-- " + gateway.Address.ToString()); - } + Assert.NotNull(ipProperties.GatewayAddresses); + _log.WriteLine("- Gateway Addresses: " + ipProperties.GatewayAddresses.Count); + foreach (GatewayIPAddressInformation gateway in ipProperties.GatewayAddresses) + { + _log.WriteLine("-- " + gateway.Address.ToString()); + } - Assert.Throws(() => ipProperties.IsDnsEnabled); + Assert.Throws(() => ipProperties.IsDnsEnabled); - Assert.Throws(() => ipProperties.IsDynamicDnsEnabled); + Assert.Throws(() => ipProperties.IsDynamicDnsEnabled); - Assert.NotNull(ipProperties.MulticastAddresses); - _log.WriteLine("- Multicast Addresses: " + ipProperties.MulticastAddresses.Count); - foreach (IPAddressInformation multi in ipProperties.MulticastAddresses) - { - _log.WriteLine("-- " + multi.Address.ToString()); - Assert.Throws(() => multi.IsDnsEligible); - Assert.Throws(() => multi.IsTransient); - } + Assert.NotNull(ipProperties.MulticastAddresses); + _log.WriteLine("- Multicast Addresses: " + ipProperties.MulticastAddresses.Count); + foreach (IPAddressInformation multi in ipProperties.MulticastAddresses) + { + _log.WriteLine("-- " + multi.Address.ToString()); + Assert.Throws(() => multi.IsDnsEligible); + Assert.Throws(() => multi.IsTransient); + } - Assert.NotNull(ipProperties.UnicastAddresses); - _log.WriteLine("- Unicast Addresses: " + ipProperties.UnicastAddresses.Count); - foreach (UnicastIPAddressInformation uni in ipProperties.UnicastAddresses) - { - _log.WriteLine("-- " + uni.Address.ToString()); - Assert.Throws(() => uni.AddressPreferredLifetime); - Assert.Throws(() => uni.AddressValidLifetime); - Assert.Throws(() => uni.DhcpLeaseLifetime); - Assert.Throws(() => uni.DuplicateAddressDetectionState); - - Assert.NotNull(uni.IPv4Mask); - _log.WriteLine("--- IPv4 Mask: " + uni.IPv4Mask); - Assert.Throws(() => uni.IsDnsEligible); - Assert.Throws(() => uni.IsTransient); - Assert.Throws(() => uni.PrefixOrigin); - Assert.Throws(() => uni.SuffixOrigin); - - // Prefix Length - _log.WriteLine("--- PrefixLength: " + uni.PrefixLength); - Assert.True(uni.PrefixLength > 0); - Assert.True((uni.Address.AddressFamily == AddressFamily.InterNetwork ? 33 : 129) > uni.PrefixLength); + Assert.NotNull(ipProperties.UnicastAddresses); + _log.WriteLine("- Unicast Addresses: " + ipProperties.UnicastAddresses.Count); + foreach (UnicastIPAddressInformation uni in ipProperties.UnicastAddresses) + { + _log.WriteLine("-- " + uni.Address.ToString()); + Assert.Throws(() => uni.AddressPreferredLifetime); + Assert.Throws(() => uni.AddressValidLifetime); + Assert.Throws(() => uni.DhcpLeaseLifetime); + Assert.Throws(() => uni.DuplicateAddressDetectionState); + + Assert.NotNull(uni.IPv4Mask); + _log.WriteLine("--- IPv4 Mask: " + uni.IPv4Mask); + Assert.Throws(() => uni.IsDnsEligible); + Assert.Throws(() => uni.IsTransient); + Assert.Throws(() => uni.PrefixOrigin); + Assert.Throws(() => uni.SuffixOrigin); + + // Prefix Length + _log.WriteLine("--- PrefixLength: " + uni.PrefixLength); + Assert.True(uni.PrefixLength > 0); + Assert.True((uni.Address.AddressFamily == AddressFamily.InterNetwork ? 33 : 129) > uni.PrefixLength); - } + } - Assert.Throws(() => ipProperties.WinsServersAddresses); - } + Assert.Throws(() => ipProperties.WinsServersAddresses); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] - public void IPInfoTest_AccessAllIPv4Properties_NoErrors() + public async Task IPInfoTest_AccessAllIPv4Properties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - _log.WriteLine("IPv4 Properties:"); + _log.WriteLine("IPv4 Properties:"); - IPv4InterfaceProperties ipv4Properties = ipProperties.GetIPv4Properties(); + IPv4InterfaceProperties ipv4Properties = ipProperties.GetIPv4Properties(); - _log.WriteLine("Index: " + ipv4Properties.Index); - Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingActive); - Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingEnabled); - Assert.Throws(() => ipv4Properties.IsDhcpEnabled); - Assert.Throws(() => ipv4Properties.IsForwardingEnabled); - _log.WriteLine("Mtu: " + ipv4Properties.Mtu); - Assert.Throws(() => ipv4Properties.UsesWins); - } + _log.WriteLine("Index: " + ipv4Properties.Index); + Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingActive); + Assert.Throws(() => ipv4Properties.IsAutomaticPrivateAddressingEnabled); + Assert.Throws(() => ipv4Properties.IsDhcpEnabled); + Assert.Throws(() => ipv4Properties.IsForwardingEnabled); + _log.WriteLine("Mtu: " + ipv4Properties.Mtu); + Assert.Throws(() => ipv4Properties.UsesWins); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] - public void IPInfoTest_AccessAllIPv6Properties_NoErrors() + public async Task IPInfoTest_AccessAllIPv6Properties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - _log.WriteLine("IPv6 Properties:"); + _log.WriteLine("IPv6 Properties:"); - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); - if (ipv6Properties == null) - { - _log.WriteLine("IPv6Properties is null"); - continue; - } + if (ipv6Properties == null) + { + _log.WriteLine("IPv6Properties is null"); + continue; + } - _log.WriteLine("Index: " + ipv6Properties.Index); - _log.WriteLine("Mtu: " + ipv6Properties.Mtu); - Assert.Throws(() => ipv6Properties.GetScopeId(ScopeLevel.Link)); - } + _log.WriteLine("Index: " + ipv6Properties.Index); + _log.WriteLine("Mtu: " + ipv6Properties.Mtu); + Assert.Throws(() => ipv6Properties.GetScopeId(ScopeLevel.Link)); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv6", "true")] - public void IPv6ScopeId_AccessAllValues_Success() + public async Task IPv6ScopeId_AccessAllValues_Success() { - Assert.True(Capability.IPv6Support()); - - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + Assert.True(Capability.IPv6Support()); - if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { - continue; - } + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + { + continue; + } - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - Array values = Enum.GetValues(typeof(ScopeLevel)); - foreach (ScopeLevel level in values) - { - Assert.Throws(() => ipv6Properties.GetScopeId(level)); + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + + Array values = Enum.GetValues(typeof(ScopeLevel)); + foreach (ScopeLevel level in values) + { + Assert.Throws(() => ipv6Properties.GetScopeId(level)); + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv4", "true")] - public void IPInfoTest_IPv4Loopback_ProperAddress() + public async Task IPInfoTest_IPv4Loopback_ProperAddress() { - Assert.True(Capability.IPv4Support()); + await Task.Run(() => + { + Assert.True(Capability.IPv4Support()); - _log.WriteLine("Loopback IPv4 index: " + NetworkInterface.LoopbackInterfaceIndex); + _log.WriteLine("Loopback IPv4 index: " + NetworkInterface.LoopbackInterfaceIndex); - NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo0"); - Assert.NotNull(loopback); + NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo0"); + Assert.NotNull(loopback); - foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) - { - if (unicast.Address.Equals(IPAddress.Loopback)) + foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) { - Assert.Equal(IPAddress.Parse("255.0.0.0"), unicast.IPv4Mask); - Assert.Equal(8, unicast.PrefixLength); - break; + if (unicast.Address.Equals(IPAddress.Loopback)) + { + Assert.Equal(IPAddress.Parse("255.0.0.0"), unicast.IPv4Mask); + Assert.Equal(8, unicast.PrefixLength); + break; + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv6", "true")] - public void IPInfoTest_IPv6Loopback_ProperAddress() + public async Task IPInfoTest_IPv6Loopback_ProperAddress() { - Assert.True(Capability.IPv6Support()); + await Task.Run(() => + { + Assert.True(Capability.IPv6Support()); - _log.WriteLine("Loopback IPv6 index: " + NetworkInterface.IPv6LoopbackInterfaceIndex); + _log.WriteLine("Loopback IPv6 index: " + NetworkInterface.IPv6LoopbackInterfaceIndex); - NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo0"); - Assert.NotNull(loopback); + NetworkInterface loopback = NetworkInterface.GetAllNetworkInterfaces().First(ni => ni.Name == "lo0"); + Assert.NotNull(loopback); - foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) - { - if (unicast.Address.Equals(IPAddress.IPv6Loopback)) + foreach (UnicastIPAddressInformation unicast in loopback.GetIPProperties().UnicastAddresses) { - Assert.Equal(128, unicast.PrefixLength); - break; + if (unicast.Address.Equals(IPAddress.IPv6Loopback)) + { + Assert.Equal(128, unicast.PrefixLength); + break; + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } } } diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Windows.cs b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Windows.cs index 92ef12ff9effb2..819020777e6db6 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Windows.cs +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/IPInterfacePropertiesTest_Windows.cs @@ -1,8 +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.Net.Http.Functional.Tests; using System.Net.Sockets; using System.Net.Test.Common; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -14,211 +16,226 @@ public class IPInterfacePropertiesTest_Windows { private readonly ITestOutputHelper _log; - public IPInterfacePropertiesTest_Windows() + public IPInterfacePropertiesTest_Windows(ITestOutputHelper output) { - _log = TestLogging.GetInstance(); + _log = output; } [Fact] - public void IPInfoTest_AccessAllProperties_NoErrors() + public async Task IPInfoTest_AccessAllProperties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); - _log.WriteLine("- Supports IPv4: " + nic.Supports(NetworkInterfaceComponent.IPv4)); - _log.WriteLine("- Supports IPv6: " + nic.Supports(NetworkInterfaceComponent.IPv6)); - - IPInterfaceProperties ipProperties = nic.GetIPProperties(); - - Assert.NotNull(ipProperties); - - Assert.NotNull(ipProperties.AnycastAddresses); - _log.WriteLine("- Anycast Addresses: " + ipProperties.AnycastAddresses.Count); - foreach (IPAddressInformation anyAddr in ipProperties.AnycastAddresses) - { - _log.WriteLine("-- " + anyAddr.Address.ToString()); - _log.WriteLine("--- Dns Eligible: " + anyAddr.IsDnsEligible); - _log.WriteLine("--- Transient: " + anyAddr.IsTransient); - } - - Assert.NotNull(ipProperties.DhcpServerAddresses); - _log.WriteLine("- Dhcp Server Addresses: " + ipProperties.DhcpServerAddresses.Count); - foreach (IPAddress dhcp in ipProperties.DhcpServerAddresses) - { - _log.WriteLine("-- " + dhcp.ToString()); - } - - Assert.NotNull(ipProperties.DnsAddresses); - _log.WriteLine("- Dns Addresses: " + ipProperties.DnsAddresses.Count); - foreach (IPAddress dns in ipProperties.DnsAddresses) - { - _log.WriteLine("-- " + dns.ToString()); - } - - Assert.NotNull(ipProperties.DnsSuffix); - _log.WriteLine("- Dns Suffix: " + ipProperties.DnsSuffix); - - Assert.NotNull(ipProperties.GatewayAddresses); - _log.WriteLine("- Gateway Addresses: " + ipProperties.GatewayAddresses.Count); - foreach (GatewayIPAddressInformation gateway in ipProperties.GatewayAddresses) - { - _log.WriteLine("-- " + gateway.Address.ToString()); - } - - _log.WriteLine("- Dns Enabled: " + ipProperties.IsDnsEnabled); - - _log.WriteLine("- Dynamic Dns Enabled: " + ipProperties.IsDynamicDnsEnabled); - - Assert.NotNull(ipProperties.MulticastAddresses); - _log.WriteLine("- Multicast Addresses: " + ipProperties.MulticastAddresses.Count); - foreach (IPAddressInformation multi in ipProperties.MulticastAddresses) - { - _log.WriteLine("-- " + multi.Address.ToString()); - _log.WriteLine("--- Dns Eligible: " + multi.IsDnsEligible); - _log.WriteLine("--- Transient: " + multi.IsTransient); - } - - Assert.NotNull(ipProperties.UnicastAddresses); - _log.WriteLine("- Unicast Addresses: " + ipProperties.UnicastAddresses.Count); - foreach (UnicastIPAddressInformation uni in ipProperties.UnicastAddresses) + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { - _log.WriteLine("-- " + uni.Address.ToString()); - _log.WriteLine("--- Preferred Lifetime: " + uni.AddressPreferredLifetime); - _log.WriteLine("--- Valid Lifetime: " + uni.AddressValidLifetime); - _log.WriteLine("--- Dhcp lease Lifetime: " + uni.DhcpLeaseLifetime); - _log.WriteLine("--- Duplicate Address Detection State: " + uni.DuplicateAddressDetectionState); - - Assert.NotNull(uni.IPv4Mask); - _log.WriteLine("--- IPv4 Mask: " + uni.IPv4Mask); - _log.WriteLine("--- Dns Eligible: " + uni.IsDnsEligible); - _log.WriteLine("--- Transient: " + uni.IsTransient); - _log.WriteLine("--- Prefix Origin: " + uni.PrefixOrigin); - _log.WriteLine("--- Suffix Origin: " + uni.SuffixOrigin); - - // Prefix Length - _log.WriteLine("--- Prefix Length: " + uni.PrefixLength); - Assert.NotEqual(0, uni.PrefixLength); + _log.WriteLine("Nic: " + nic.Name); + _log.WriteLine("- Supports IPv4: " + nic.Supports(NetworkInterfaceComponent.IPv4)); + _log.WriteLine("- Supports IPv6: " + nic.Supports(NetworkInterfaceComponent.IPv6)); + + IPInterfaceProperties ipProperties = nic.GetIPProperties(); + + Assert.NotNull(ipProperties); + + Assert.NotNull(ipProperties.AnycastAddresses); + _log.WriteLine("- Anycast Addresses: " + ipProperties.AnycastAddresses.Count); + foreach (IPAddressInformation anyAddr in ipProperties.AnycastAddresses) + { + _log.WriteLine("-- " + anyAddr.Address.ToString()); + _log.WriteLine("--- Dns Eligible: " + anyAddr.IsDnsEligible); + _log.WriteLine("--- Transient: " + anyAddr.IsTransient); + } + + Assert.NotNull(ipProperties.DhcpServerAddresses); + _log.WriteLine("- Dhcp Server Addresses: " + ipProperties.DhcpServerAddresses.Count); + foreach (IPAddress dhcp in ipProperties.DhcpServerAddresses) + { + _log.WriteLine("-- " + dhcp.ToString()); + } + + Assert.NotNull(ipProperties.DnsAddresses); + _log.WriteLine("- Dns Addresses: " + ipProperties.DnsAddresses.Count); + foreach (IPAddress dns in ipProperties.DnsAddresses) + { + _log.WriteLine("-- " + dns.ToString()); + } + + Assert.NotNull(ipProperties.DnsSuffix); + _log.WriteLine("- Dns Suffix: " + ipProperties.DnsSuffix); + + Assert.NotNull(ipProperties.GatewayAddresses); + _log.WriteLine("- Gateway Addresses: " + ipProperties.GatewayAddresses.Count); + foreach (GatewayIPAddressInformation gateway in ipProperties.GatewayAddresses) + { + _log.WriteLine("-- " + gateway.Address.ToString()); + } + + _log.WriteLine("- Dns Enabled: " + ipProperties.IsDnsEnabled); + + _log.WriteLine("- Dynamic Dns Enabled: " + ipProperties.IsDynamicDnsEnabled); + + Assert.NotNull(ipProperties.MulticastAddresses); + _log.WriteLine("- Multicast Addresses: " + ipProperties.MulticastAddresses.Count); + foreach (IPAddressInformation multi in ipProperties.MulticastAddresses) + { + _log.WriteLine("-- " + multi.Address.ToString()); + _log.WriteLine("--- Dns Eligible: " + multi.IsDnsEligible); + _log.WriteLine("--- Transient: " + multi.IsTransient); + } + + Assert.NotNull(ipProperties.UnicastAddresses); + _log.WriteLine("- Unicast Addresses: " + ipProperties.UnicastAddresses.Count); + foreach (UnicastIPAddressInformation uni in ipProperties.UnicastAddresses) + { + _log.WriteLine("-- " + uni.Address.ToString()); + _log.WriteLine("--- Preferred Lifetime: " + uni.AddressPreferredLifetime); + _log.WriteLine("--- Valid Lifetime: " + uni.AddressValidLifetime); + _log.WriteLine("--- Dhcp lease Lifetime: " + uni.DhcpLeaseLifetime); + _log.WriteLine("--- Duplicate Address Detection State: " + uni.DuplicateAddressDetectionState); + + Assert.NotNull(uni.IPv4Mask); + _log.WriteLine("--- IPv4 Mask: " + uni.IPv4Mask); + _log.WriteLine("--- Dns Eligible: " + uni.IsDnsEligible); + _log.WriteLine("--- Transient: " + uni.IsTransient); + _log.WriteLine("--- Prefix Origin: " + uni.PrefixOrigin); + _log.WriteLine("--- Suffix Origin: " + uni.SuffixOrigin); + + // Prefix Length + _log.WriteLine("--- Prefix Length: " + uni.PrefixLength); + Assert.NotEqual(0, uni.PrefixLength); + } + + Assert.NotNull(ipProperties.WinsServersAddresses); + _log.WriteLine("- Wins Addresses: " + ipProperties.WinsServersAddresses.Count); + foreach (IPAddress wins in ipProperties.WinsServersAddresses) + { + _log.WriteLine("-- " + wins.ToString()); + } } - - Assert.NotNull(ipProperties.WinsServersAddresses); - _log.WriteLine("- Wins Addresses: " + ipProperties.WinsServersAddresses.Count); - foreach (IPAddress wins in ipProperties.WinsServersAddresses) - { - _log.WriteLine("-- " + wins.ToString()); - } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] - public void IPInfoTest_AccessAllIPv4Properties_NoErrors() + public async Task IPInfoTest_AccessAllIPv4Properties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - _log.WriteLine("IPv4 Properties:"); + _log.WriteLine("IPv4 Properties:"); - if (!nic.Supports(NetworkInterfaceComponent.IPv4)) - { - var nie = Assert.Throws(() => ipProperties.GetIPv4Properties()); - Assert.Equal(SocketError.ProtocolNotSupported, (SocketError)nie.ErrorCode); - continue; - } + if (!nic.Supports(NetworkInterfaceComponent.IPv4)) + { + var nie = Assert.Throws(() => ipProperties.GetIPv4Properties()); + Assert.Equal(SocketError.ProtocolNotSupported, (SocketError)nie.ErrorCode); + continue; + } - IPv4InterfaceProperties ipv4Properties = ipProperties.GetIPv4Properties(); + IPv4InterfaceProperties ipv4Properties = ipProperties.GetIPv4Properties(); - _log.WriteLine("Index: " + ipv4Properties.Index); - _log.WriteLine("IsAutomaticPrivateAddressingActive: " + ipv4Properties.IsAutomaticPrivateAddressingActive); - _log.WriteLine("IsAutomaticPrivateAddressingEnabled: " + ipv4Properties.IsAutomaticPrivateAddressingEnabled); - _log.WriteLine("IsDhcpEnabled: " + ipv4Properties.IsDhcpEnabled); - _log.WriteLine("IsForwardingEnabled: " + ipv4Properties.IsForwardingEnabled); - _log.WriteLine("Mtu: " + ipv4Properties.Mtu); - _log.WriteLine("UsesWins: " + ipv4Properties.UsesWins); - } + _log.WriteLine("Index: " + ipv4Properties.Index); + _log.WriteLine("IsAutomaticPrivateAddressingActive: " + ipv4Properties.IsAutomaticPrivateAddressingActive); + _log.WriteLine("IsAutomaticPrivateAddressingEnabled: " + ipv4Properties.IsAutomaticPrivateAddressingEnabled); + _log.WriteLine("IsDhcpEnabled: " + ipv4Properties.IsDhcpEnabled); + _log.WriteLine("IsForwardingEnabled: " + ipv4Properties.IsForwardingEnabled); + _log.WriteLine("Mtu: " + ipv4Properties.Mtu); + _log.WriteLine("UsesWins: " + ipv4Properties.UsesWins); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] - public void IPInfoTest_AccessAllIPv6Properties_NoErrors() + public async Task IPInfoTest_AccessAllIPv6Properties_NoErrors() { - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + { + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - _log.WriteLine("IPv6 Properties:"); + _log.WriteLine("IPv6 Properties:"); - if (!nic.Supports(NetworkInterfaceComponent.IPv6)) - { - var nie = Assert.Throws(() => ipProperties.GetIPv6Properties()); - Assert.Equal(SocketError.ProtocolNotSupported, (SocketError)nie.ErrorCode); - continue; - } + if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + { + var nie = Assert.Throws(() => ipProperties.GetIPv6Properties()); + Assert.Equal(SocketError.ProtocolNotSupported, (SocketError)nie.ErrorCode); + continue; + } - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); - if (ipv6Properties == null) - { - _log.WriteLine("IPv6Properties is null"); - continue; - } + if (ipv6Properties == null) + { + _log.WriteLine("IPv6Properties is null"); + continue; + } - _log.WriteLine("Index: " + ipv6Properties.Index); - _log.WriteLine("Mtu: " + ipv6Properties.Mtu); - _log.WriteLine("ScopeID: " + ipv6Properties.GetScopeId(ScopeLevel.Link)); - } + _log.WriteLine("Index: " + ipv6Properties.Index); + _log.WriteLine("Mtu: " + ipv6Properties.Mtu); + _log.WriteLine("ScopeID: " + ipv6Properties.GetScopeId(ScopeLevel.Link)); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv6", "true")] - public void IPv6ScopeId_GetLinkLevel_MatchesIndex() + public async Task IPv6ScopeId_GetLinkLevel_MatchesIndex() { - Assert.True(Capability.IPv6Support()); - - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + Assert.True(Capability.IPv6Support()); - if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { - continue; - } + IPInterfaceProperties ipProperties = nic.GetIPProperties(); + + if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + { + continue; + } - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); - // This is not officially guaranteed by Windows, but it's what gets used. - Assert.Equal(ipv6Properties.Index, ipv6Properties.GetScopeId(ScopeLevel.Link)); - } + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + // This is not officially guaranteed by Windows, but it's what gets used. + Assert.Equal(ipv6Properties.Index, ipv6Properties.GetScopeId(ScopeLevel.Link)); + } + }).WaitAsync(TestHelper.PassingTestTimeout); } [Fact] [Trait("IPv6", "true")] - public void IPv6ScopeId_AccessAllValues_Success() + public async Task IPv6ScopeId_AccessAllValues_Success() { - Assert.True(Capability.IPv6Support()); - - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) + await Task.Run(() => { - _log.WriteLine("Nic: " + nic.Name); + Assert.True(Capability.IPv6Support()); - if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { - continue; - } + _log.WriteLine("Nic: " + nic.Name); - IPInterfaceProperties ipProperties = nic.GetIPProperties(); + if (!nic.Supports(NetworkInterfaceComponent.IPv6)) + { + continue; + } - _log.WriteLine("- IPv6 Scope levels:"); + IPInterfaceProperties ipProperties = nic.GetIPProperties(); - IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + _log.WriteLine("- IPv6 Scope levels:"); - Array values = Enum.GetValues(typeof(ScopeLevel)); - foreach (ScopeLevel level in values) - { - _log.WriteLine("-- Level: " + level + "; " + ipv6Properties.GetScopeId(level)); + IPv6InterfaceProperties ipv6Properties = ipProperties.GetIPv6Properties(); + + Array values = Enum.GetValues(typeof(ScopeLevel)); + foreach (ScopeLevel level in values) + { + _log.WriteLine("-- Level: " + level + "; " + ipv6Properties.GetScopeId(level)); + } } - } + }).WaitAsync(TestHelper.PassingTestTimeout); } } } diff --git a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj index 07e4ae778c3708..911dc187186c32 100644 --- a/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj +++ b/src/libraries/System.Net.NetworkInformation/tests/FunctionalTests/System.Net.NetworkInformation.Functional.Tests.csproj @@ -1,4 +1,4 @@ - + true ../../src/Resources/Strings.resx @@ -41,6 +41,10 @@ + + The remote certificate is invalid because of errors in the certificate chain: {0} + + The application protocol list is invalid. + 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 1d31aab803cfdd..4549842ee8e681 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -38,8 +38,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -56,6 +93,7 @@ + @@ -83,6 +121,11 @@ + + + + + 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 3c1f232ed7e7e3..1e8fd20f066e5d 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 @@ -219,6 +219,8 @@ internal override QuicStreamProvider OpenBidirectionalStream() internal MockStream OpenStream(long streamId, bool bidirectional) { + CheckDisposed(); + ConnectionState? state = _state; if (state is null) { @@ -274,12 +276,15 @@ internal override async ValueTask AcceptStreamAsync(Cancella catch (ChannelClosedException) { long errorCode = _isClient ? state._serverErrorCode : state._clientErrorCode; - throw new QuicConnectionAbortedException(errorCode); + throw (errorCode == -1) ? new QuicOperationAbortedException() : new QuicConnectionAbortedException(errorCode); } } internal override ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default) { + // TODO: We should abort local streams (and signal the peer to do likewise) + // Currently, we are not tracking the streams associated with this connection. + ConnectionState? state = _state; if (state is not null) { @@ -292,10 +297,12 @@ internal override ValueTask CloseAsync(long errorCode, CancellationToken cancell if (_isClient) { state._clientErrorCode = errorCode; + DrainAcceptQueue(-1, errorCode); } else { state._serverErrorCode = errorCode; + DrainAcceptQueue(errorCode, -1); } } @@ -312,19 +319,37 @@ private void CheckDisposed() } } + private void DrainAcceptQueue(long outboundErrorCode, long inboundErrorCode) + { + ConnectionState? state = _state; + if (state is not null) + { + // TODO: We really only need to do the complete and drain once, but it doesn't really hurt to do it twice. + state._clientInitiatedStreamChannel.Writer.TryComplete(); + while (state._clientInitiatedStreamChannel.Reader.TryRead(out MockStream.StreamState? streamState)) + { + streamState._outboundReadErrorCode = streamState._outboundWriteErrorCode = outboundErrorCode; + streamState._inboundStreamBuffer?.AbortRead(); + streamState._outboundStreamBuffer?.EndWrite(); + } + + state._serverInitiatedStreamChannel.Writer.TryComplete(); + while (state._serverInitiatedStreamChannel.Reader.TryRead(out MockStream.StreamState? streamState)) + { + streamState._inboundReadErrorCode = streamState._inboundWriteErrorCode = inboundErrorCode; + streamState._outboundStreamBuffer?.AbortRead(); + streamState._inboundStreamBuffer?.EndWrite(); + } + } + } + private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { - ConnectionState? state = _state; - if (state is not null) - { - Channel streamChannel = _isClient ? state._clientInitiatedStreamChannel : state._serverInitiatedStreamChannel; - streamChannel.Writer.Complete(); - } - + DrainAcceptQueue(-1, -1); PeerStreamLimit? streamLimit = LocalStreamLimit; if (streamLimit is not null) @@ -448,6 +473,7 @@ public ConnectionState(SslApplicationProtocol applicationProtocol) _applicationProtocol = applicationProtocol; _clientInitiatedStreamChannel = Channel.CreateUnbounded(); _serverInitiatedStreamChannel = Channel.CreateUnbounded(); + _clientErrorCode = _serverErrorCode = -1; } } } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs index 313fc1eb6b6915..fde0eab97d197b 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockStream.cs @@ -73,7 +73,7 @@ internal override async ValueTask ReadAsync(Memory buffer, Cancellati long errorCode = _isInitiator ? _streamState._inboundReadErrorCode : _streamState._outboundReadErrorCode; if (errorCode != 0) { - throw new QuicStreamAbortedException(errorCode); + throw (errorCode == -1) ? new QuicOperationAbortedException() : new QuicStreamAbortedException(errorCode); } } 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 f3dcbf2ca706ff..7b0579e73c7279 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 @@ -386,10 +386,7 @@ private static uint HandleEventPeerCertificateReceived(State state, ref Connecti chain.ChainPolicy.ExtraStore.AddRange(additionalCertificates); } - if (!chain.Build(certificate)) - { - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateChainErrors; - } + sslPolicyErrors |= CertificateValidation.BuildChainAndVerifyProperties(chain, certificate, true, state.IsServer, state.TargetHost); } if (!state.RemoteCertificateRequired) @@ -418,7 +415,6 @@ private static uint HandleEventPeerCertificateReceived(State state, ref Connecti if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(state, $"{state.TraceId} Certificate validation for '${certificate?.Subject}' finished with ${sslPolicyErrors}"); -// return (sslPolicyErrors == SslPolicyErrors.None) ? MsQuicStatusCodes.Success : MsQuicStatusCodes.HandshakeFailure; if (sslPolicyErrors != SslPolicyErrors.None) { 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 616d83788a67cb..a6c554b5231d0e 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 @@ -595,9 +595,7 @@ internal override int Read(Span buffer) byte[] rentedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { - Task t = ReadAsync(new Memory(rentedBuffer, 0, buffer.Length)).AsTask(); - ((IAsyncResult)t).AsyncWaitHandle.WaitOne(); - int readLength = t.GetAwaiter().GetResult(); + int readLength = ReadAsync(new Memory(rentedBuffer, 0, buffer.Length)).AsTask().GetAwaiter().GetResult(); rentedBuffer.AsSpan(0, readLength).CopyTo(buffer); return readLength; } @@ -612,9 +610,7 @@ internal override void Write(ReadOnlySpan buffer) ThrowIfDisposed(); // TODO: optimize this. - Task t = WriteAsync(buffer.ToArray()).AsTask(); - ((IAsyncResult)t).AsyncWaitHandle.WaitOne(); - t.GetAwaiter().GetResult(); + WriteAsync(buffer.ToArray()).AsTask().GetAwaiter().GetResult(); } // MsQuic doesn't support explicit flushing diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs index e4486c92b8a269..0d1ec65f00e79f 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs @@ -20,23 +20,19 @@ namespace System.Net.Quic.Tests [ConditionalClass(typeof(QuicTestBase), nameof(IsSupported))] public class MsQuicTests : QuicTestBase { - readonly ITestOutputHelper _output; private static ReadOnlyMemory s_data = Encoding.UTF8.GetBytes("Hello world!"); - public MsQuicTests(ITestOutputHelper output) - { - _output = output; - } + public MsQuicTests(ITestOutputHelper output) : base(output) { } [Fact] public async Task UnidirectionalAndBidirectionalStreamCountsWork() { using QuicListener listener = CreateQuicListener(); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; Assert.Equal(100, serverConnection.GetRemoteAvailableBidirectionalStreamCount()); Assert.Equal(100, serverConnection.GetRemoteAvailableUnidirectionalStreamCount()); } @@ -55,10 +51,10 @@ public async Task UnidirectionalAndBidirectionalChangeValues() }; using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; Assert.Equal(100, clientConnection.GetRemoteAvailableBidirectionalStreamCount()); Assert.Equal(100, clientConnection.GetRemoteAvailableUnidirectionalStreamCount()); Assert.Equal(10, serverConnection.GetRemoteAvailableBidirectionalStreamCount()); @@ -112,10 +108,9 @@ public async Task ConnectWithCertificateChain() }; using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); - ValueTask clientTask = clientConnection.ConnectAsync(); - - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; } [Fact] @@ -157,6 +152,7 @@ public async Task CertificateCallbackThrowPropagates() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56263")] public async Task ConnectWithCertificateCallback() { X509Certificate2 c1 = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate(); @@ -238,6 +234,80 @@ public async Task ConnectWithCertificateCallback() serverConnection.Dispose(); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/56454")] + public async Task ConnectWithCertificateForDifferentName_Throws() + { + (X509Certificate2 certificate, _) = System.Net.Security.Tests.TestHelper.GenerateCertificates("localhost"); + + var quicOptions = new QuicListenerOptions(); + quicOptions.ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0); + quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions(); + quicOptions.ServerAuthenticationOptions.ServerCertificate = certificate; + + using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, quicOptions); + + QuicClientConnectionOptions options = new QuicClientConnectionOptions() + { + RemoteEndPoint = listener.ListenEndPoint, + ClientAuthenticationOptions = GetSslClientAuthenticationOptions(), + }; + + // Use different target host on purpose to get RemoteCertificateNameMismatch ssl error. + options.ClientAuthenticationOptions.TargetHost = "loopback"; + options.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + { + Assert.Equal(certificate.Subject, cert.Subject); + Assert.Equal(certificate.Issuer, cert.Issuer); + Assert.Equal(SslPolicyErrors.RemoteCertificateNameMismatch, errors & SslPolicyErrors.RemoteCertificateNameMismatch); + return SslPolicyErrors.None == errors; + }; + + using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); + ValueTask clientTask = clientConnection.ConnectAsync(); + + using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); + await Assert.ThrowsAsync(async () => await clientTask); + } + + [Theory] + [InlineData("127.0.0.1", true)] + [InlineData("::1", true)] + [InlineData("127.0.0.1", false)] + [InlineData("::1", false)] + public async Task ConnectWithCertificateForLoopbackIP_IndicatesExpectedError(string ipString, bool expectsError) + { + var ipAddress = IPAddress.Parse(ipString); + (X509Certificate2 certificate, _) = System.Net.Security.Tests.TestHelper.GenerateCertificates(expectsError ? "badhost" : "localhost"); + + var quicOptions = new QuicListenerOptions(); + quicOptions.ListenEndPoint = new IPEndPoint(ipAddress, 0); + quicOptions.ServerAuthenticationOptions = GetSslServerAuthenticationOptions(); + quicOptions.ServerAuthenticationOptions.ServerCertificate = certificate; + + using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, quicOptions); + + QuicClientConnectionOptions options = new QuicClientConnectionOptions() + { + RemoteEndPoint = new IPEndPoint(ipAddress, listener.ListenEndPoint.Port), + ClientAuthenticationOptions = GetSslClientAuthenticationOptions(), + }; + + options.ClientAuthenticationOptions.RemoteCertificateValidationCallback = (sender, cert, chain, errors) => + { + Assert.Equal(certificate.Subject, cert.Subject); + Assert.Equal(certificate.Issuer, cert.Issuer); + Assert.Equal(expectsError ? SslPolicyErrors.RemoteCertificateNameMismatch : SslPolicyErrors.None, errors & SslPolicyErrors.RemoteCertificateNameMismatch); + return true; + }; + + using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); + ValueTask clientTask = clientConnection.ConnectAsync(); + + using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); + await clientTask; + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] [ActiveIssue("https://github.com/microsoft/msquic/pull/1728")] @@ -268,10 +338,10 @@ public async Task ConnectWithClientCertificate() clientOptions.ClientAuthenticationOptions.ClientCertificates = new X509CertificateCollection() { ClientCertificate }; using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, clientOptions); - ValueTask clientTask = clientConnection.ConnectAsync(); + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; // Verify functionality of the connections. await PingPong(clientConnection, serverConnection); // check we completed the client certificate verification. @@ -285,10 +355,9 @@ public async Task WaitForAvailableUnidirectionStreamsAsyncWorks() { using QuicListener listener = CreateQuicListener(maxUnidirectionalStreams: 1); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; listener.Dispose(); // No stream opened yet, should return immediately. @@ -313,9 +382,9 @@ public async Task WaitForAvailableBidirectionStreamsAsyncWorks() using QuicListener listener = CreateQuicListener(maxBidirectionalStreams: 1); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; // No stream opened yet, should return immediately. Assert.True(clientConnection.WaitForAvailableBidirectionalStreamsAsync().IsCompletedSuccessfully); @@ -351,16 +420,15 @@ public async Task SetListenerTimeoutWorksWithSmallTimeout() }; using QuicConnection clientConnection = new QuicConnection(QuicImplementationProviders.MsQuic, options); - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; await Assert.ThrowsAsync(async () => await serverConnection.AcceptStreamAsync().AsTask().WaitAsync(TimeSpan.FromSeconds(100))); } [Theory] [MemberData(nameof(WriteData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public async Task WriteTests(int[][] writes, WriteType writeType) { await RunClientServer( @@ -456,9 +524,10 @@ public async Task CallDifferentWriteMethodsWorks() using QuicListener listener = CreateQuicListener(); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; + ReadOnlyMemory helloWorld = Encoding.ASCII.GetBytes("Hello world!"); ReadOnlySequence ros = CreateReadOnlySequenceFromBytes(helloWorld.ToArray()); @@ -481,21 +550,18 @@ public async Task CallDifferentWriteMethodsWorks() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/49157")] public async Task CloseAsync_ByServer_AcceptThrows() { - await RunClientServer( - clientConnection => - { - return Task.CompletedTask; - }, - async serverConnection => - { - var acceptTask = serverConnection.AcceptStreamAsync(); - await serverConnection.CloseAsync(errorCode: 0); - // make sure - await Assert.ThrowsAsync(() => acceptTask.AsTask()); - }); + (QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(); + + using (clientConnection) + using (serverConnection) + { + var acceptTask = serverConnection.AcceptStreamAsync(); + await serverConnection.CloseAsync(errorCode: 0); + // make sure we throw + await Assert.ThrowsAsync(() => acceptTask.AsTask()); + } } internal static ReadOnlySequence CreateReadOnlySequenceFromBytes(byte[] data) @@ -640,9 +706,9 @@ async Task GetStreamIdWithoutStartWorks() using QuicListener listener = CreateQuicListener(); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); Assert.Equal(0, clientStream.StreamId); @@ -663,9 +729,9 @@ async Task GetStreamIdWithoutStartWorks() using QuicListener listener = CreateQuicListener(); using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - ValueTask clientTask = clientConnection.ConnectAsync(); - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); - await clientTask; + Task serverTask = listener.AcceptConnectionAsync().AsTask(); + await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds); + using QuicConnection serverConnection = serverTask.Result; using QuicStream clientStream = clientConnection.OpenBidirectionalStream(); Assert.Equal(0, clientStream.StreamId); @@ -707,7 +773,7 @@ await Task.Run(async () => byte[] buffer = new byte[100]; QuicConnectionAbortedException ex = await Assert.ThrowsAsync(() => serverStream.ReadAsync(buffer).AsTask()); Assert.Equal(ExpectedErrorCode, ex.ErrorCode); - }).WaitAsync(TimeSpan.FromSeconds(5)); + }).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds)); } [Fact] @@ -733,7 +799,7 @@ await Task.Run(async () => byte[] buffer = new byte[100]; await Assert.ThrowsAsync(() => serverStream.ReadAsync(buffer).AsTask()); - }).WaitAsync(TimeSpan.FromSeconds(5)); + }).WaitAsync(TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds)); } } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs index 9988961e81e5af..9f7010f44d5f0d 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicConnectionTests.cs @@ -13,6 +13,8 @@ public abstract class QuicConnectionTests : QuicTestBase { const int ExpectedErrorCode = 1234; + public QuicConnectionTests(ITestOutputHelper output) : base(output) { } + [Fact] public async Task TestConnect() { @@ -37,23 +39,146 @@ public async Task TestConnect() Assert.Equal(ApplicationProtocol.ToString(), serverConnection.NegotiatedApplicationProtocol.ToString()); } + private static async Task OpenAndUseStreamAsync(QuicConnection c) + { + QuicStream s = c.OpenBidirectionalStream(); + + // This will pend + await s.ReadAsync(new byte[1]); + + return s; + } + [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/55242", TestPlatforms.Linux)] - public async Task AcceptStream_ConnectionAborted_ByClient_Throws() + public async Task CloseAsync_WithPendingAcceptAndConnect_PendingAndSubsequentThrowOperationAbortedException() { using var sync = new SemaphoreSlim(0); await RunClientServer( async clientConnection => { - await clientConnection.CloseAsync(ExpectedErrorCode); + await sync.WaitAsync(); + }, + async serverConnection => + { + // Pend operations before the client closes. + Task acceptTask = serverConnection.AcceptStreamAsync().AsTask(); + Assert.False(acceptTask.IsCompleted); + Task connectTask = OpenAndUseStreamAsync(serverConnection); + Assert.False(connectTask.IsCompleted); + + await serverConnection.CloseAsync(ExpectedErrorCode); + sync.Release(); + + // Pending ops should fail + await Assert.ThrowsAsync(() => acceptTask); + await Assert.ThrowsAsync(() => connectTask); + + // Subsequent attempts should fail + // TODO: Which exception is correct? + if (IsMockProvider) + { + await Assert.ThrowsAsync(async () => await serverConnection.AcceptStreamAsync()); + await Assert.ThrowsAsync(async () => await OpenAndUseStreamAsync(serverConnection)); + } + else + { + await Assert.ThrowsAsync(async () => await serverConnection.AcceptStreamAsync()); + + // TODO: ActiveIssue https://github.com/dotnet/runtime/issues/56133 + // MsQuic fails with System.Net.Quic.QuicException: Failed to open stream to peer. Error Code: INVALID_STATE + //await Assert.ThrowsAsync(async () => await OpenAndUseStreamAsync(serverConnection)); + await Assert.ThrowsAsync(() => OpenAndUseStreamAsync(serverConnection)); + } + }); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55242", TestPlatforms.Linux)] + public async Task Dispose_WithPendingAcceptAndConnect_PendingAndSubsequentThrowOperationAbortedException() + { + using var sync = new SemaphoreSlim(0); + + await RunClientServer( + async clientConnection => + { + await sync.WaitAsync(); }, async serverConnection => + { + // Pend operations before the client closes. + Task acceptTask = serverConnection.AcceptStreamAsync().AsTask(); + Assert.False(acceptTask.IsCompleted); + Task connectTask = OpenAndUseStreamAsync(serverConnection); + Assert.False(connectTask.IsCompleted); + + serverConnection.Dispose(); + + sync.Release(); + + // Pending ops should fail + await Assert.ThrowsAsync(() => acceptTask); + await Assert.ThrowsAsync(() => connectTask); + + // Subsequent attempts should fail + // TODO: Should these be QuicOperationAbortedException, to match above? Or vice-versa? + await Assert.ThrowsAsync(async () => await serverConnection.AcceptStreamAsync()); + await Assert.ThrowsAsync(async () => await OpenAndUseStreamAsync(serverConnection)); + }); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/55242", TestPlatforms.Linux)] + public async Task ConnectionClosedByPeer_WithPendingAcceptAndConnect_PendingAndSubsequentThrowConnectionAbortedException() + { + if (IsMockProvider) + { + return; + } + + using var sync = new SemaphoreSlim(0); + + await RunClientServer( + async clientConnection => { await sync.WaitAsync(); - QuicConnectionAbortedException ex = await Assert.ThrowsAsync(() => serverConnection.AcceptStreamAsync().AsTask()); + + await clientConnection.CloseAsync(ExpectedErrorCode); + }, + async serverConnection => + { + // Pend operations before the client closes. + Task acceptTask = serverConnection.AcceptStreamAsync().AsTask(); + Assert.False(acceptTask.IsCompleted); + Task connectTask = OpenAndUseStreamAsync(serverConnection); + Assert.False(connectTask.IsCompleted); + + sync.Release(); + + // Pending ops should fail + QuicConnectionAbortedException ex; + + ex = await Assert.ThrowsAsync(() => acceptTask); + Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + ex = await Assert.ThrowsAsync(() => connectTask); + Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + + // Subsequent attempts should fail + ex = await Assert.ThrowsAsync(() => serverConnection.AcceptStreamAsync().AsTask()); Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + // TODO: ActiveIssue https://github.com/dotnet/runtime/issues/56133 + // MsQuic fails with System.Net.Quic.QuicException: Failed to open stream to peer. Error Code: INVALID_STATE + if (IsMsQuicProvider) + { + await Assert.ThrowsAsync(() => OpenAndUseStreamAsync(serverConnection)); + } + else + { + ex = await Assert.ThrowsAsync(() => OpenAndUseStreamAsync(serverConnection)); + Assert.Equal(ExpectedErrorCode, ex.ErrorCode); + } }); } @@ -79,7 +204,7 @@ private static async Task DoReads(QuicStream reader, int readCount) [InlineData(10)] public async Task CloseAsync_WithOpenStream_LocalAndPeerStreamsFailWithQuicOperationAbortedException(int writesBeforeClose) { - if (typeof(T) == typeof(MockProviderFactory)) + if (IsMockProvider) { return; } @@ -122,7 +247,7 @@ await RunClientServer( [InlineData(10)] public async Task Dispose_WithOpenLocalStream_LocalStreamFailsWithQuicOperationAbortedException(int writesBeforeClose) { - if (typeof(T) == typeof(MockProviderFactory)) + if (IsMockProvider) { return; } @@ -162,8 +287,14 @@ await RunClientServer( } } - public sealed class QuicConnectionTests_MockProvider : QuicConnectionTests { } + public sealed class QuicConnectionTests_MockProvider : QuicConnectionTests + { + public QuicConnectionTests_MockProvider(ITestOutputHelper output) : base(output) { } + } [ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported))] - public sealed class QuicConnectionTests_MsQuicProvider : QuicConnectionTests { } + public sealed class QuicConnectionTests_MsQuicProvider : QuicConnectionTests + { + public QuicConnectionTests_MsQuicProvider(ITestOutputHelper output) : base(output) { } + } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs index 3eb016bd320521..642f6bd11c4c69 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs @@ -3,12 +3,15 @@ using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace System.Net.Quic.Tests { public abstract class QuicListenerTests : QuicTestBase where T : IQuicImplProviderFactory, new() { + public QuicListenerTests(ITestOutputHelper output) : base(output) { } + [Fact] public async Task Listener_Backlog_Success() { @@ -25,8 +28,14 @@ await Task.Run(async () => } } - public sealed class QuicListenerTests_MockProvider : QuicListenerTests { } + public sealed class QuicListenerTests_MockProvider : QuicListenerTests + { + public QuicListenerTests_MockProvider(ITestOutputHelper output) : base(output) { } + } [ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported))] - public sealed class QuicListenerTests_MsQuicProvider : QuicListenerTests { } + public sealed class QuicListenerTests_MsQuicProvider : QuicListenerTests + { + public QuicListenerTests_MsQuicProvider(ITestOutputHelper output) : base(output) { } + } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs index 4ca6e0e055659c..9ba6c5a46c807e 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamConnectedStreamConformanceTests.cs @@ -4,11 +4,14 @@ using System.Collections.Generic; using System.IO; using System.IO.Tests; +using System.Net.Sockets; using System.Net.Quic.Implementations; using System.Net.Security; using System.Security.Cryptography.X509Certificates; +using System.Threading; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace System.Net.Quic.Tests { @@ -23,11 +26,17 @@ public sealed class MsQuicQuicStreamConformanceTests : QuicStreamConformanceTest protected override QuicImplementationProvider Provider => QuicImplementationProviders.MsQuic; protected override bool UsableAfterCanceledReads => false; protected override bool BlocksOnZeroByteReads => true; + + public MsQuicQuicStreamConformanceTests(ITestOutputHelper output) + { + _output = output; + } } public abstract class QuicStreamConformanceTests : ConnectedStreamConformanceTests { public X509Certificate2 ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate(); + public ITestOutputHelper _output; public bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { @@ -75,21 +84,31 @@ await WhenAllOrAnyFailed( }), Task.Run(async () => { - connection2 = new QuicConnection( - provider, - listener.ListenEndPoint, - GetSslClientAuthenticationOptions()); - await connection2.ConnectAsync(); - stream2 = connection2.OpenBidirectionalStream(); - // OpenBidirectionalStream only allocates ID. We will force stream opening - // by Writing there and receiving data on the other side. - await stream2.WriteAsync(buffer); + try + { + connection2 = new QuicConnection( + provider, + listener.ListenEndPoint, + GetSslClientAuthenticationOptions()); + await connection2.ConnectAsync(); + stream2 = connection2.OpenBidirectionalStream(); + // OpenBidirectionalStream only allocates ID. We will force stream opening + // by Writing there and receiving data on the other side. + await stream2.WriteAsync(buffer); + } + catch (Exception ex) + { + _output?.WriteLine($"Failed to {ex.Message}"); + throw; + } })); + // No need to keep the listener once we have connected connection and streams + listener.Dispose(); + var result = new StreamPairWithOtherDisposables(stream1, stream2); result.Disposables.Add(connection1); result.Disposables.Add(connection2); - result.Disposables.Add(listener); return result; } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index af45e791a48b96..40b43fd4dc952b 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; namespace System.Net.Quic.Tests { @@ -16,6 +17,7 @@ public abstract class QuicStreamTests : QuicTestBase where T : IQuicImplProviderFactory, new() { private static byte[] s_data = Encoding.UTF8.GetBytes("Hello world!"); + public QuicStreamTests(ITestOutputHelper output) : base(output) { } [Fact] public async Task BasicTest() @@ -55,6 +57,8 @@ await RunClientServer( [Fact] public async Task MultipleReadsAndWrites() { + using CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(PassingTestTimeout); const int sendCount = 5; int expectedBytesCount = s_data.Length * sendCount; byte[] expected = new byte[expectedBytesCount]; @@ -69,7 +73,7 @@ await RunClientServer( iterations: 100, serverFunction: async connection => { - await using QuicStream stream = await connection.AcceptStreamAsync(); + await using QuicStream stream = await connection.AcceptStreamAsync(cts.Token); byte[] buffer = new byte[expectedBytesCount]; int bytesRead = await ReadAll(stream, buffer); @@ -80,9 +84,9 @@ await RunClientServer( { await stream.WriteAsync(s_data); } - await stream.WriteAsync(Memory.Empty, endStream: true); + await stream.WriteAsync(Memory.Empty, endStream: true, cts.Token); - await stream.ShutdownCompleted(); + await stream.ShutdownCompleted(cts.Token); }, clientFunction: async connection => { @@ -90,16 +94,16 @@ await RunClientServer( for (int i = 0; i < sendCount; i++) { - await stream.WriteAsync(s_data); + await stream.WriteAsync(s_data, cts.Token); } - await stream.WriteAsync(Memory.Empty, endStream: true); + await stream.WriteAsync(Memory.Empty, endStream: true, cts.Token); byte[] buffer = new byte[expectedBytesCount]; int bytesRead = await ReadAll(stream, buffer); Assert.Equal(expectedBytesCount, bytesRead); Assert.Equal(expected, buffer); - await stream.ShutdownCompleted(); + await stream.ShutdownCompleted(cts.Token); } ); } @@ -677,8 +681,14 @@ async Task ReadUntilAborted() } } - public sealed class QuicStreamTests_MockProvider : QuicStreamTests { } + public sealed class QuicStreamTests_MockProvider : QuicStreamTests + { + public QuicStreamTests_MockProvider(ITestOutputHelper output) : base(output) { } + } [ConditionalClass(typeof(QuicTestBase), nameof(QuicTestBase.IsSupported))] - public sealed class QuicStreamTests_MsQuicProvider : QuicStreamTests { } + public sealed class QuicStreamTests_MsQuicProvider : QuicStreamTests + { + public QuicStreamTests_MsQuicProvider(ITestOutputHelper output) : base(output) { } + } } diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs index 9a6e43a863e5b1..2a03bca1181635 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicTestBase.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Net.Quic.Implementations; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -9,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Xunit; +using Xunit.Abstractions; using System.Diagnostics.Tracing; namespace System.Net.Quic.Tests @@ -23,14 +25,22 @@ public abstract class QuicTestBase public static QuicImplementationProvider ImplementationProvider { get; } = s_factory.GetProvider(); public static bool IsSupported => ImplementationProvider.IsSupported; + public static bool IsMockProvider => typeof(T) == typeof(MockProviderFactory); + public static bool IsMsQuicProvider => typeof(T) == typeof(MsQuicProviderFactory); + public static SslApplicationProtocol ApplicationProtocol { get; } = new SslApplicationProtocol("quictest"); public X509Certificate2 ServerCertificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate(); public X509Certificate2 ClientCertificate = System.Net.Test.Common.Configuration.Certificates.GetClientCertificate(); + public ITestOutputHelper _output; public const int PassingTestTimeoutMilliseconds = 4 * 60 * 1000; public static TimeSpan PassingTestTimeout => TimeSpan.FromMilliseconds(PassingTestTimeoutMilliseconds); + public QuicTestBase(ITestOutputHelper output) + { + _output = output; + } public bool RemoteCertificateValidationCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors) { Assert.Equal(ServerCertificate.GetCertHash(), certificate?.GetCertHash()); @@ -89,6 +99,17 @@ internal QuicListener CreateQuicListener(IPEndPoint endpoint) return CreateQuicListener(options); } + internal async Task<(QuicConnection, QuicConnection)> CreateConnectedQuicConnection() + { + using QuicListener listener = CreateQuicListener(); + QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); + + ValueTask clientTask = clientConnection.ConnectAsync(); + ValueTask serverTask = listener.AcceptConnectionAsync(); + await new Task[] { clientTask.AsTask(), serverTask.AsTask() }.WhenAllOrAnyFailed(PassingTestTimeoutMilliseconds); + return (clientConnection, serverTask.Result); + } + internal async Task PingPong(QuicConnection client, QuicConnection server) { using QuicStream clientStream = client.OpenBidirectionalStream(); @@ -121,7 +142,7 @@ internal async Task PingPong(QuicConnection client, QuicConnection server) private QuicListener CreateQuicListener(QuicListenerOptions options) => new QuicListener(ImplementationProvider, options); - internal async Task RunClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 30_000, QuicListenerOptions listenerOptions = null) + internal async Task RunClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = PassingTestTimeoutMilliseconds, QuicListenerOptions listenerOptions = null) { const long ClientCloseErrorCode = 11111; const long ServerCloseErrorCode = 22222; @@ -137,7 +158,7 @@ await new[] { Task.Run(async () => { - using QuicConnection serverConnection = await listener.AcceptConnectionAsync(); + using QuicConnection serverConnection = await listener.AcceptConnectionAsync().AsTask().WaitAsync(millisecondsTimeout); await serverFunction(serverConnection); serverFinished.Release(); @@ -147,7 +168,16 @@ await new[] Task.Run(async () => { using QuicConnection clientConnection = CreateQuicConnection(listener.ListenEndPoint); - await clientConnection.ConnectAsync(); + try + { + await clientConnection.ConnectAsync(); + } + catch (Exception ex) + { + _output?.WriteLine("Failed to connect {0} with {1}", listener.ListenEndPoint, ex.Message); + throw; + } + await clientFunction(clientConnection); clientFinished.Release(); @@ -190,10 +220,10 @@ await RunClientServer( ); } - internal Task RunBidirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 30_000) + internal Task RunBidirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = PassingTestTimeoutMilliseconds) => RunStreamClientServer(clientFunction, serverFunction, bidi: true, iterations, millisecondsTimeout); - internal Task RunUnirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = 30_000) + internal Task RunUnirectionalClientServer(Func clientFunction, Func serverFunction, int iterations = 1, int millisecondsTimeout = PassingTestTimeoutMilliseconds) => RunStreamClientServer(clientFunction, serverFunction, bidi: false, iterations, millisecondsTimeout); internal static async Task ReadAll(QuicStream stream, byte[] buffer) 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 157d9f4f7a51d7..74dde855abe7e8 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs @@ -967,15 +967,11 @@ private Exception TranslateConnectException(Exception e) private async void CreateConnectionAsync() { - string hostname = _uri.Host; - int port = _uri.Port; - - TcpClient client = new TcpClient(); - object result; try { - await client.ConnectAsync(hostname, port).ConfigureAwait(false); + var client = new TcpClient(); + await client.ConnectAsync(_uri.Host, _uri.Port).ConfigureAwait(false); result = new FtpControlStream(client); } catch (Exception e) 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 ca89fca29fa5ba..60c21f69a2cfde 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -144,6 +144,8 @@ + if (status.ErrorCode == SecurityStatusPalErrorCode.TryAgain) { // No need to hold on the buffer any more. - ArrayPool.Shared.Return(bufferToReturn); + byte[] tmp = bufferToReturn; bufferToReturn = null; + ArrayPool.Shared.Return(tmp); + // Call WriteSingleChunk() recursively to avoid code duplication. // This should be extremely rare in cases when second renegotiation happens concurrently with Write. await WriteSingleChunk(writeAdapter, buffer).ConfigureAwait(false); @@ -788,14 +790,12 @@ async ValueTask CompleteWriteAsync(ValueTask writeTask, byte[] bufferToReturn) // actually contains no decrypted or encrypted bytes private void ReturnReadBufferIfEmpty() { - if (_internalBuffer != null && _decryptedBytesCount == 0 && _internalBufferCount == 0) + if (_internalBuffer is byte[] internalBuffer && _decryptedBytesCount == 0 && _internalBufferCount == 0) { - ArrayPool.Shared.Return(_internalBuffer); _internalBuffer = null; - _internalBufferCount = 0; _internalOffset = 0; - _decryptedBytesCount = 0; _decryptedBytesOffset = 0; + ArrayPool.Shared.Return(internalBuffer); } else if (_decryptedBytesCount == 0) { diff --git a/src/libraries/System.Net.Security/tests/StressTests/SslStress/StressOperations.cs b/src/libraries/System.Net.Security/tests/StressTests/SslStress/StressOperations.cs index 8c2ec6f35e49d8..eea046d392fb97 100644 --- a/src/libraries/System.Net.Security/tests/StressTests/SslStress/StressOperations.cs +++ b/src/libraries/System.Net.Security/tests/StressTests/SslStress/StressOperations.cs @@ -24,7 +24,7 @@ namespace SslStress { public struct DataSegment { - private readonly byte[] _buffer; + private byte[] _buffer; public DataSegment(int length) { @@ -37,7 +37,12 @@ public DataSegment(int length) public Span AsSpan() => new Span(_buffer, 0, Length); public ulong Checksum => CRC.CalculateCRC(AsSpan()); - public void Return() => ArrayPool.Shared.Return(_buffer); + public void Return() + { + byte[] toReturn = _buffer; + _buffer = null; + ArrayPool.Shared.Return(toReturn); + } /// Create and populate a segment with random data public static DataSegment CreateRandom(Random random, int maxLength) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index 01c6b48ed4de6b..69c8b9b28a64c1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -84,7 +84,6 @@ public async Task Connect_MultipleIPAddresses_Success(IPAddress listenAt) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/55053", TestPlatforms.Linux)] public async Task Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_Success() { if (!SupportsMultiConnect) @@ -96,21 +95,14 @@ public async Task Connect_DualMode_MultiAddressFamilyConnect_RetrievedEndPoints_ { Assert.True(client.DualMode); - Task connectTask = MultiConnectAsync(client, new IPAddress[] { IPAddress.IPv6Loopback, IPAddress.Loopback }, port); - await connectTask; - - var localEndPoint = client.LocalEndPoint as IPEndPoint; - Assert.NotNull(localEndPoint); - Assert.Equal(IPAddress.Loopback.MapToIPv6(), localEndPoint.Address); + await MultiConnectAsync(client, new IPAddress[] { IPAddress.IPv6Loopback, IPAddress.Loopback }, port); - var remoteEndPoint = client.RemoteEndPoint as IPEndPoint; - Assert.NotNull(remoteEndPoint); - Assert.Equal(IPAddress.Loopback.MapToIPv6(), remoteEndPoint.Address); + CheckIsIpv6LoopbackEndPoint(client.LocalEndPoint); + CheckIsIpv6LoopbackEndPoint(client.RemoteEndPoint); } } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/54677", TestPlatforms.Linux)] [ActiveIssue("https://github.com/dotnet/runtime/issues/55709", TestPlatforms.Linux)] public async Task Connect_DualMode_DnsConnect_RetrievedEndPoints_Success() { @@ -127,19 +119,20 @@ public async Task Connect_DualMode_DnsConnect_RetrievedEndPoints_Success() { Assert.True(client.DualMode); - Task connectTask = ConnectAsync(client, new DnsEndPoint("localhost", port)); - await connectTask; - - var localEndPoint = client.LocalEndPoint as IPEndPoint; - Assert.NotNull(localEndPoint); - Assert.True(localEndPoint.Address.Equals(IPAddress.IPv6Loopback) || localEndPoint.Address.Equals(IPAddress.Loopback.MapToIPv6())); + await ConnectAsync(client, new DnsEndPoint("localhost", port)); - var remoteEndPoint = client.RemoteEndPoint as IPEndPoint; - Assert.NotNull(remoteEndPoint); - Assert.Equal(IPAddress.Loopback.MapToIPv6(), remoteEndPoint.Address); + CheckIsIpv6LoopbackEndPoint(client.LocalEndPoint); + CheckIsIpv6LoopbackEndPoint(client.RemoteEndPoint); } } + private static void CheckIsIpv6LoopbackEndPoint(EndPoint endPoint) + { + IPEndPoint ep = endPoint as IPEndPoint; + Assert.NotNull(ep); + Assert.True(ep.Address.Equals(IPAddress.IPv6Loopback) || ep.Address.Equals(IPAddress.Loopback.MapToIPv6())); + } + [Fact] public async Task Connect_OnConnectedSocket_Fails() { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs index eb229d9d37e3f4..d537025bdc5fe9 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DualModeSocketTest.cs @@ -495,42 +495,36 @@ public void Socket_ConnectAsyncV4IPEndPointToV4Host_Throws() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void ConnectAsyncV4IPEndPointToV4Host_Success() { DualModeConnectAsync_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.Loopback, false); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void ConnectAsyncV6IPEndPointToV6Host_Success() { DualModeConnectAsync_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback, false); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void ConnectAsyncV4IPEndPointToV6Host_Fails() { DualModeConnectAsync_IPEndPointToHost_Fails_Helper(IPAddress.Loopback, IPAddress.IPv6Loopback); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void ConnectAsyncV6IPEndPointToV4Host_Fails() { DualModeConnectAsync_IPEndPointToHost_Fails_Helper(IPAddress.IPv6Loopback, IPAddress.Loopback); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void ConnectAsyncV4IPEndPointToDualHost_Success() { DualModeConnectAsync_IPEndPointToHost_Helper(IPAddress.Loopback, IPAddress.IPv6Any, true); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void ConnectAsyncV6IPEndPointToDualHost_Success() { DualModeConnectAsync_IPEndPointToHost_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Any, true); @@ -889,21 +883,18 @@ private void DualModeConnect_BeginAccept_Helper(IPAddress listenOn, IPAddress co public class DualModeAcceptAsync : DualModeBase { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void AcceptAsyncV4BoundToSpecificV4_Success() { DualModeConnect_AcceptAsync_Helper(IPAddress.Loopback, IPAddress.Loopback); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void AcceptAsyncV4BoundToAnyV4_Success() { DualModeConnect_AcceptAsync_Helper(IPAddress.Any, IPAddress.Loopback); } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void AcceptAsyncV6BoundToSpecificV6_Success() { DualModeConnect_AcceptAsync_Helper(IPAddress.IPv6Loopback, IPAddress.IPv6Loopback); @@ -946,7 +937,6 @@ public void AcceptAsyncV6BoundToAnyV4_CantConnect() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void AcceptAsyncV4BoundToAnyV6_Success() { DualModeConnect_AcceptAsync_Helper(IPAddress.IPv6Any, IPAddress.Loopback); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs index d0b7ba9bb09fec..443924b66d2b44 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/IPPacketInformationTest.cs @@ -24,7 +24,6 @@ public void GetHashCode_DefaultValues_Success() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void Equals_NonDefaultValue_Success() { IPPacketInformation packetInfo = GetNonDefaultIPPacketInformation(); @@ -42,7 +41,6 @@ public void Equals_NonDefaultValue_Success() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public void GetHashCode_NonDefaultValue_Succes() { IPPacketInformation packetInfo = GetNonDefaultIPPacketInformation(); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs index 10e7ee6bf1572c..134c80d9cf3e14 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/LoggingTest.cs @@ -19,7 +19,6 @@ public LoggingTest(ITestOutputHelper output) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public static void EventSource_ExistsWithCorrectId() { Type esType = typeof(Socket).Assembly.GetType("System.Net.NetEventSource", throwOnError: true, ignoreCase: false); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index efac4a735b74f3..fbe9102f43265b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -310,6 +310,7 @@ public void EndReceiveFrom_AddressFamilyDoesNotMatch_Throws_ArgumentException() } [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/54418", TestPlatforms.MacCatalyst)] public void BeginReceiveFrom_RemoteEpIsReturnedWhenCompletedSynchronously() { EndPoint anyEp = new IPEndPoint(IPAddress.Any, 0); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index acd9029136eb7c..c8047e05d5f8fd 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -23,7 +23,6 @@ public TelemetryTest(ITestOutputHelper output) } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50568", TestPlatforms.Android)] public static void EventSource_ExistsWithCorrectId() { Type esType = typeof(Socket).Assembly.GetType("System.Net.Sockets.SocketsTelemetry", throwOnError: true, ignoreCase: false); diff --git a/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs b/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs index 553bf48943d5a7..d23488f44c02ce 100644 --- a/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs +++ b/src/libraries/System.Net.WebClient/src/System/Net/WebClient.cs @@ -143,7 +143,7 @@ public Encoding Encoding get { return _encoding; } set { - ArgumentNullException.ThrowIfNull(value, nameof(value)); + ArgumentNullException.ThrowIfNull(value); _encoding = value; } } @@ -280,7 +280,7 @@ public byte[] DownloadData(string address) => public byte[] DownloadData(Uri address) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); StartOperation(); try @@ -320,8 +320,8 @@ public void DownloadFile(string address, string fileName) => public void DownloadFile(Uri address, string fileName) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(fileName); WebRequest? request = null; FileStream? fs = null; @@ -359,7 +359,7 @@ public Stream OpenRead(string address) => public Stream OpenRead(Uri address) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); WebRequest? request = null; StartOperation(); @@ -392,7 +392,7 @@ public Stream OpenWrite(string address, string? method) => public Stream OpenWrite(Uri address, string? method) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); if (method == null) { method = MapToDefaultMethod(address); @@ -432,8 +432,8 @@ public byte[] UploadData(string address, string? method, byte[] data) => public byte[] UploadData(Uri address, string? method, byte[] data) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(data); if (method == null) { method = MapToDefaultMethod(address); @@ -553,8 +553,8 @@ public byte[] UploadFile(string address, string? method, string fileName) => public byte[] UploadFile(Uri address, string? method, string fileName) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(fileName); if (method == null) { method = MapToDefaultMethod(address); @@ -626,8 +626,8 @@ public byte[] UploadValues(string address, string? method, NameValueCollection d public byte[] UploadValues(Uri address, string? method, NameValueCollection data) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(data); if (method == null) { method = MapToDefaultMethod(address); @@ -665,8 +665,8 @@ public string UploadString(string address, string? method, string data) => public string UploadString(Uri address, string? method, string data) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(data); if (method == null) { method = MapToDefaultMethod(address); @@ -691,7 +691,7 @@ public string DownloadString(string address) => public string DownloadString(Uri address) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); StartOperation(); try @@ -781,7 +781,7 @@ private void CopyHeadersTo(WebRequest request) private Uri GetUri(string address) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); Uri? uri; if (_baseAddress != null) @@ -801,7 +801,7 @@ private Uri GetUri(string address) private Uri GetUri(Uri address) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); Uri? uri = address; @@ -1297,7 +1297,7 @@ public void OpenReadAsync(Uri address) => public void OpenReadAsync(Uri address, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); AsyncOperation asyncOp = StartAsyncOperation(userToken); try @@ -1335,7 +1335,7 @@ public void OpenWriteAsync(Uri address, string? method) => public void OpenWriteAsync(Uri address, string? method, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); if (method == null) { method = MapToDefaultMethod(address); @@ -1396,7 +1396,7 @@ public void DownloadStringAsync(Uri address) => public void DownloadStringAsync(Uri address, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); AsyncOperation asyncOp = StartAsyncOperation(userToken); try @@ -1422,7 +1422,7 @@ public void DownloadDataAsync(Uri address) => public void DownloadDataAsync(Uri address, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); + ArgumentNullException.ThrowIfNull(address); AsyncOperation asyncOp = StartAsyncOperation(userToken); try @@ -1448,8 +1448,8 @@ public void DownloadFileAsync(Uri address, string fileName) => public void DownloadFileAsync(Uri address, string fileName, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(fileName); FileStream? fs = null; AsyncOperation asyncOp = StartAsyncOperation(userToken); @@ -1474,8 +1474,8 @@ public void UploadStringAsync(Uri address, string? method, string data) => public void UploadStringAsync(Uri address, string? method, string data, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(data); if (method == null) { method = MapToDefaultMethod(address); @@ -1525,8 +1525,8 @@ public void UploadDataAsync(Uri address, string? method, byte[] data) => public void UploadDataAsync(Uri address, string? method, byte[] data, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(data); if (method == null) { method = MapToDefaultMethod(address); @@ -1566,8 +1566,8 @@ public void UploadFileAsync(Uri address, string? method, string fileName) => public void UploadFileAsync(Uri address, string? method, string fileName, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(fileName, nameof(fileName)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(fileName); if (method == null) { method = MapToDefaultMethod(address); @@ -1605,8 +1605,8 @@ public void UploadValuesAsync(Uri address, string? method, NameValueCollection d public void UploadValuesAsync(Uri address, string? method, NameValueCollection data, object? userToken) { - ArgumentNullException.ThrowIfNull(address, nameof(address)); - ArgumentNullException.ThrowIfNull(data, nameof(data)); + ArgumentNullException.ThrowIfNull(address); + ArgumentNullException.ThrowIfNull(data); if (method == null) { method = MapToDefaultMethod(address); diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index 6266f01e8ede53..f5d86dfff25ba8 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -320,6 +320,7 @@ await SendAsync( [OuterLoop("Uses external servers", typeof(PlatformDetection), nameof(PlatformDetection.LocalEchoServerIsNotAvailable))] [ConditionalTheory(nameof(WebSocketsSupported)), MemberData(nameof(EchoServers))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53957", TestPlatforms.Browser)] public async Task SendReceive_VaryingLengthBuffers_Success(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs index f3ecf278886d34..7d8eb832c2e2ad 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketDeflater.cs @@ -35,10 +35,10 @@ public void Dispose() public void ReleaseBuffer() { - if (_buffer is not null) + if (_buffer is byte[] toReturn) { - ArrayPool.Shared.Return(_buffer); _buffer = null; + ArrayPool.Shared.Return(toReturn); } } @@ -70,8 +70,11 @@ public ReadOnlySpan Deflate(ReadOnlySpan payload, bool endOfMessage) // Rent a 30% bigger buffer byte[] newBuffer = ArrayPool.Shared.Rent((int)(_buffer.Length * 1.3)); _buffer.AsSpan(0, position).CopyTo(newBuffer); - ArrayPool.Shared.Return(_buffer); + + byte[] toReturn = _buffer; _buffer = newBuffer; + + ArrayPool.Shared.Return(toReturn); } return new ReadOnlySpan(_buffer, 0, position); diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs index d47e646fa60ea6..5f580e9c9fdb3c 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/Compression/WebSocketInflater.cs @@ -111,9 +111,11 @@ public void AddBytes(int totalBytesReceived, bool endOfMessage) { byte[] newBuffer = ArrayPool.Shared.Rent(_available + FlushMarkerLength); _buffer.AsSpan(0, _available).CopyTo(newBuffer); - ArrayPool.Shared.Return(_buffer); + byte[] toReturn = _buffer; _buffer = newBuffer; + + ArrayPool.Shared.Return(toReturn); } FlushMarker.CopyTo(_buffer.AsSpan(_available)); @@ -202,12 +204,13 @@ private unsafe bool Finish(Span output, ref int written) private void ReleaseBuffer() { - if (_buffer is not null) + if (_buffer is byte[] toReturn) { - ArrayPool.Shared.Return(_buffer); _buffer = null; _available = 0; _position = 0; + + ArrayPool.Shared.Return(toReturn); } } diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs index 864b7425617963..297b4c5c4703b9 100644 --- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs +++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs @@ -1452,11 +1452,10 @@ private void ReleaseSendBuffer() { Debug.Assert(_sendFrameAsyncLock.CurrentCount == 0, "Caller should hold the _sendFrameAsyncLock"); - byte[]? old = _sendBuffer; - if (old != null) + if (_sendBuffer is byte[] toReturn) { _sendBuffer = null; - ArrayPool.Shared.Return(old); + ArrayPool.Shared.Return(toReturn); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs index ec23d53df0431d..a82372b92b34db 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs @@ -59,10 +59,11 @@ public ReadOnlySpan AsSpan() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (_arrayFromPool != null) + T[]? toReturn = _arrayFromPool; + if (toReturn != null) { - ArrayPool.Shared.Return(_arrayFromPool); _arrayFromPool = null; + ArrayPool.Shared.Return(toReturn); } } 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 87d09e16679ed7..556e4e8df58fe2 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 @@ -2127,7 +2127,7 @@ internal unsafe void DispatchToAllListeners(EventWrittenEventArgs eventCallbackA } } - if (lastThrownException != null) + if (lastThrownException != null && ThrowOnEventWriteErrors) { throw new EventSourceException(lastThrownException); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 041bc5c0ce04b1..1495d2a4ccfaf3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -398,25 +398,25 @@ private static bool TryParseExactD(ReadOnlySpan guidString, ref GuidResult } Span bytes = MemoryMarshal.AsBytes(new Span(ref result, 1)); - uint invalidIfFF = 0; - bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfFF); - bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfFF); - bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfFF); - bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfFF); - bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfFF); - bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfFF); - bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfFF); - bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfFF); - bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfFF); - bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfFF); - bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfFF); - bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfFF); - bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfFF); - bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfFF); - bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfFF); - bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfFF); - - if (invalidIfFF != 0xFF) + int invalidIfNegative = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfNegative); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfNegative); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfNegative); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfNegative); + bytes[4] = DecodeByte(guidString[11], guidString[12], ref invalidIfNegative); + bytes[5] = DecodeByte(guidString[9], guidString[10], ref invalidIfNegative); + bytes[6] = DecodeByte(guidString[16], guidString[17], ref invalidIfNegative); + bytes[7] = DecodeByte(guidString[14], guidString[15], ref invalidIfNegative); + bytes[8] = DecodeByte(guidString[19], guidString[20], ref invalidIfNegative); + bytes[9] = DecodeByte(guidString[21], guidString[22], ref invalidIfNegative); + bytes[10] = DecodeByte(guidString[24], guidString[25], ref invalidIfNegative); + bytes[11] = DecodeByte(guidString[26], guidString[27], ref invalidIfNegative); + bytes[12] = DecodeByte(guidString[28], guidString[29], ref invalidIfNegative); + bytes[13] = DecodeByte(guidString[30], guidString[31], ref invalidIfNegative); + bytes[14] = DecodeByte(guidString[32], guidString[33], ref invalidIfNegative); + bytes[15] = DecodeByte(guidString[34], guidString[35], ref invalidIfNegative); + + if (invalidIfNegative >= 0) { if (!BitConverter.IsLittleEndian) { @@ -484,25 +484,25 @@ private static bool TryParseExactN(ReadOnlySpan guidString, ref GuidResult } Span bytes = MemoryMarshal.AsBytes(new Span(ref result, 1)); - uint invalidIfFF = 0; - bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfFF); - bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfFF); - bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfFF); - bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfFF); - bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfFF); - bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfFF); - bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfFF); - bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfFF); - bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfFF); - bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfFF); - bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfFF); - bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfFF); - bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfFF); - bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfFF); - bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfFF); - bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfFF); - - if (invalidIfFF != 0xFF) + int invalidIfNegative = 0; + bytes[0] = DecodeByte(guidString[6], guidString[7], ref invalidIfNegative); + bytes[1] = DecodeByte(guidString[4], guidString[5], ref invalidIfNegative); + bytes[2] = DecodeByte(guidString[2], guidString[3], ref invalidIfNegative); + bytes[3] = DecodeByte(guidString[0], guidString[1], ref invalidIfNegative); + bytes[4] = DecodeByte(guidString[10], guidString[11], ref invalidIfNegative); + bytes[5] = DecodeByte(guidString[8], guidString[9], ref invalidIfNegative); + bytes[6] = DecodeByte(guidString[14], guidString[15], ref invalidIfNegative); + bytes[7] = DecodeByte(guidString[12], guidString[13], ref invalidIfNegative); + bytes[8] = DecodeByte(guidString[16], guidString[17], ref invalidIfNegative); + bytes[9] = DecodeByte(guidString[18], guidString[19], ref invalidIfNegative); + bytes[10] = DecodeByte(guidString[20], guidString[21], ref invalidIfNegative); + bytes[11] = DecodeByte(guidString[22], guidString[23], ref invalidIfNegative); + bytes[12] = DecodeByte(guidString[24], guidString[25], ref invalidIfNegative); + bytes[13] = DecodeByte(guidString[26], guidString[27], ref invalidIfNegative); + bytes[14] = DecodeByte(guidString[28], guidString[29], ref invalidIfNegative); + bytes[15] = DecodeByte(guidString[30], guidString[31], ref invalidIfNegative); + + if (invalidIfNegative >= 0) { if (!BitConverter.IsLittleEndian) { @@ -695,21 +695,29 @@ private static bool TryParseExactX(ReadOnlySpan guidString, ref GuidResult } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static byte DecodeByte(char ch1, char ch2, ref uint invalidIfFF) + private static byte DecodeByte(nuint ch1, nuint ch2, ref int invalidIfNegative) { // TODO https://github.com/dotnet/runtime/issues/13464: // Replace the Unsafe.Add with HexConverter.FromChar once the bounds checks are eliminated. - uint h1 = 0xFF; - if (ch1 < HexConverter.CharToHexLookup.Length) - h1 = Unsafe.Add(ref MemoryMarshal.GetReference(HexConverter.CharToHexLookup), ch1); + ReadOnlySpan lookup = HexConverter.CharToHexLookup; - uint h2 = 0xFF; - if (ch2 < HexConverter.CharToHexLookup.Length) - h2 = Unsafe.Add(ref MemoryMarshal.GetReference(HexConverter.CharToHexLookup), ch2); + int h1 = -1; + if (ch1 < (nuint)lookup.Length) + { + h1 = (sbyte)Unsafe.Add(ref MemoryMarshal.GetReference(lookup), (nint)ch1); + } + h1 <<= 4; + + int h2 = -1; + if (ch2 < (nuint)lookup.Length) + { + h2 = (sbyte)Unsafe.Add(ref MemoryMarshal.GetReference(lookup), (nint)ch2); + } - invalidIfFF |= h1 | h2; - return (byte)(h1 << 4 | h2); + int result = h1 | h2; + invalidIfNegative |= result; + return (byte)result; } private static bool TryParseHex(ReadOnlySpan guidString, out ushort result, ref bool overflow) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs index 04f8282736a44c..a2c83ab03d65ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerator.Unix.cs @@ -236,12 +236,17 @@ private void InternalDispose(bool disposing) CloseDirectoryHandle(); - if (_pathBuffer != null) - ArrayPool.Shared.Return(_pathBuffer); - _pathBuffer = null; - if (_entryBuffer != null) - ArrayPool.Shared.Return(_entryBuffer); - _entryBuffer = null; + if (_pathBuffer is char[] pathBuffer) + { + _pathBuffer = null; + ArrayPool.Shared.Return(pathBuffer); + } + + if (_entryBuffer is byte[] entryBuffer) + { + _entryBuffer = null; + ArrayPool.Shared.Return(entryBuffer); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 668e78ccd255a1..ff2a7bd9ef6286 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -796,8 +796,11 @@ private static async Task InternalReadAllBytesUnknownLengthAsync(FileStr byte[] tmp = ArrayPool.Shared.Rent((int)newLength); Buffer.BlockCopy(rentedArray, 0, tmp, 0, bytesRead); - ArrayPool.Shared.Return(rentedArray); + + byte[] toReturn = rentedArray; rentedArray = tmp; + + ArrayPool.Shared.Return(toReturn); } Debug.Assert(bytesRead < rentedArray.Length); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index 698e3bc57245e8..191106a8de229e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -568,8 +568,9 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i // the return value is the required buffer size, in TCHARs. This value includes the size of the terminating null character. if (result > buffer.Length) { - ArrayPool.Shared.Return(buffer); + char[] toReturn = buffer; buffer = ArrayPool.Shared.Rent((int)result); + ArrayPool.Shared.Return(toReturn); result = GetFinalPathNameByHandle(handle, buffer); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs index 5f4b8e11a19206..4d08cd9f241275 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs @@ -50,16 +50,7 @@ public static string GetFullPath(string path) if (path.Contains('\0')) throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path)); - if (PathInternal.IsExtended(path.AsSpan())) - { - // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\ - // paths and neither should we. Even if we wanted to GetFullPathName does not work - // properly with device paths. If one wants to pass a \\?\ path through normalization - // one can chop off the prefix, pass it to GetFullPath and add it again. - return path; - } - - return PathHelper.Normalize(path); + return GetFullyQualifiedPath(path); } public static string GetFullPath(string path, string basePath) @@ -77,7 +68,7 @@ public static string GetFullPath(string path, string basePath) throw new ArgumentException(SR.Argument_InvalidPathChars); if (IsPathFullyQualified(path)) - return GetFullPath(path); + return GetFullyQualifiedPath(path); if (PathInternal.IsEffectivelyEmpty(path.AsSpan())) return basePath; @@ -129,7 +120,21 @@ public static string GetFullPath(string path, string basePath) return PathInternal.IsDevice(combinedPath.AsSpan()) ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath.AsSpan())) - : GetFullPath(combinedPath); + : GetFullyQualifiedPath(combinedPath); + } + + internal static string GetFullyQualifiedPath(string path) + { + if (PathInternal.IsExtended(path.AsSpan())) + { + // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\ + // paths and neither should we. Even if we wanted to GetFullPathName does not work + // properly with device paths. If one wants to pass a \\?\ path through normalization + // one can chop off the prefix, pass it to GetFullPath and add it again. + return path; + } + + return PathHelper.Normalize(path); } public static string GetTempPath() diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index 0f59f6748a1a43..13e6bd7b867c9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -50,6 +50,12 @@ private unsafe ValueTask ReadAsyncInternal(Memory destination, Cancel // touch the file pointer location at all. We will adjust it // ourselves, but only in memory. This isn't threadsafe. _filePosition += destination.Length; + + // We know for sure that there is nothing to read, so we just return here and avoid a sys-call. + if (destination.IsEmpty && LengthCachingSupported) + { + return ValueTask.FromResult(0); + } } (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) = RandomAccess.QueueAsyncReadFile(_fileHandle, destination, positionBefore, cancellationToken); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs index 7652fc768c1f35..c6115ef04dfa87 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs @@ -115,7 +115,7 @@ protected void UpdateLengthOnChangePosition() } } - private bool LengthCachingSupported => OperatingSystem.IsWindows() && _share <= FileShare.Read && !_exposedHandle; + protected bool LengthCachingSupported => OperatingSystem.IsWindows() && _share <= FileShare.Read && !_exposedHandle; /// Gets or sets the position within the current stream public sealed override long Position diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs index 1ac8ecb60c91d1..bf2b0d1d1cb3c7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.Blocking.cs @@ -12,7 +12,7 @@ public short MinThreadsGoal get { _threadAdjustmentLock.VerifyIsLocked(); - return Math.Min(_separated.numThreadsGoal, TargetThreadsGoalForBlockingAdjustment); + return Math.Min(_separated.counts.NumThreadsGoal, TargetThreadsGoalForBlockingAdjustment); } } @@ -44,7 +44,7 @@ public bool NotifyThreadBlocked() Debug.Assert(_numBlockedThreads > 0); if (_pendingBlockingAdjustment != PendingBlockingAdjustment.WithDelayIfNecessary && - _separated.numThreadsGoal < TargetThreadsGoalForBlockingAdjustment) + _separated.counts.NumThreadsGoal < TargetThreadsGoalForBlockingAdjustment) { if (_pendingBlockingAdjustment == PendingBlockingAdjustment.None) { @@ -79,7 +79,7 @@ public void NotifyThreadUnblocked() if (_pendingBlockingAdjustment != PendingBlockingAdjustment.Immediately && _numThreadsAddedDueToBlocking > 0 && - _separated.numThreadsGoal > TargetThreadsGoalForBlockingAdjustment) + _separated.counts.NumThreadsGoal > TargetThreadsGoalForBlockingAdjustment) { wakeGateThread = true; _pendingBlockingAdjustment = PendingBlockingAdjustment.Immediately; @@ -126,7 +126,8 @@ private uint PerformBlockingAdjustment(bool previousDelayElapsed, out bool addWo addWorker = false; short targetThreadsGoal = TargetThreadsGoalForBlockingAdjustment; - short numThreadsGoal = _separated.numThreadsGoal; + ThreadCounts counts = _separated.counts; + short numThreadsGoal = counts.NumThreadsGoal; if (numThreadsGoal == targetThreadsGoal) { return 0; @@ -144,7 +145,8 @@ private uint PerformBlockingAdjustment(bool previousDelayElapsed, out bool addWo short toSubtract = Math.Min((short)(numThreadsGoal - targetThreadsGoal), _numThreadsAddedDueToBlocking); _numThreadsAddedDueToBlocking -= toSubtract; - _separated.numThreadsGoal = numThreadsGoal -= toSubtract; + numThreadsGoal -= toSubtract; + _separated.counts.InterlockedSetNumThreadsGoal(numThreadsGoal); HillClimbing.ThreadPoolHillClimber.ForceChange( numThreadsGoal, HillClimbing.StateOrTransition.CooperativeBlocking); @@ -158,7 +160,6 @@ private uint PerformBlockingAdjustment(bool previousDelayElapsed, out bool addWo { // Calculate how many threads can be added without a delay. Threads that were already created but may be just // waiting for work can be released for work without a delay, but creating a new thread may need a delay. - ThreadCounts counts = _separated.counts; short maxThreadsGoalWithoutDelay = Math.Max(configuredMaxThreadsWithoutDelay, Math.Min(counts.NumExistingThreads, _maxThreads)); short targetThreadsGoalWithoutDelay = Math.Min(targetThreadsGoal, maxThreadsGoalWithoutDelay); @@ -225,7 +226,7 @@ private uint PerformBlockingAdjustment(bool previousDelayElapsed, out bool addWo } while (false); _numThreadsAddedDueToBlocking += (short)(newNumThreadsGoal - numThreadsGoal); - _separated.numThreadsGoal = newNumThreadsGoal; + counts = _separated.counts.InterlockedSetNumThreadsGoal(newNumThreadsGoal); HillClimbing.ThreadPoolHillClimber.ForceChange( newNumThreadsGoal, HillClimbing.StateOrTransition.CooperativeBlocking); 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 fefe16da418eb3..b28286d8ebe0e5 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 @@ -126,20 +126,31 @@ private static void GateThreadStart() // of the number of existing threads, is compared with the goal. There may be alternative // solutions, for now this is only to maintain consistency in behavior. ThreadCounts counts = threadPoolInstance._separated.counts; - if (counts.NumProcessingWork < threadPoolInstance._maxThreads && - counts.NumProcessingWork >= threadPoolInstance._separated.numThreadsGoal) + while ( + counts.NumProcessingWork < threadPoolInstance._maxThreads && + counts.NumProcessingWork >= counts.NumThreadsGoal) { if (debuggerBreakOnWorkStarvation) { Debugger.Break(); } + ThreadCounts newCounts = counts; short newNumThreadsGoal = (short)(counts.NumProcessingWork + 1); - threadPoolInstance._separated.numThreadsGoal = newNumThreadsGoal; - HillClimbing.ThreadPoolHillClimber.ForceChange( - newNumThreadsGoal, - HillClimbing.StateOrTransition.Starvation); - addWorker = true; + newCounts.NumThreadsGoal = newNumThreadsGoal; + + ThreadCounts countsBeforeUpdate = + threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); + if (countsBeforeUpdate == counts) + { + HillClimbing.ThreadPoolHillClimber.ForceChange( + newNumThreadsGoal, + HillClimbing.StateOrTransition.Starvation); + addWorker = true; + break; + } + + counts = countsBeforeUpdate; } } finally @@ -183,7 +194,7 @@ private static bool SufficientDelaySinceLastDequeue(PortableThreadPool threadPoo } else { - minimumDelay = (uint)threadPoolInstance._separated.numThreadsGoal * DequeueDelayThresholdMs; + minimumDelay = (uint)threadPoolInstance._separated.counts.NumThreadsGoal * DequeueDelayThresholdMs; } return delay > minimumDelay; 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 d4673f5cd73296..43aa0fcf7102cb 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 @@ -16,14 +16,15 @@ private struct ThreadCounts // SOS's ThreadPool command depends on this layout private const byte NumProcessingWorkShift = 0; private const byte NumExistingThreadsShift = 16; + private const byte NumThreadsGoalShift = 32; - private uint _data; // SOS's ThreadPool command depends on this name + private ulong _data; // SOS's ThreadPool command depends on this name - private ThreadCounts(uint data) => _data = data; + private ThreadCounts(ulong data) => _data = data; private short GetInt16Value(byte shift) => (short)(_data >> shift); private void SetInt16Value(short value, byte shift) => - _data = (_data & ~((uint)ushort.MaxValue << shift)) | ((uint)(ushort)value << shift); + _data = (_data & ~((ulong)ushort.MaxValue << shift)) | ((ulong)(ushort)value << shift); /// /// Number of threads processing work items. @@ -43,7 +44,7 @@ public void SubtractNumProcessingWork(short value) Debug.Assert(value >= 0); Debug.Assert(value <= NumProcessingWork); - _data -= (uint)(ushort)value << NumProcessingWorkShift; + _data -= (ulong)(ushort)value << NumProcessingWorkShift; } public void InterlockedDecrementNumProcessingWork() @@ -72,19 +73,61 @@ public void SubtractNumExistingThreads(short value) Debug.Assert(value >= 0); Debug.Assert(value <= NumExistingThreads); - _data -= (uint)(ushort)value << NumExistingThreadsShift; + _data -= (ulong)(ushort)value << NumExistingThreadsShift; + } + + /// + /// Max possible thread pool threads we want to have. + /// + public short NumThreadsGoal + { + get => GetInt16Value(NumThreadsGoalShift); + set + { + Debug.Assert(value > 0); + SetInt16Value(value, NumThreadsGoalShift); + } + } + + public ThreadCounts InterlockedSetNumThreadsGoal(short value) + { + ThreadPoolInstance._threadAdjustmentLock.VerifyIsLocked(); + + ThreadCounts counts = this; + while (true) + { + ThreadCounts newCounts = counts; + newCounts.NumThreadsGoal = value; + + ThreadCounts countsBeforeUpdate = InterlockedCompareExchange(newCounts, counts); + if (countsBeforeUpdate == counts) + { + return newCounts; + } + + counts = countsBeforeUpdate; + } } public ThreadCounts VolatileRead() => new ThreadCounts(Volatile.Read(ref _data)); - public ThreadCounts InterlockedCompareExchange(ThreadCounts newCounts, ThreadCounts oldCounts) => - new ThreadCounts(Interlocked.CompareExchange(ref _data, newCounts._data, oldCounts._data)); + public ThreadCounts InterlockedCompareExchange(ThreadCounts newCounts, ThreadCounts oldCounts) + { +#if DEBUG + if (newCounts.NumThreadsGoal != oldCounts.NumThreadsGoal) + { + ThreadPoolInstance._threadAdjustmentLock.VerifyIsLocked(); + } +#endif + + return new ThreadCounts(Interlocked.CompareExchange(ref _data, newCounts._data, oldCounts._data)); + } public static bool operator ==(ThreadCounts lhs, ThreadCounts rhs) => lhs._data == rhs._data; public static bool operator !=(ThreadCounts lhs, ThreadCounts rhs) => lhs._data != rhs._data; public override bool Equals([NotNullWhen(true)] object? obj) => obj is ThreadCounts other && _data == other._data; - public override int GetHashCode() => (int)_data; + public override int GetHashCode() => (int)_data + (int)(_data >> 32); } } } 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 8b1a44946d10f5..1298d1be4121d9 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 @@ -124,22 +124,19 @@ private static void WorkerThreadStart() ThreadCounts newCounts = counts; newCounts.SubtractNumExistingThreads(1); short newNumExistingThreads = (short)(numExistingThreads - 1); - - ThreadCounts oldCounts = threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); + short newNumThreadsGoal = + Math.Max( + threadPoolInstance.MinThreadsGoal, + Math.Min(newNumExistingThreads, counts.NumThreadsGoal)); + newCounts.NumThreadsGoal = newNumThreadsGoal; + + ThreadCounts oldCounts = + threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); if (oldCounts == counts) { - short newNumThreadsGoal = - Math.Max( - threadPoolInstance.MinThreadsGoal, - Math.Min(newNumExistingThreads, threadPoolInstance._separated.numThreadsGoal)); - if (threadPoolInstance._separated.numThreadsGoal != newNumThreadsGoal) - { - threadPoolInstance._separated.numThreadsGoal = newNumThreadsGoal; - HillClimbing.ThreadPoolHillClimber.ForceChange( - newNumThreadsGoal, - HillClimbing.StateOrTransition.ThreadTimedOut); - } - + HillClimbing.ThreadPoolHillClimber.ForceChange( + newNumThreadsGoal, + HillClimbing.StateOrTransition.ThreadTimedOut); if (NativeRuntimeEventSource.Log.IsEnabled()) { NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadStop((uint)newNumExistingThreads); @@ -181,7 +178,7 @@ internal static void MaybeAddWorkingWorker(PortableThreadPool threadPoolInstance while (true) { numProcessingWork = counts.NumProcessingWork; - if (numProcessingWork >= threadPoolInstance._separated.numThreadsGoal) + if (numProcessingWork >= counts.NumThreadsGoal) { return; } @@ -256,7 +253,7 @@ internal static bool ShouldStopProcessingWorkNow(PortableThreadPool threadPoolIn // code from which this implementation was ported, which turns a processing thread into a retired thread // and checks for pending requests like RemoveWorkingWorker. In this implementation there are // no retired threads, so only the count of threads processing work is considered. - if (counts.NumProcessingWork <= threadPoolInstance._separated.numThreadsGoal) + if (counts.NumProcessingWork <= counts.NumThreadsGoal) { return false; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs index 8c6163fa2f7d2d..a425298cacd8e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.cs @@ -51,8 +51,6 @@ private struct CacheLineSeparated { [FieldOffset(Internal.PaddingHelpers.CACHE_LINE_SIZE * 1)] public ThreadCounts counts; // SOS's ThreadPool command depends on this name - [FieldOffset(Internal.PaddingHelpers.CACHE_LINE_SIZE * 1 + sizeof(uint))] - public short numThreadsGoal; [FieldOffset(Internal.PaddingHelpers.CACHE_LINE_SIZE * 2)] public int lastDequeueTime; @@ -103,7 +101,7 @@ private PortableThreadPool() _maxThreads = _minThreads; } - _separated.numThreadsGoal = _minThreads; + _separated.counts.NumThreadsGoal = _minThreads; } public bool SetMinThreads(int workerThreads, int ioCompletionThreads) @@ -142,9 +140,9 @@ public bool SetMinThreads(int workerThreads, int ioCompletionThreads) wakeGateThread = true; } } - else if (_separated.numThreadsGoal < newMinThreads) + else if (_separated.counts.NumThreadsGoal < newMinThreads) { - _separated.numThreadsGoal = newMinThreads; + _separated.counts.InterlockedSetNumThreadsGoal(newMinThreads); if (_separated.numRequestedWorkers > 0) { addWorker = true; @@ -193,9 +191,9 @@ public bool SetMaxThreads(int workerThreads, int ioCompletionThreads) short newMaxThreads = (short)Math.Min(workerThreads, MaxPossibleThreadCount); _maxThreads = newMaxThreads; - if (_separated.numThreadsGoal > newMaxThreads) + if (_separated.counts.NumThreadsGoal > newMaxThreads) { - _separated.numThreadsGoal = newMaxThreads; + _separated.counts.InterlockedSetNumThreadsGoal(newMaxThreads); } return true; } @@ -272,13 +270,15 @@ private void AdjustMaxWorkersActive() bool addWorker = false; try { - // Skip hill climbing when there is a pending blocking adjustment. Hill climbing may otherwise bypass the - // blocking adjustment heuristics and increase the thread count too quickly. - if (_pendingBlockingAdjustment != PendingBlockingAdjustment.None) + // Repeated checks from ShouldAdjustMaxWorkersActive() inside the lock + ThreadCounts counts = _separated.counts; + if (counts.NumProcessingWork > counts.NumThreadsGoal || + _pendingBlockingAdjustment != PendingBlockingAdjustment.None) { return; } + long startTime = _currentSampleStartTime; long endTime = Stopwatch.GetTimestamp(); long freq = Stopwatch.Frequency; @@ -291,13 +291,13 @@ private void AdjustMaxWorkersActive() int totalNumCompletions = (int)_completionCounter.Count; int numCompletions = totalNumCompletions - _separated.priorCompletionCount; + short oldNumThreadsGoal = counts.NumThreadsGoal; int newNumThreadsGoal; (newNumThreadsGoal, _threadAdjustmentIntervalMs) = - HillClimbing.ThreadPoolHillClimber.Update(_separated.numThreadsGoal, elapsedSeconds, numCompletions); - short oldNumThreadsGoal = _separated.numThreadsGoal; + HillClimbing.ThreadPoolHillClimber.Update(oldNumThreadsGoal, elapsedSeconds, numCompletions); if (oldNumThreadsGoal != (short)newNumThreadsGoal) { - _separated.numThreadsGoal = (short)newNumThreadsGoal; + _separated.counts.InterlockedSetNumThreadsGoal((short)newNumThreadsGoal); // // If we're increasing the goal, inject a thread. If that thread finds work, it will inject @@ -354,7 +354,8 @@ private bool ShouldAdjustMaxWorkersActive(int currentTimeMs) // threads processing work to stop in response to a decreased thread count goal. The logic here is a bit // different from the original CoreCLR code from which this implementation was ported because in this // implementation there are no retired threads, so only the count of threads processing work is considered. - if (_separated.counts.NumProcessingWork > _separated.numThreadsGoal) + ThreadCounts counts = _separated.counts; + if (counts.NumProcessingWork > counts.NumThreadsGoal) { return false; } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs index c5ad4b0f42660a..d7e54e1ebab2ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs @@ -181,10 +181,9 @@ private static unsafe void EnumerateFilesRecursively(string path, Predicate? toExplore = null; // List used as a stack int bufferSize = Interop.Sys.GetReadDirRBufferSize(); - byte[]? dirBuffer = null; + byte[] dirBuffer = ArrayPool.Shared.Rent(bufferSize); try { - dirBuffer = ArrayPool.Shared.Rent(bufferSize); string currentPath = path; fixed (byte* dirBufferPtr = dirBuffer) @@ -266,8 +265,7 @@ private static unsafe void EnumerateFilesRecursively(string path, Predicate.Shared.Return(dirBuffer); + ArrayPool.Shared.Return(dirBuffer); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Version.cs b/src/libraries/System.Private.CoreLib/src/System/Version.cs index bf0ce8e2b4c535..469da80ebe7cfc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Version.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Version.cs @@ -197,33 +197,61 @@ public bool TryFormat(Span destination, out int charsWritten) => public bool TryFormat(Span destination, int fieldCount, out int charsWritten) { - switch (fieldCount) + switch ((uint)fieldCount) { - case 0: - charsWritten = 0; - return true; + case > 4: + ThrowArgumentException("4"); + break; + + case >= 3 when _Build == -1: + ThrowArgumentException("2"); + break; - case 1: - return ((uint)_Major).TryFormat(destination, out charsWritten); + case 4 when _Revision == -1: + ThrowArgumentException("3"); + break; + + static void ThrowArgumentException(string failureUpperBound) => + throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", failureUpperBound), nameof(fieldCount)); + } - case 2: - return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}", out charsWritten); + int totalCharsWritten = 0; - case 3: - if (_Build == -1) throw CreateBoundException("3"); - return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}.{(uint)_Build}", out charsWritten); + for (int i = 0; i < fieldCount; i++) + { + if (i != 0) + { + if (destination.IsEmpty) + { + charsWritten = 0; + return false; + } - case 4: - if (_Build == -1) throw CreateBoundException("2"); - if (_Revision == -1) throw CreateBoundException("3"); - return destination.TryWrite($"{(uint)_Major}.{(uint)_Minor}.{(uint)_Build}.{(uint)_Revision}", out charsWritten); + destination[0] = '.'; + destination = destination.Slice(1); + totalCharsWritten++; + } + + int value = i switch + { + 0 => _Major, + 1 => _Minor, + 2 => _Build, + _ => _Revision + }; + + if (!((uint)value).TryFormat(destination, out int valueCharsWritten)) + { + charsWritten = 0; + return false; + } - default: - throw CreateBoundException("4"); + totalCharsWritten += valueCharsWritten; + destination = destination.Slice(valueCharsWritten); } - static Exception CreateBoundException(string failureUpperBound) => - new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", failureUpperBound), nameof(fieldCount)); + charsWritten = totalCharsWritten; + return true; } bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => diff --git a/src/libraries/System.Reflection.Metadata/System.Reflection.Metadata.sln b/src/libraries/System.Reflection.Metadata/System.Reflection.Metadata.sln index 35971fd7edffb4..4a91b9b849a672 100644 --- a/src/libraries/System.Reflection.Metadata/System.Reflection.Metadata.sln +++ b/src/libraries/System.Reflection.Metadata/System.Reflection.Metadata.sln @@ -25,19 +25,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{B332ED71-193 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C5FDAE2B-91DB-488B-A833-10D7E800447D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections.Immutable", "..\System.Collections.Immutable\ref\System.Collections.Immutable.csproj", "{282C76D4-54C5-44BF-9F15-1A4302234DBB}" +EndProject Global - GlobalSection(NestedProjects) = preSolution - {2231787B-18C9-493C-A102-1E0E6A3D2CD3} = {31FAA062-EF3C-4F4D-AF64-A6130F92A737} - {7EE935DD-2F8B-4C72-BACF-5DB95DE080BE} = {31FAA062-EF3C-4F4D-AF64-A6130F92A737} - {6FB0D012-27EC-49EB-B41C-A01DDE2937ED} = {B332ED71-1937-4B24-8519-AE665C23DD2F} - {587255BE-DC22-4B85-9E3F-02325E7B4FF7} = {B332ED71-1937-4B24-8519-AE665C23DD2F} - {00147002-F430-4E24-95F8-5E79A4D24AE2} = {B332ED71-1937-4B24-8519-AE665C23DD2F} - {632AE256-CA0A-4EBA-8D64-F9988CDC459D} = {B332ED71-1937-4B24-8519-AE665C23DD2F} - {82DC5DDA-CCFD-4F67-9D15-9BAD27FB4251} = {B332ED71-1937-4B24-8519-AE665C23DD2F} - {A69B0EE0-BE0C-4D53-A16F-5465028D975D} = {C5FDAE2B-91DB-488B-A833-10D7E800447D} - {B905521A-FE25-4D35-9929-B2622F590263} = {C5FDAE2B-91DB-488B-A833-10D7E800447D} - {5E36AB65-74DD-47F5-8F2E-9050561BBFEE} = {C5FDAE2B-91DB-488B-A833-10D7E800447D} - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -83,10 +73,27 @@ Global {82DC5DDA-CCFD-4F67-9D15-9BAD27FB4251}.Debug|Any CPU.Build.0 = Debug|Any CPU {82DC5DDA-CCFD-4F67-9D15-9BAD27FB4251}.Release|Any CPU.ActiveCfg = Release|Any CPU {82DC5DDA-CCFD-4F67-9D15-9BAD27FB4251}.Release|Any CPU.Build.0 = Release|Any CPU + {282C76D4-54C5-44BF-9F15-1A4302234DBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {282C76D4-54C5-44BF-9F15-1A4302234DBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {282C76D4-54C5-44BF-9F15-1A4302234DBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {282C76D4-54C5-44BF-9F15-1A4302234DBB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2231787B-18C9-493C-A102-1E0E6A3D2CD3} = {31FAA062-EF3C-4F4D-AF64-A6130F92A737} + {6FB0D012-27EC-49EB-B41C-A01DDE2937ED} = {B332ED71-1937-4B24-8519-AE665C23DD2F} + {A69B0EE0-BE0C-4D53-A16F-5465028D975D} = {C5FDAE2B-91DB-488B-A833-10D7E800447D} + {587255BE-DC22-4B85-9E3F-02325E7B4FF7} = {B332ED71-1937-4B24-8519-AE665C23DD2F} + {B905521A-FE25-4D35-9929-B2622F590263} = {C5FDAE2B-91DB-488B-A833-10D7E800447D} + {7EE935DD-2F8B-4C72-BACF-5DB95DE080BE} = {31FAA062-EF3C-4F4D-AF64-A6130F92A737} + {00147002-F430-4E24-95F8-5E79A4D24AE2} = {B332ED71-1937-4B24-8519-AE665C23DD2F} + {5E36AB65-74DD-47F5-8F2E-9050561BBFEE} = {C5FDAE2B-91DB-488B-A833-10D7E800447D} + {632AE256-CA0A-4EBA-8D64-F9988CDC459D} = {B332ED71-1937-4B24-8519-AE665C23DD2F} + {82DC5DDA-CCFD-4F67-9D15-9BAD27FB4251} = {B332ED71-1937-4B24-8519-AE665C23DD2F} + {282C76D4-54C5-44BF-9F15-1A4302234DBB} = {B332ED71-1937-4B24-8519-AE665C23DD2F} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {64BB97AB-FD23-40BA-B638-FE4756AE6452} EndGlobalSection diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/StreamExtensions.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/StreamExtensions.cs index 4035e3aa0a4693..89175b7f84afd6 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/StreamExtensions.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/StreamExtensions.cs @@ -60,7 +60,7 @@ internal static int TryReadAll(this Stream stream, byte[] buffer, int offset, in Debug.Assert(count > 0); int totalBytesRead; - int bytesRead = 0; + int bytesRead; for (totalBytesRead = 0; totalBytesRead < count; totalBytesRead += bytesRead) { // Note: Don't attempt to save state in-between calls to .Read as it would @@ -76,6 +76,25 @@ internal static int TryReadAll(this Stream stream, byte[] buffer, int offset, in return totalBytesRead; } +#if NETCOREAPP3_0_OR_GREATER + internal static int TryReadAll(this Stream stream, Span buffer) + { + int totalBytesRead = 0; + while (totalBytesRead < buffer.Length) + { + int bytesRead = stream.Read(buffer.Slice(totalBytesRead)); + if (bytesRead == 0) + { + break; + } + + totalBytesRead += bytesRead; + } + + return totalBytesRead; + } +#endif + /// /// Resolve image size as either the given user-specified size or distance from current position to end-of-stream. /// Also performs the relevant argument validation and publicly visible caller has same argument names. diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReaderProvider.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReaderProvider.cs index 7179ae12a52066..836ca232a53c9c 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReaderProvider.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/MetadataReaderProvider.cs @@ -30,7 +30,7 @@ public sealed class MetadataReaderProvider : IDisposable private MetadataReader? _lazyMetadataReader; private readonly object _metadataReaderGuard = new object(); - private MetadataReaderProvider(AbstractMemoryBlock metadataBlock) + internal MetadataReaderProvider(AbstractMemoryBlock metadataBlock) { Debug.Assert(metadataBlock != null); _lazyMetadataBlock = metadataBlock; diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEReader.EmbeddedPortablePdb.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEReader.EmbeddedPortablePdb.cs index ba58af0d0cffcd..43d6e31584e928 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEReader.EmbeddedPortablePdb.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEReader.EmbeddedPortablePdb.cs @@ -26,12 +26,13 @@ public sealed partial class PEReader : IDisposable /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob. /// /// - /// Provider of a metadata reader reading Portable PDB image. + /// Provider of a metadata reader reading the embedded Portable PDB image. + /// Dispose to release resources allocated for the embedded PDB. /// /// is not a entry. /// Bad format of the data. /// PE image not available. - public MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry) + public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb) { @@ -40,11 +41,8 @@ public MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDir ValidateEmbeddedPortablePdbVersion(entry); - using (var block = GetDebugDirectoryEntryDataBlock(entry)) - { - var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block); - return MetadataReaderProvider.FromPortablePdbImage(pdbImage); - } + using var block = GetDebugDirectoryEntryDataBlock(entry); + return new MetadataReaderProvider(DecodeEmbeddedPortablePdbDebugDirectoryData(block)); } // internal for testing @@ -69,9 +67,9 @@ internal static void ValidateEmbeddedPortablePdbVersion(DebugDirectoryEntry entr } // internal for testing - internal static unsafe ImmutableArray DecodeEmbeddedPortablePdbDebugDirectoryData(AbstractMemoryBlock block) + internal static unsafe NativeHeapMemoryBlock DecodeEmbeddedPortablePdbDebugDirectoryData(AbstractMemoryBlock block) { - byte[]? decompressed; + NativeHeapMemoryBlock? decompressed; var headerReader = block.GetReader(); if (headerReader.ReadUInt32() != PortablePdbVersions.DebugDirectoryEmbeddedSignature) @@ -83,44 +81,63 @@ internal static unsafe ImmutableArray DecodeEmbeddedPortablePdbDebugDirect try { - decompressed = new byte[decompressedSize]; + decompressed = new NativeHeapMemoryBlock(decompressedSize); } catch { throw new BadImageFormatException(SR.DataTooBig); } - var compressed = new ReadOnlyUnmanagedMemoryStream(headerReader.CurrentPointer, headerReader.RemainingBytes); - var deflate = new DeflateStream(compressed, CompressionMode.Decompress, leaveOpen: true); - - if (decompressedSize > 0) + bool success = false; + try { - int actualLength; + var compressed = new ReadOnlyUnmanagedMemoryStream(headerReader.CurrentPointer, headerReader.RemainingBytes); + var deflate = new DeflateStream(compressed, CompressionMode.Decompress, leaveOpen: true); - try - { - actualLength = deflate.TryReadAll(decompressed, 0, decompressed.Length); - } - catch (InvalidDataException e) + if (decompressedSize > 0) { - throw new BadImageFormatException(e.Message, e.InnerException); + int actualLength; + + try + { +#if NETCOREAPP3_0_OR_GREATER + actualLength = deflate.TryReadAll(new Span(decompressed.Pointer, decompressed.Size)); +#else + using var decompressedStream = new UnmanagedMemoryStream(decompressed.Pointer, decompressed.Size, decompressed.Size, FileAccess.Write); + deflate.CopyTo(decompressedStream); + actualLength = (int)decompressedStream.Position; +#endif + } + catch (Exception e) + { + throw new BadImageFormatException(e.Message, e.InnerException); + } + + if (actualLength != decompressed.Size) + { + throw new BadImageFormatException(SR.SizeMismatch); + } } - if (actualLength != decompressed.Length) + // Check that there is no more compressed data left, + // in case the decompressed size specified in the header is smaller + // than the actual decompressed size of the data. + if (deflate.ReadByte() != -1) { throw new BadImageFormatException(SR.SizeMismatch); } - } - // Check that there is no more compressed data left, - // in case the decompressed size specified in the header is smaller - // than the actual decompressed size of the data. - if (deflate.ReadByte() != -1) + success = true; + } + finally { - throw new BadImageFormatException(SR.SizeMismatch); + if (!success) + { + decompressed.Dispose(); + } } - return ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref decompressed); + return decompressed; } partial void TryOpenEmbeddedPortablePdb(DebugDirectoryEntry embeddedPdbEntry, ref bool openedEmbeddedPdb, ref MetadataReaderProvider? provider, ref Exception? errorToReport) diff --git a/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/DebugDirectoryBuilderTests.cs b/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/DebugDirectoryBuilderTests.cs index 89ca4a59938929..ed0270f6994fff 100644 --- a/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/DebugDirectoryBuilderTests.cs +++ b/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/DebugDirectoryBuilderTests.cs @@ -292,26 +292,22 @@ public void EmbeddedPortablePdb() 0xEB, 0x28, 0x4F, 0x0B, 0x75, 0x31, 0x56, 0x12, 0x04, 0x00 // compressed data }, bytes); - using (var pinned = new PinnedBlob(bytes)) - { - var actual = PEReader.ReadDebugDirectoryEntries(pinned.CreateReader(0, DebugDirectoryEntry.Size)); - Assert.Equal(1, actual.Length); - Assert.Equal(0u, actual[0].Stamp); - Assert.Equal(0x0100, actual[0].MajorVersion); - Assert.Equal(0x0100, actual[0].MinorVersion); - Assert.Equal(DebugDirectoryEntryType.EmbeddedPortablePdb, actual[0].Type); - Assert.False(actual[0].IsPortableCodeView); - Assert.Equal(0x00000012, actual[0].DataSize); - Assert.Equal(0x0000001c, actual[0].DataRelativeVirtualAddress); - Assert.Equal(0x0000001c, actual[0].DataPointer); - - var provider = new ByteArrayMemoryProvider(bytes); - using (var block = provider.GetMemoryBlock(actual[0].DataPointer, actual[0].DataSize)) - { - var decoded = PEReader.DecodeEmbeddedPortablePdbDebugDirectoryData(block); - AssertEx.Equal(new byte[] { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 }, decoded); - } - } + using var pinned = new PinnedBlob(bytes); + var actual = PEReader.ReadDebugDirectoryEntries(pinned.CreateReader(0, DebugDirectoryEntry.Size)); + Assert.Equal(1, actual.Length); + Assert.Equal(0u, actual[0].Stamp); + Assert.Equal(0x0100, actual[0].MajorVersion); + Assert.Equal(0x0100, actual[0].MinorVersion); + Assert.Equal(DebugDirectoryEntryType.EmbeddedPortablePdb, actual[0].Type); + Assert.False(actual[0].IsPortableCodeView); + Assert.Equal(0x00000012, actual[0].DataSize); + Assert.Equal(0x0000001c, actual[0].DataRelativeVirtualAddress); + Assert.Equal(0x0000001c, actual[0].DataPointer); + + var provider = new ByteArrayMemoryProvider(bytes); + using var block = provider.GetMemoryBlock(actual[0].DataPointer, actual[0].DataSize); + using var decoded = PEReader.DecodeEmbeddedPortablePdbDebugDirectoryData(block); + AssertEx.Equal(new byte[] { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 }, decoded.GetContentUnchecked(0, decoded.Size)); } [Fact] diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs index 1e7c4896bae46f..227b537b08a1dc 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs @@ -72,7 +72,7 @@ internal enum CoreType internal static class CoreTypeHelpers { - public static void GetFullName(this CoreType coreType, out byte[] ns, out byte[] name) + public static void GetFullName(this CoreType coreType, out ReadOnlySpan ns, out ReadOnlySpan name) { switch (coreType) { @@ -121,7 +121,7 @@ public static void GetFullName(this CoreType coreType, out byte[] ns, out byte[] case CoreType.FieldOffsetAttribute: ns = Utf8Constants.SystemRuntimeInteropServices; name = Utf8Constants.FieldOffsetAttribute; return; default: Debug.Fail("Unexpected coreType passed to GetCoreTypeFullName: " + coreType); - ns = name = null; + ns = name = default; return; } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreTypes.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreTypes.cs index dab13c30bfdc0c..c40fd5efb042b2 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreTypes.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreTypes.cs @@ -26,7 +26,7 @@ internal CoreTypes(MetadataLoadContext loader, string? coreAssemblyName) { for (int i = 0; i < numCoreTypes; i++) { - ((CoreType)i).GetFullName(out byte[] ns, out byte[] name); + ((CoreType)i).GetFullName(out ReadOnlySpan ns, out ReadOnlySpan name); RoType? type = coreAssembly.GetTypeCore(ns, name, ignoreCase: false, out e); coreTypes[i] = type; if (type == null) diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/EcmaSignatureTypeProviderForToString.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/EcmaSignatureTypeProviderForToString.cs index 7859dc1b6a9a7f..af6ff67c3b871d 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/EcmaSignatureTypeProviderForToString.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/EcmaSignatureTypeProviderForToString.cs @@ -57,7 +57,7 @@ public string GetGenericInstantiation(string genericType, ImmutableArray public string GetPrimitiveType(PrimitiveTypeCode typeCode) { - typeCode.ToCoreType().GetFullName(out byte[] ns, out byte[] name); + typeCode.ToCoreType().GetFullName(out ReadOnlySpan ns, out ReadOnlySpan name); return ns.ToUtf16() + "." + name.ToUtf16(); // This is not safe for types outside of a namespace, but all primitive types are known to be in "System" } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs index 8381a6a56d0870..2c45856dcf7367 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs @@ -367,8 +367,22 @@ public static RoAssemblyName ToRoAssemblyName(this AssemblyName assemblyName) public static byte[] ToUtf8(this string s) => Encoding.UTF8.GetBytes(s); - public static string ToUtf16(this ReadOnlySpan utf8) => ToUtf16(utf8.ToArray()); - public static string ToUtf16(this byte[] utf8) => Encoding.UTF8.GetString(utf8); +#if NETCOREAPP3_1_OR_GREATER + public static string ToUtf16(this ReadOnlySpan utf8) => Encoding.UTF8.GetString(utf8); +#else + public static unsafe string ToUtf16(this ReadOnlySpan utf8) + { + if (utf8.IsEmpty) + { + return string.Empty; + } + + fixed (byte* ptr = utf8) + { + return Encoding.UTF8.GetString(ptr, utf8.Length); + } + } +#endif // Guards ToString() implementations. Sample usage: // diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs index fe889e9ea1868a..5617958b5f8162 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs @@ -5,60 +5,60 @@ namespace System.Reflection.TypeLoading { internal static class Utf8Constants { - public static readonly byte[] System = { 83, 121, 115, 116, 101, 109 }; - public static readonly byte[] SystemReflection = { 83, 121, 115, 116, 101, 109, 46, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110 }; - public static readonly byte[] SystemCollectionsGeneric = { 83, 121, 115, 116, 101, 109, 46, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 115, 46, 71, 101, 110, 101, 114, 105, 99 }; - public static readonly byte[] SystemRuntimeInteropServices = { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 73, 110, 116, 101, 114, 111, 112, 83, 101, 114, 118, 105, 99, 101, 115 }; - public static readonly byte[] SystemRuntimeCompilerServices = { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 67, 111, 109, 112, 105, 108, 101, 114, 83, 101, 114, 118, 105, 99, 101, 115 }; + public static ReadOnlySpan System => new byte[] { 83, 121, 115, 116, 101, 109 }; + public static ReadOnlySpan SystemReflection => new byte[] { 83, 121, 115, 116, 101, 109, 46, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110 }; + public static ReadOnlySpan SystemCollectionsGeneric => new byte[] { 83, 121, 115, 116, 101, 109, 46, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 115, 46, 71, 101, 110, 101, 114, 105, 99 }; + public static ReadOnlySpan SystemRuntimeInteropServices => new byte[] { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 73, 110, 116, 101, 114, 111, 112, 83, 101, 114, 118, 105, 99, 101, 115 }; + public static ReadOnlySpan SystemRuntimeCompilerServices => new byte[] { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 67, 111, 109, 112, 105, 108, 101, 114, 83, 101, 114, 118, 105, 99, 101, 115 }; - public static readonly byte[] Array = { 65, 114, 114, 97, 121 }; - public static readonly byte[] Boolean = { 66, 111, 111, 108, 101, 97, 110 }; - public static readonly byte[] Byte = { 66, 121, 116, 101 }; - public static readonly byte[] Char = { 67, 104, 97, 114 }; - public static readonly byte[] Double = { 68, 111, 117, 98, 108, 101 }; - public static readonly byte[] Enum = { 69, 110, 117, 109 }; - public static readonly byte[] Int16 = { 73, 110, 116, 49, 54 }; - public static readonly byte[] Int32 = { 73, 110, 116, 51, 50 }; - public static readonly byte[] Int64 = { 73, 110, 116, 54, 52 }; - public static readonly byte[] IntPtr = { 73, 110, 116, 80, 116, 114 }; - public static readonly byte[] Object = { 79, 98, 106, 101, 99, 116 }; - public static readonly byte[] NullableT = { 78, 117, 108, 108, 97, 98, 108, 101, 96, 49 }; - public static readonly byte[] SByte = { 83, 66, 121, 116, 101 }; - public static readonly byte[] Single = { 83, 105, 110, 103, 108, 101 }; - public static readonly byte[] String = { 83, 116, 114, 105, 110, 103 }; - public static readonly byte[] TypedReference = { 84, 121, 112, 101, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101 }; - public static readonly byte[] UInt16 = { 85, 73, 110, 116, 49, 54 }; - public static readonly byte[] UInt32 = { 85, 73, 110, 116, 51, 50 }; - public static readonly byte[] UInt64 = { 85, 73, 110, 116, 54, 52 }; - public static readonly byte[] UIntPtr = { 85, 73, 110, 116, 80, 116, 114 }; - public static readonly byte[] ValueType = { 86, 97, 108, 117, 101, 84, 121, 112, 101 }; - public static readonly byte[] Void = { 86, 111, 105, 100 }; - public static readonly byte[] MulticastDelegate = { 77, 117, 108, 116, 105, 99, 97, 115, 116, 68, 101, 108, 101, 103, 97, 116, 101 }; - public static readonly byte[] IEnumerableT = { 73, 69, 110, 117, 109, 101, 114, 97, 98, 108, 101, 96, 49 }; - public static readonly byte[] ICollectionT = { 73, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 96, 49 }; - public static readonly byte[] IListT = { 73, 76, 105, 115, 116, 96, 49 }; - public static readonly byte[] IReadOnlyListT = { 73, 82, 101, 97, 100, 79, 110, 108, 121, 76, 105, 115, 116, 96, 49 }; - public static readonly byte[] Type = { 84, 121, 112, 101 }; - public static readonly byte[] DBNull = { 68, 66, 78, 117, 108, 108 }; - public static readonly byte[] Decimal = { 68, 101, 99, 105, 109, 97, 108 }; - public static readonly byte[] DateTime = { 68, 97, 116, 101, 84, 105, 109, 101 }; - public static readonly byte[] ComImportAttribute = { 67, 111, 109, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] DllImportAttribute = { 68, 108, 108, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] CallingConvention = { 67, 97, 108, 108, 105, 110, 103, 67, 111, 110, 118, 101, 110, 116, 105, 111, 110 }; - public static readonly byte[] CharSet = { 67, 104, 97, 114, 83, 101, 116 }; - public static readonly byte[] MarshalAsAttribute = { 77, 97, 114, 115, 104, 97, 108, 65, 115, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] UnmanagedType = { 85, 110, 109, 97, 110, 97, 103, 101, 100, 84, 121, 112, 101 }; - public static readonly byte[] VarEnum = { 86, 97, 114, 69, 110, 117, 109 }; - public static readonly byte[] InAttribute = { 73, 110, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] OutAttriubute = { 79, 117, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] OptionalAttribute = { 79, 112, 116, 105, 111, 110, 97, 108, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] PreserveSigAttribute = { 80, 114, 101, 115, 101, 114, 118, 101, 83, 105, 103, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] FieldOffsetAttribute = { 70, 105, 101, 108, 100, 79, 102, 102, 115, 101, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] IsByRefLikeAttribute = { 73, 115, 66, 121, 82, 101, 102, 76, 105, 107, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] DecimalConstantAttribute = { 68, 101, 99, 105, 109, 97, 108, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] CustomConstantAttribute = { 67, 117, 115, 116, 111, 109, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] GuidAttribute = { 71, 117, 105, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] DefaultMemberAttribute = { 68, 101, 102, 97, 117, 108, 116, 77, 101, 109, 98, 101, 114, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; - public static readonly byte[] DateTimeConstantAttribute = { 68, 97, 116, 101, 84, 105, 109, 101, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan Array => new byte[] { 65, 114, 114, 97, 121 }; + public static ReadOnlySpan Boolean => new byte[] { 66, 111, 111, 108, 101, 97, 110 }; + public static ReadOnlySpan Byte => new byte[] { 66, 121, 116, 101 }; + public static ReadOnlySpan Char => new byte[] { 67, 104, 97, 114 }; + public static ReadOnlySpan Double => new byte[] { 68, 111, 117, 98, 108, 101 }; + public static ReadOnlySpan Enum => new byte[] { 69, 110, 117, 109 }; + public static ReadOnlySpan Int16 => new byte[] { 73, 110, 116, 49, 54 }; + public static ReadOnlySpan Int32 => new byte[] { 73, 110, 116, 51, 50 }; + public static ReadOnlySpan Int64 => new byte[] { 73, 110, 116, 54, 52 }; + public static ReadOnlySpan IntPtr => new byte[] { 73, 110, 116, 80, 116, 114 }; + public static ReadOnlySpan Object => new byte[] { 79, 98, 106, 101, 99, 116 }; + public static ReadOnlySpan NullableT => new byte[] { 78, 117, 108, 108, 97, 98, 108, 101, 96, 49 }; + public static ReadOnlySpan SByte => new byte[] { 83, 66, 121, 116, 101 }; + public static ReadOnlySpan Single => new byte[] { 83, 105, 110, 103, 108, 101 }; + public static ReadOnlySpan String => new byte[] { 83, 116, 114, 105, 110, 103 }; + public static ReadOnlySpan TypedReference => new byte[] { 84, 121, 112, 101, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101 }; + public static ReadOnlySpan UInt16 => new byte[] { 85, 73, 110, 116, 49, 54 }; + public static ReadOnlySpan UInt32 => new byte[] { 85, 73, 110, 116, 51, 50 }; + public static ReadOnlySpan UInt64 => new byte[] { 85, 73, 110, 116, 54, 52 }; + public static ReadOnlySpan UIntPtr => new byte[] { 85, 73, 110, 116, 80, 116, 114 }; + public static ReadOnlySpan ValueType => new byte[] { 86, 97, 108, 117, 101, 84, 121, 112, 101 }; + public static ReadOnlySpan Void => new byte[] { 86, 111, 105, 100 }; + public static ReadOnlySpan MulticastDelegate => new byte[] { 77, 117, 108, 116, 105, 99, 97, 115, 116, 68, 101, 108, 101, 103, 97, 116, 101 }; + public static ReadOnlySpan IEnumerableT => new byte[] { 73, 69, 110, 117, 109, 101, 114, 97, 98, 108, 101, 96, 49 }; + public static ReadOnlySpan ICollectionT => new byte[] { 73, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 96, 49 }; + public static ReadOnlySpan IListT => new byte[] { 73, 76, 105, 115, 116, 96, 49 }; + public static ReadOnlySpan IReadOnlyListT => new byte[] { 73, 82, 101, 97, 100, 79, 110, 108, 121, 76, 105, 115, 116, 96, 49 }; + public static ReadOnlySpan Type => new byte[] { 84, 121, 112, 101 }; + public static ReadOnlySpan DBNull => new byte[] { 68, 66, 78, 117, 108, 108 }; + public static ReadOnlySpan Decimal => new byte[] { 68, 101, 99, 105, 109, 97, 108 }; + public static ReadOnlySpan DateTime => new byte[] { 68, 97, 116, 101, 84, 105, 109, 101 }; + public static ReadOnlySpan ComImportAttribute => new byte[] { 67, 111, 109, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan DllImportAttribute => new byte[] { 68, 108, 108, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan CallingConvention => new byte[] { 67, 97, 108, 108, 105, 110, 103, 67, 111, 110, 118, 101, 110, 116, 105, 111, 110 }; + public static ReadOnlySpan CharSet => new byte[] { 67, 104, 97, 114, 83, 101, 116 }; + public static ReadOnlySpan MarshalAsAttribute => new byte[] { 77, 97, 114, 115, 104, 97, 108, 65, 115, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan UnmanagedType => new byte[] { 85, 110, 109, 97, 110, 97, 103, 101, 100, 84, 121, 112, 101 }; + public static ReadOnlySpan VarEnum => new byte[] { 86, 97, 114, 69, 110, 117, 109 }; + public static ReadOnlySpan InAttribute => new byte[] { 73, 110, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan OutAttriubute => new byte[] { 79, 117, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan OptionalAttribute => new byte[] { 79, 112, 116, 105, 111, 110, 97, 108, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan PreserveSigAttribute => new byte[] { 80, 114, 101, 115, 101, 114, 118, 101, 83, 105, 103, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan FieldOffsetAttribute => new byte[] { 70, 105, 101, 108, 100, 79, 102, 102, 115, 101, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan IsByRefLikeAttribute => new byte[] { 73, 115, 66, 121, 82, 101, 102, 76, 105, 107, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan DecimalConstantAttribute => new byte[] { 68, 101, 99, 105, 109, 97, 108, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan CustomConstantAttribute => new byte[] { 67, 117, 115, 116, 111, 109, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan GuidAttribute => new byte[] { 71, 117, 105, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan DefaultMemberAttribute => new byte[] { 68, 101, 102, 97, 117, 108, 116, 77, 101, 109, 98, 101, 114, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; + public static ReadOnlySpan DateTimeConstantAttribute => new byte[] { 68, 97, 116, 101, 84, 105, 109, 101, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 }; } } diff --git a/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs b/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs index 6c020bbfffd4ba..f931cbe364b539 100644 --- a/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs +++ b/src/libraries/System.Reflection.TypeExtensions/src/System/Reflection/TypeExtensions.cs @@ -11,14 +11,14 @@ public static class TypeExtensions [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type, Type[] types) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetConstructor(types); } public static ConstructorInfo[] GetConstructors( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetConstructors(); } @@ -26,7 +26,7 @@ public static ConstructorInfo[] GetConstructors( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetConstructors(bindingAttr); } @@ -39,7 +39,7 @@ public static MemberInfo[] GetDefaultMembers( | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicNestedTypes)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetDefaultMembers(); } @@ -47,7 +47,7 @@ public static MemberInfo[] GetDefaultMembers( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] this Type type, string name) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetEvent(name); } @@ -56,14 +56,14 @@ public static MemberInfo[] GetDefaultMembers( string name, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetEvent(name, bindingAttr); } public static EventInfo[] GetEvents( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetEvents(); } @@ -71,7 +71,7 @@ public static EventInfo[] GetEvents( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetEvents(bindingAttr); } @@ -79,7 +79,7 @@ public static EventInfo[] GetEvents( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] this Type type, string name) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetField(name); } @@ -88,14 +88,14 @@ public static EventInfo[] GetEvents( string name, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetField(name, bindingAttr); } public static FieldInfo[] GetFields( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetFields(); } @@ -103,20 +103,20 @@ public static FieldInfo[] GetFields( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetFields(bindingAttr); } public static Type[] GetGenericArguments(this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetGenericArguments(); } public static Type[] GetInterfaces( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetInterfaces(); } @@ -130,7 +130,7 @@ public static MemberInfo[] GetMember( | DynamicallyAccessedMemberTypes.PublicNestedTypes)] this Type type, string name) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMember(name); } @@ -139,7 +139,7 @@ public static MemberInfo[] GetMember( string name, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMember(name, bindingAttr); } @@ -152,7 +152,7 @@ public static MemberInfo[] GetMembers( | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicNestedTypes)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMembers(); } @@ -160,7 +160,7 @@ public static MemberInfo[] GetMembers( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMembers(bindingAttr); } @@ -168,7 +168,7 @@ public static MemberInfo[] GetMembers( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type, string name) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMethod(name); } @@ -177,7 +177,7 @@ public static MemberInfo[] GetMembers( string name, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMethod(name, bindingAttr); } @@ -186,14 +186,14 @@ public static MemberInfo[] GetMembers( string name, Type[] types) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMethod(name, types); } public static MethodInfo[] GetMethods( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMethods(); } @@ -201,7 +201,7 @@ public static MethodInfo[] GetMethods( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetMethods(bindingAttr); } @@ -210,7 +210,7 @@ public static MethodInfo[] GetMethods( string name, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetNestedType(name, bindingAttr); } @@ -218,14 +218,14 @@ public static Type[] GetNestedTypes( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetNestedTypes(bindingAttr); } public static PropertyInfo[] GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type type) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetProperties(); } @@ -233,7 +233,7 @@ public static PropertyInfo[] GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] this Type type, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetProperties(bindingAttr); } @@ -241,7 +241,7 @@ public static PropertyInfo[] GetProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type type, string name) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetProperty(name); } @@ -250,7 +250,7 @@ public static PropertyInfo[] GetProperties( string name, BindingFlags bindingAttr) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetProperty(name, bindingAttr); } @@ -259,7 +259,7 @@ public static PropertyInfo[] GetProperties( string name, Type? returnType) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetProperty(name, returnType); } @@ -269,19 +269,19 @@ public static PropertyInfo[] GetProperties( Type? returnType, Type[] types) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.GetProperty(name, returnType, types); } public static bool IsAssignableFrom(this Type type, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] Type? c) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.IsAssignableFrom(c); } public static bool IsInstanceOfType(this Type type, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? o) { - ArgumentNullException.ThrowIfNull(type, nameof(type)); + ArgumentNullException.ThrowIfNull(type); return type.IsInstanceOfType(o); } } @@ -291,20 +291,20 @@ public static class AssemblyExtensions [RequiresUnreferencedCode("Types might be removed")] public static Type[] GetExportedTypes(this Assembly assembly) { - ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); + ArgumentNullException.ThrowIfNull(assembly); return assembly.GetExportedTypes(); } public static Module[] GetModules(this Assembly assembly) { - ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); + ArgumentNullException.ThrowIfNull(assembly); return assembly.GetModules(); } [RequiresUnreferencedCode("Types might be removed")] public static Type[] GetTypes(this Assembly assembly) { - ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); + ArgumentNullException.ThrowIfNull(assembly); return assembly.GetTypes(); } } @@ -313,37 +313,37 @@ public static class EventInfoExtensions { public static MethodInfo? GetAddMethod(this EventInfo eventInfo) { - ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo); return eventInfo.GetAddMethod(); } public static MethodInfo? GetAddMethod(this EventInfo eventInfo, bool nonPublic) { - ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo); return eventInfo.GetAddMethod(nonPublic); } public static MethodInfo? GetRaiseMethod(this EventInfo eventInfo) { - ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo); return eventInfo.GetRaiseMethod(); } public static MethodInfo? GetRaiseMethod(this EventInfo eventInfo, bool nonPublic) { - ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo); return eventInfo.GetRaiseMethod(nonPublic); } public static MethodInfo? GetRemoveMethod(this EventInfo eventInfo) { - ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo); return eventInfo.GetRemoveMethod(); } public static MethodInfo? GetRemoveMethod(this EventInfo eventInfo, bool nonPublic) { - ArgumentNullException.ThrowIfNull(eventInfo, nameof(eventInfo)); + ArgumentNullException.ThrowIfNull(eventInfo); return eventInfo.GetRemoveMethod(nonPublic); } } @@ -358,7 +358,7 @@ public static class MemberInfoExtensions /// This maybe public static bool HasMetadataToken(this MemberInfo member) { - ArgumentNullException.ThrowIfNull(member, nameof(member)); + ArgumentNullException.ThrowIfNull(member); try { @@ -380,7 +380,7 @@ public static bool HasMetadataToken(this MemberInfo member) /// public static int GetMetadataToken(this MemberInfo member) { - ArgumentNullException.ThrowIfNull(member, nameof(member)); + ArgumentNullException.ThrowIfNull(member); int token = GetMetadataTokenOrZeroOrThrow(member); @@ -413,7 +413,7 @@ public static class MethodInfoExtensions { public static MethodInfo GetBaseDefinition(this MethodInfo method) { - ArgumentNullException.ThrowIfNull(method, nameof(method)); + ArgumentNullException.ThrowIfNull(method); return method.GetBaseDefinition(); } } @@ -422,13 +422,13 @@ public static class ModuleExtensions { public static bool HasModuleVersionId(this Module module) { - ArgumentNullException.ThrowIfNull(module, nameof(module)); + ArgumentNullException.ThrowIfNull(module); return true; // not expected to fail on platforms with Module.ModuleVersionId built-in. } public static Guid GetModuleVersionId(this Module module) { - ArgumentNullException.ThrowIfNull(module, nameof(module)); + ArgumentNullException.ThrowIfNull(module); return module.ModuleVersionId; } } @@ -437,37 +437,37 @@ public static class PropertyInfoExtensions { public static MethodInfo[] GetAccessors(this PropertyInfo property) { - ArgumentNullException.ThrowIfNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property); return property.GetAccessors(); } public static MethodInfo[] GetAccessors(this PropertyInfo property, bool nonPublic) { - ArgumentNullException.ThrowIfNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property); return property.GetAccessors(nonPublic); } public static MethodInfo? GetGetMethod(this PropertyInfo property) { - ArgumentNullException.ThrowIfNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property); return property.GetGetMethod(); } public static MethodInfo? GetGetMethod(this PropertyInfo property, bool nonPublic) { - ArgumentNullException.ThrowIfNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property); return property.GetGetMethod(nonPublic); } public static MethodInfo? GetSetMethod(this PropertyInfo property) { - ArgumentNullException.ThrowIfNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property); return property.GetSetMethod(); } public static MethodInfo? GetSetMethod(this PropertyInfo property, bool nonPublic) { - ArgumentNullException.ThrowIfNull(property, nameof(property)); + ArgumentNullException.ThrowIfNull(property); return property.GetSetMethod(nonPublic); } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index aa90a8bbb1f32f..ffee155326a5f1 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1996,6 +1996,7 @@ public static explicit operator decimal(BigInteger value) Span stackallocedXd = stackalloc uint[1]; Span xd = stackallocedXd; bool negx = GetPartsForBitManipulation(ref value, ref xd); + bool trackSignBit = false; if (negx) { @@ -2020,9 +2021,13 @@ public static explicit operator decimal(BigInteger value) } NumericsHelpers.DangerousMakeTwosComplement(xd); // Mutates xd + if (xd[^1] == 0) + { + trackSignBit = true; + } } - int zl = xd.Length - digitShift; + int zl = xd.Length - digitShift + (trackSignBit ? 1: 0); uint[]? zdArray = null; Span zd = stackalloc uint[0]; if (zl > 0) @@ -2057,6 +2062,11 @@ stackalloc uint[StackallocUInt32Limit].Slice(0, zl) : if (negx) { NumericsHelpers.DangerousMakeTwosComplement(zd); // Mutates zd + + if (trackSignBit) + { + zd[^1] = 1; + } } return new BigInteger(zd, zdArray, negx); 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 5107f0ed7d7671..0e7fbf1e30ebab 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs @@ -54,6 +54,15 @@ public static void RunRightShiftTests() tempByteArray2 = new byte[] { (byte)32 }; VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); } + + // RightShift Method - All One Uint Large BigIntegers - 32 bit Shift + for (int i = 0; i < s_samples; i++) + { + tempByteArray1 = GetRandomLengthAllOnesUIntByteArray(s_random); + tempByteArray2 = new byte[] { (byte)32 }; + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + } + // RightShift Method - Large BigIntegers - large - Shift for (int i = 0; i < s_samples; i++) { @@ -211,6 +220,16 @@ private static byte[] GetRandomNegByteArray(Random random, int size) return value; } + private static byte[] GetRandomLengthAllOnesUIntByteArray(Random random) + { + int gap = random.Next(0, 128); + int byteLength = 4 + gap * 4 + 1; + byte[] array = new byte[byteLength]; + array[0] = 1; + array[^1] = 0xFF; + return array; + } + private static string Print(byte[] bytes) { return MyBigIntImp.Print(bytes); diff --git a/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs b/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs index 78384466f7fb10..3a5e3ad675b938 100644 --- a/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System/ArgumentNullExceptionTests.cs @@ -68,7 +68,6 @@ public static void ThrowIfNull_Null_ThrowsArgumentNullException(string paramName } [Fact] - [ActiveIssue("https://github.com/dotnet/csharplang/issues/287")] public static void ThrowIfNull_UsesArgumentExpression() { object something = null; diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/CallerArgumentExpressionAttributeTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/CallerArgumentExpressionAttributeTests.cs index 975690ed8c8355..f0a374e705ba12 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/CallerArgumentExpressionAttributeTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/CallerArgumentExpressionAttributeTests.cs @@ -7,204 +7,25 @@ namespace System.Runtime.CompilerServices.Tests { public static class CallerArgumentExpressionAttributeTests { - public static string IntParamMethod(int val, [CallerArgumentExpression("val")] string expr = null) + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("paramName")] + public static void Ctor_ParameterName_Roundtrip(string value) { - return expr; - } - - [Theory, InlineData("testParamName"), InlineData(""), InlineData(null)] - public static void ArgumentToCallerArgumentExpressionSetsParameterNameProperty(string paramName) - { - var attr = new CallerArgumentExpressionAttribute(paramName); - - Assert.Equal(paramName, attr.ParameterName); - } - - [Fact] - public static void SuppliedArgumentOverridesExpression() - { - int notVal = 0; - - string suppliedVal = "supplied value"; - Assert.Equal(suppliedVal, IntParamMethod(notVal, suppliedVal)); - - Assert.Equal(IntParamMethod(notVal), IntParamMethodPassthrough(notVal)); - } - - private static string IntParamMethodPassthrough(int val, [CallerArgumentExpression("val")] string expr = null) - { - return IntParamMethod(val, expr); - } - - [Fact] - public static void InvalidParameterName() - { - int notVal = 0; - - Assert.Null(InvalidParameterNameMethod(notVal)); - } - - private static string InvalidParameterNameMethod(int val, [CallerArgumentExpression("notVal")] string expr = null) - { - return expr; - } - - [Fact] - public static void NullParameterName() - { - int notVal = 0; - - Assert.Null(NullParameterNameMethod(notVal)); - } - - private static string NullParameterNameMethod(int val, [CallerArgumentExpression(null)] string expr = null) - { - return expr; - } - - [Fact] - public static void OverloadedMethodPrecedence() - { - int notVal = 0; - - Assert.Equal(OverloadedMethodReturn, OverloadedMethod(notVal)); - } - - private const string OverloadedMethodReturn = "not CallerArgumentExpression"; - - private static string OverloadedMethod(int val) - { - return OverloadedMethodReturn; - } - - private static string OverloadedMethod(int val, [CallerArgumentExpression(null)] string expr = null) - { - return expr; - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void SimpleExpression() - { - int notVal = 0; - - Assert.Equal("notVal", IntParamMethod(notVal)); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void ComplexExpression() - { - int x = 5; - - Assert.Equal("Math.Min(x + 20, x * x)", - IntParamMethod(Math.Min(x + 20, x * x))); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void SurroundingWhitespaceHandling() - { - int notVal = 0; - - Assert.Equal("notVal", IntParamMethod(notVal)); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void InternalWhitespaceHandling() - { - int notVal = 0; - - Assert.Equal("notVal + 20", IntParamMethod(notVal + 20)); - - Assert.Equal(@"Math.Min(notVal * 2, - notVal + 20)", - IntParamMethod(Math.Min(notVal * 2, - notVal + 20))); - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void InternalCommentHandling() - { - int notVal = 0; - - Assert.Equal("notVal + /*comment*/20", IntParamMethod(notVal + /*comment*/20)); - Assert.Equal("notVal + 20 //comment", - IntParamMethod(notVal + 20 //comment - )); + var caea = new CallerArgumentExpressionAttribute(value); + Assert.Equal(value, caea.ParameterName); } [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void OptionalParameterHandling() + public static void BasicTest() { - string suppliedVal = "supplied value"; - Assert.Equal("suppliedVal", OptionalParamMethod(suppliedVal)); - Assert.Equal("suppliedVal", OptionalParamMethod(val: suppliedVal)); - - Assert.Equal("StringConst + \" string literal\"", OptionalParamMethod()); - - Assert.Equal("\"no file\"", CompilerSuppliedParamMethod()); + // Just a quick test to validate basic behavior. Compiler tests validate it fully. + Assert.Equal("\"hello\"", GetValue("hello")); + Assert.Equal("3 + 2", GetValue(3 + 2)); + Assert.Equal("new object()", GetValue(new object())); } - private const string StringConst = "hello"; - - private static string OptionalParamMethod(string val = StringConst + " string literal", [CallerArgumentExpression("val")] string expr = null) - { - return expr; - } - - private static string CompilerSuppliedParamMethod([CallerFilePath] string val = "no file", [CallerArgumentExpression("val")] string expr = null) - { - return expr; - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void ExtensionMethodThisParameterHandling() - { - int notVal = 0; - - Assert.Equal("notVal", notVal.ExtensionMethod()); - } - - private static string ExtensionMethod(this int val, [CallerArgumentExpression("val")] string expr = null) - { - return expr; - } - - [Fact] - [ActiveIssue("https://github.com/dotnet/roslyn/issues/19605")] - public static void InstanceMethodThisHandling() - { - var instance = new InstanceTest(); - - Assert.Equal("instance", instance.Method()); - Assert.Equal("new InstanceTest()", new InstanceTest().Method()); - Assert.Equal("(instance ?? new InstanceTest())", (instance ?? new InstanceTest()).Method()); - - Assert.Equal("", instance.NoThisMethodCaller()); - Assert.Equal("this", instance.ThisMethodCaller()); - } - - private class InstanceTest - { - public string NoThisMethodCaller() - { - return Method(); - } - - public string ThisMethodCaller() - { - return this.Method(); - } - - public string Method([CallerArgumentExpression("this")] string expr = null) - { - return expr; - } - } + private static string GetValue(object argument, [CallerArgumentExpression("argument")] string expr = null) => expr; } } 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 7a83e35ce13bf1..fccdff3feb81a6 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 @@ -4,8 +4,8 @@ using System; using System.IO; using System.Diagnostics; -using System.Numerics; using System.Security.Cryptography; +using static System.Numerics.BitOperations; namespace Internal.Cryptography { @@ -342,11 +342,6 @@ private static unsafe void SHATransform(uint* expandedBuffer, uint* state, byte* state[7] += h; } - private static uint RotateRight(uint x, int n) - { - return (((x) >> (n)) | ((x) << (32 - (n)))); - } - private static uint Ch(uint x, uint y, uint z) { return ((x & y) ^ ((x ^ 0xffffffff) & z)); @@ -917,11 +912,6 @@ private static unsafe void SHATransform(ulong* expandedBuffer, ulong* state, byt state[7] += h; } - private static ulong RotateRight(ulong x, int n) - { - return (((x) >> (n)) | ((x) << (64 - (n)))); - } - private static ulong Ch(ulong x, ulong y, ulong z) { return ((x & y) ^ ((x ^ 0xffffffffffffffff) & z)); diff --git a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs index 9a67b46516e7d7..c4c7c051b2277f 100644 --- a/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs +++ b/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs @@ -637,7 +637,7 @@ unsafe static int TransformBlock(ICryptoTransform transform, ReadOnlyMemory.Shared.Rent(inputBuffer.Length); + byte[]? rentedBuffer = ArrayPool.Shared.Rent(inputBuffer.Length); int result = default; // Pin the rented buffer for security. @@ -655,7 +655,7 @@ unsafe static int TransformBlock(ICryptoTransform transform, ReadOnlyMemory.Shared.Return(rentedBuffer); - rentedBuffer = null!; + rentedBuffer = null; return result; } } @@ -667,7 +667,7 @@ public unsafe override void CopyTo(Stream destination, int bufferSize) CheckCopyToArguments(destination, bufferSize); // Use ArrayPool.Shared instead of CryptoPool because the array is passed out. - byte[] rentedBuffer = ArrayPool.Shared.Rent(bufferSize); + byte[]? rentedBuffer = ArrayPool.Shared.Rent(bufferSize); // Pin the array for security. fixed (byte* _ = &rentedBuffer[0]) { @@ -686,7 +686,7 @@ public unsafe override void CopyTo(Stream destination, int bufferSize) } } ArrayPool.Shared.Return(rentedBuffer); - rentedBuffer = null!; + rentedBuffer = null; } /// @@ -699,7 +699,7 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio private async Task CopyToAsyncInternal(Stream destination, int bufferSize, CancellationToken cancellationToken) { // Use ArrayPool.Shared instead of CryptoPool because the array is passed out. - byte[] rentedBuffer = ArrayPool.Shared.Rent(bufferSize); + byte[]? rentedBuffer = ArrayPool.Shared.Rent(bufferSize); // Pin the array for security. GCHandle pinHandle = GCHandle.Alloc(rentedBuffer, GCHandleType.Pinned); try @@ -717,7 +717,7 @@ private async Task CopyToAsyncInternal(Stream destination, int bufferSize, Cance pinHandle.Free(); } ArrayPool.Shared.Return(rentedBuffer); - rentedBuffer = null!; + rentedBuffer = null; } private void CheckCopyToArguments(Stream destination, int bufferSize) diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs index 942c6251f6cbaa..27f1df388e7f15 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509CertificateReader.cs @@ -267,15 +267,12 @@ public string Issuer { get { - if (_issuer == null) - { - // IssuerName is mutable to callers in X509Certificate. We want to be - // able to get the issuer even if IssuerName has been mutated, so we - // don't use it here. - _issuer = Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetIssuerName(_cert)).Name; - } - - return _issuer; + // IssuerName is mutable to callers in X509Certificate. We want to be + // able to get the issuer even if IssuerName has been mutated, so we + // don't use it here. + return _issuer ??= UseCertInteriorData(static cert => { + return Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetIssuerName(cert)).Name; + }); } } @@ -283,15 +280,12 @@ public string Subject { get { - if (_subject == null) - { - // SubjectName is mutable to callers in X509Certificate. We want to be - // able to get the subject even if SubjectName has been mutated, so we - // don't use it here. - _subject = Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetSubjectName(_cert)).Name; - } - - return _subject; + // SubjectName is mutable to callers in X509Certificate. We want to be + // able to get the subject even if SubjectName has been mutated, so we + // don't use it here. + return _subject ??= UseCertInteriorData(static cert => { + return Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetSubjectName(cert)).Name; + }); } } @@ -311,8 +305,10 @@ public string KeyAlgorithm { get { - IntPtr oidPtr = Interop.Crypto.GetX509PublicKeyAlgorithm(_cert); - return Interop.Crypto.GetOidValue(oidPtr); + return UseCertInteriorData(static cert => { + IntPtr oidPtr = Interop.Crypto.GetX509PublicKeyAlgorithm(cert); + return Interop.Crypto.GetOidValue(oidPtr); + }); } } @@ -328,8 +324,10 @@ public byte[] PublicKeyValue { get { - IntPtr keyBytesPtr = Interop.Crypto.GetX509PublicKeyBytes(_cert); - return Interop.Crypto.GetAsn1StringBytes(keyBytesPtr); + return UseCertInteriorData(static cert => { + IntPtr keyBytesPtr = Interop.Crypto.GetX509PublicKeyBytes(cert); + return Interop.Crypto.GetAsn1StringBytes(keyBytesPtr); + }); } } @@ -348,8 +346,10 @@ public string SignatureAlgorithm { get { - IntPtr oidPtr = Interop.Crypto.GetX509SignatureAlgorithm(_cert); - return Interop.Crypto.GetOidValue(oidPtr); + return UseCertInteriorData(static cert => { + IntPtr oidPtr = Interop.Crypto.GetX509SignatureAlgorithm(cert); + return Interop.Crypto.GetOidValue(oidPtr); + }); } } @@ -357,7 +357,10 @@ public DateTime NotAfter { get { - return ExtractValidityDateTime(Interop.Crypto.GetX509NotAfter(_cert)); + + return UseCertInteriorData(static cert => { + return ExtractValidityDateTime(Interop.Crypto.GetX509NotAfter(cert)); + }); } } @@ -365,7 +368,9 @@ public DateTime NotBefore { get { - return ExtractValidityDateTime(Interop.Crypto.GetX509NotBefore(_cert)); + return UseCertInteriorData(static cert => { + return ExtractValidityDateTime(Interop.Crypto.GetX509NotBefore(cert)); + }); } } @@ -421,12 +426,9 @@ public X500DistinguishedName SubjectName { get { - if (_subjectName == null) - { - _subjectName = Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetSubjectName(_cert)); - } - - return _subjectName; + return _subjectName ??= UseCertInteriorData(static cert => { + return Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetSubjectName(cert)); + }); } } @@ -434,90 +436,91 @@ public X500DistinguishedName IssuerName { get { - if (_issuerName == null) - { - _issuerName = Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetIssuerName(_cert)); - } - - return _issuerName; + return _issuerName ??= UseCertInteriorData(static cert => { + return Interop.Crypto.LoadX500Name(Interop.Crypto.X509GetIssuerName(cert)); + }); } } public PolicyData GetPolicyData() { - PolicyData policyData = default; + return UseCertInteriorData(static cert => { + PolicyData policyData = default; - int extensionCount = Interop.Crypto.X509GetExtCount(_cert); + int extensionCount = Interop.Crypto.X509GetExtCount(cert); - for (int i = 0; i < extensionCount; i++) - { - IntPtr ext = Interop.Crypto.X509GetExt(_cert, i); - Interop.Crypto.CheckValidOpenSslHandle(ext); + for (int i = 0; i < extensionCount; i++) + { + IntPtr ext = Interop.Crypto.X509GetExt(cert, i); + Interop.Crypto.CheckValidOpenSslHandle(ext); - IntPtr oidPtr = Interop.Crypto.X509ExtensionGetOid(ext); - Interop.Crypto.CheckValidOpenSslHandle(oidPtr); - string oidValue = Interop.Crypto.GetOidValue(oidPtr); + IntPtr oidPtr = Interop.Crypto.X509ExtensionGetOid(ext); + Interop.Crypto.CheckValidOpenSslHandle(oidPtr); + string oidValue = Interop.Crypto.GetOidValue(oidPtr); - IntPtr dataPtr = Interop.Crypto.X509ExtensionGetData(ext); - Interop.Crypto.CheckValidOpenSslHandle(dataPtr); + IntPtr dataPtr = Interop.Crypto.X509ExtensionGetData(ext); + Interop.Crypto.CheckValidOpenSslHandle(dataPtr); - switch (oidValue) - { - case Oids.ApplicationCertPolicies: - policyData.ApplicationCertPolicies = Interop.Crypto.GetAsn1StringBytes(dataPtr); - break; - case Oids.CertPolicies: - policyData.CertPolicies = Interop.Crypto.GetAsn1StringBytes(dataPtr); - break; - case Oids.CertPolicyMappings: - policyData.CertPolicyMappings = Interop.Crypto.GetAsn1StringBytes(dataPtr); - break; - case Oids.CertPolicyConstraints: - policyData.CertPolicyConstraints = Interop.Crypto.GetAsn1StringBytes(dataPtr); - break; - case Oids.EnhancedKeyUsage: - policyData.EnhancedKeyUsage = Interop.Crypto.GetAsn1StringBytes(dataPtr); - break; - case Oids.InhibitAnyPolicyExtension: - policyData.InhibitAnyPolicyExtension = Interop.Crypto.GetAsn1StringBytes(dataPtr); + switch (oidValue) + { + case Oids.ApplicationCertPolicies: + policyData.ApplicationCertPolicies = Interop.Crypto.GetAsn1StringBytes(dataPtr); + break; + case Oids.CertPolicies: + policyData.CertPolicies = Interop.Crypto.GetAsn1StringBytes(dataPtr); + break; + case Oids.CertPolicyMappings: + policyData.CertPolicyMappings = Interop.Crypto.GetAsn1StringBytes(dataPtr); + break; + case Oids.CertPolicyConstraints: + policyData.CertPolicyConstraints = Interop.Crypto.GetAsn1StringBytes(dataPtr); break; + case Oids.EnhancedKeyUsage: + policyData.EnhancedKeyUsage = Interop.Crypto.GetAsn1StringBytes(dataPtr); + break; + case Oids.InhibitAnyPolicyExtension: + policyData.InhibitAnyPolicyExtension = Interop.Crypto.GetAsn1StringBytes(dataPtr); + break; + } } - } - return policyData; + return policyData; + }); } public IEnumerable Extensions { get { - int extensionCount = Interop.Crypto.X509GetExtCount(_cert); - X509Extension[] extensions = new X509Extension[extensionCount]; + return UseCertInteriorData(static cert => { + int extensionCount = Interop.Crypto.X509GetExtCount(cert); + X509Extension[] extensions = new X509Extension[extensionCount]; - for (int i = 0; i < extensionCount; i++) - { - IntPtr ext = Interop.Crypto.X509GetExt(_cert, i); + for (int i = 0; i < extensionCount; i++) + { + IntPtr ext = Interop.Crypto.X509GetExt(cert, i); - Interop.Crypto.CheckValidOpenSslHandle(ext); + Interop.Crypto.CheckValidOpenSslHandle(ext); - IntPtr oidPtr = Interop.Crypto.X509ExtensionGetOid(ext); + IntPtr oidPtr = Interop.Crypto.X509ExtensionGetOid(ext); - Interop.Crypto.CheckValidOpenSslHandle(oidPtr); + Interop.Crypto.CheckValidOpenSslHandle(oidPtr); - string oidValue = Interop.Crypto.GetOidValue(oidPtr); - Oid oid = new Oid(oidValue); + string oidValue = Interop.Crypto.GetOidValue(oidPtr); + Oid oid = new Oid(oidValue); - IntPtr dataPtr = Interop.Crypto.X509ExtensionGetData(ext); + IntPtr dataPtr = Interop.Crypto.X509ExtensionGetData(ext); - Interop.Crypto.CheckValidOpenSslHandle(dataPtr); + Interop.Crypto.CheckValidOpenSslHandle(dataPtr); - byte[] extData = Interop.Crypto.GetAsn1StringBytes(dataPtr); - bool critical = Interop.Crypto.X509ExtensionGetCritical(ext); + byte[] extData = Interop.Crypto.GetAsn1StringBytes(dataPtr); + bool critical = Interop.Crypto.X509ExtensionGetCritical(ext); - extensions[i] = new X509Extension(oid, extData, critical); - } + extensions[i] = new X509Extension(oid, extData, critical); + } - return extensions; + return extensions; + }); } } @@ -850,5 +853,29 @@ public byte[] Export(X509ContentType contentType, SafePasswordHandle password) return exported; } } + + private T UseCertInteriorData(Func callback) + { + // Many of the reader's APIs perform two steps of getting an IntPtr to + // interior data of the X509* object, then passing that IntPtr to some + // other API that interprets the data in the pointer. If the SafeX509Handle + // is disposed in between the two calls, then the data in the IntPtr no longer + // points to valid data. + // To keep the X509 object alive, manually increment the reference to it. + bool addedRef = false; + + try + { + _cert.DangerousAddRef(ref addedRef); + return callback(_cert); + } + finally + { + if (addedRef) + { + _cert.DangerousRelease(); + } + } + } } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs index b227a05667773f..8cd7abe0660a88 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/UnixPkcs12Reader.cs @@ -167,6 +167,7 @@ private static void ReturnRentedContentInfos(ContentInfoAsn[] rentedContents) if (contentType == DecryptedSentinel) { ReadOnlyMemory content = rentedContents[i].Content; + rentedContents[i].Content = default; if (!MemoryMarshal.TryGetArray(content, out ArraySegment segment)) { @@ -174,7 +175,6 @@ private static void ReturnRentedContentInfos(ContentInfoAsn[] rentedContents) } CryptoPool.Return(segment); - rentedContents[0].Content = default; } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs index 4fae4694bd4176..73d0e7b2371278 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.iOS/AppleCertificatePal.Pem.cs @@ -63,8 +63,9 @@ internal static bool TryDecodePem(ReadOnlySpan rawData, DerCallback derCal X509ContentType.Pkcs7; bool cont = derCallback(certBytes.AsSpan(0, bytesWritten), contentType); - CryptoPool.Return(certBytes, clearSize: 0); + byte[] toReturn = certBytes; certBytes = null; + CryptoPool.Return(toReturn, clearSize: 0); if (!cont) { diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs index 24363f1ab4e427..98cd5c47cbdc7a 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/CertTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; +using System.Threading; using Microsoft.DotNet.XUnitExtensions; using Test.Cryptography; using Xunit; @@ -23,6 +24,34 @@ public CertTests(ITestOutputHelper output) _log = output; } + [Fact] + public static void RaceUseAndDisposeDoesNotCrash() + { + X509Certificate2 cert = new X509Certificate2(TestFiles.MicrosoftRootCertFile); + + Thread subjThread = new Thread(static state => { + X509Certificate2 c = (X509Certificate2)state; + + try + { + _ = c.Subject; + } + catch + { + // managed exceptions are okay, we are looking for runtime crashes. + } + }); + + Thread disposeThread = new Thread(static state => { + ((X509Certificate2)state).Dispose(); + }); + + subjThread.Start(cert); + disposeThread.Start(cert); + disposeThread.Join(); + subjThread.Join(); + } + [Fact] public static void X509CertTest() { diff --git a/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs index 334e5226a5c543..65270711f210fa 100644 --- a/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs +++ b/src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs @@ -13,7 +13,7 @@ namespace System.Text.Json.Serialization #else public #endif - class JsonSourceGenerationOptionsAttribute : JsonAttribute + sealed class JsonSourceGenerationOptionsAttribute : JsonAttribute { /// /// Specifies the default ignore condition. diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 58b6d523aad9aa..99a987c5792917 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -199,6 +199,15 @@ public static partial class JsonSerializer public static object? Deserialize(string json, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static object? Deserialize(string json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static object? Deserialize(this System.Text.Json.JsonDocument document, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static object? Deserialize(this System.Text.Json.JsonDocument document, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static object? Deserialize(this System.Text.Json.JsonElement element, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static object? Deserialize(this System.Text.Json.JsonElement element, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static object? Deserialize(this System.Text.Json.Nodes.JsonNode? node, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static object? Deserialize(this System.Text.Json.Nodes.JsonNode? node, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static object? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Type returnType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static object? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Type returnType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] @@ -222,6 +231,15 @@ public static partial class JsonSerializer public static TValue? Deserialize(string json, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize(string json, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static TValue? Deserialize(this System.Text.Json.JsonDocument document, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static TValue? Deserialize(this System.Text.Json.JsonDocument document, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static TValue? Deserialize(this System.Text.Json.JsonElement element, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static TValue? Deserialize(this System.Text.Json.JsonElement element, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static TValue? Deserialize(this System.Text.Json.Nodes.JsonNode? node, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static TValue? Deserialize(this System.Text.Json.Nodes.JsonNode? node, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static TValue? Deserialize< TValue>(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] @@ -240,6 +258,24 @@ public static void Serialize(System.Text.Json.Utf8JsonWriter writer, object? val public static System.Threading.Tasks.Task SerializeAsync(System.IO.Stream utf8Json, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task SerializeAsync(System.IO.Stream utf8Json, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Text.Json.JsonDocument SerializeToDocument(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static System.Text.Json.JsonDocument SerializeToDocument(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Text.Json.JsonDocument SerializeToDocument(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static System.Text.Json.JsonDocument SerializeToDocument(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Text.Json.JsonElement SerializeToElement(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static System.Text.Json.JsonElement SerializeToElement(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Text.Json.JsonElement SerializeToElement(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static System.Text.Json.JsonElement SerializeToElement(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Text.Json.Nodes.JsonNode? SerializeToNode(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static System.Text.Json.Nodes.JsonNode? SerializeToNode(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] + public static System.Text.Json.Nodes.JsonNode? SerializeToNode(TValue value, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } + public static System.Text.Json.Nodes.JsonNode? SerializeToNode(TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] public static byte[] SerializeToUtf8Bytes(object? value, System.Type inputType, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static byte[] SerializeToUtf8Bytes(object? value, System.Type inputType, System.Text.Json.Serialization.JsonSerializerContext context) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.")] @@ -852,7 +888,7 @@ protected JsonSerializerContext(System.Text.Json.JsonSerializerOptions? instance public abstract System.Text.Json.Serialization.Metadata.JsonTypeInfo? GetTypeInfo(System.Type type); } [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=false)] - public partial class JsonSourceGenerationOptionsAttribute : System.Text.Json.Serialization.JsonAttribute + public sealed partial class JsonSourceGenerationOptionsAttribute : System.Text.Json.Serialization.JsonAttribute { public JsonSourceGenerationOptionsAttribute() { } public System.Text.Json.Serialization.JsonIgnoreCondition DefaultIgnoreCondition { get { throw null; } set { } } diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 5a9ea1839ba691..031229db66af61 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -99,6 +99,12 @@ + + + + + + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs index 8df0c30e76b7ed..c287240964a5ca 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.Parse.cs @@ -46,7 +46,7 @@ public sealed partial class JsonDocument /// public static JsonDocument Parse(ReadOnlyMemory utf8Json, JsonDocumentOptions options = default) { - return Parse(utf8Json, options.GetReaderOptions(), null); + return Parse(utf8Json, options.GetReaderOptions()); } /// @@ -80,7 +80,7 @@ public static JsonDocument Parse(ReadOnlySequence utf8Json, JsonDocumentOp if (utf8Json.IsSingleSegment) { - return Parse(utf8Json.First, readerOptions, null); + return Parse(utf8Json.First, readerOptions); } int length = checked((int)utf8Json.Length); @@ -137,6 +137,15 @@ public static JsonDocument Parse(Stream utf8Json, JsonDocumentOptions options = } } + internal static JsonDocument ParseRented(PooledByteBufferWriter utf8Json, JsonDocumentOptions options = default) + { + return Parse( + utf8Json.WrittenMemory, + options.GetReaderOptions(), + extraRentedArrayPoolBytes: null, + extraPooledByteBufferWriter: utf8Json); + } + internal static JsonDocument ParseValue(Stream utf8Json, JsonDocumentOptions options) { Debug.Assert(utf8Json != null); @@ -664,14 +673,15 @@ JsonDocument Create(byte[] utf8Json) { MetadataDb database = MetadataDb.CreateLocked(utf8Json.Length); database.Append(tokenType, startLocation: 0, utf8Json.Length); - return new JsonDocument(utf8Json, database, extraRentedBytes: null); + return new JsonDocument(utf8Json, database); } } private static JsonDocument Parse( ReadOnlyMemory utf8Json, JsonReaderOptions readerOptions, - byte[]? extraRentedBytes) + byte[]? extraRentedArrayPoolBytes = null, + PooledByteBufferWriter? extraPooledByteBufferWriter = null) { ReadOnlySpan utf8JsonSpan = utf8Json.Span; var database = MetadataDb.CreateRented(utf8Json.Length, convertToAlloc: false); @@ -691,7 +701,7 @@ private static JsonDocument Parse( stack.Dispose(); } - return new JsonDocument(utf8Json, database, extraRentedBytes); + return new JsonDocument(utf8Json, database, extraRentedArrayPoolBytes, extraPooledByteBufferWriter); } private static JsonDocument ParseUnrented( @@ -729,7 +739,7 @@ private static JsonDocument ParseUnrented( } } - return new JsonDocument(utf8Json, database, extraRentedBytes: null); + return new JsonDocument(utf8Json, database); } private static ArraySegment ReadToEnd(Stream stream) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs index 26eabb07c1142d..2cb13212373b4d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.cs @@ -23,7 +23,13 @@ public sealed partial class JsonDocument : IDisposable { private ReadOnlyMemory _utf8Json; private MetadataDb _parsedData; - private byte[]? _extraRentedBytes; + + private byte[]? _extraRentedArrayPoolBytes; + private bool _hasExtraRentedArrayPoolBytes; + + private PooledByteBufferWriter? _extraPooledByteBufferWriter; + private bool _hasExtraPooledByteBufferWriter; + private (int, string?) _lastIndexAndString = (-1, null); internal bool IsDisposable { get; } @@ -36,19 +42,33 @@ public sealed partial class JsonDocument : IDisposable private JsonDocument( ReadOnlyMemory utf8Json, MetadataDb parsedData, - byte[]? extraRentedBytes, + byte[]? extraRentedArrayPoolBytes = null, + PooledByteBufferWriter? extraPooledByteBufferWriter = null, bool isDisposable = true) { Debug.Assert(!utf8Json.IsEmpty); + // We never have both rented fields. + Debug.Assert(extraRentedArrayPoolBytes == null || extraPooledByteBufferWriter == null); + _utf8Json = utf8Json; _parsedData = parsedData; - _extraRentedBytes = extraRentedBytes; + + if (_extraRentedArrayPoolBytes != null) + { + _hasExtraRentedArrayPoolBytes = true; + _extraRentedArrayPoolBytes = extraRentedArrayPoolBytes; + } + else if (extraPooledByteBufferWriter != null) + { + _hasExtraPooledByteBufferWriter = true; + _extraPooledByteBufferWriter = extraPooledByteBufferWriter; + } IsDisposable = isDisposable; - // extraRentedBytes better be null if we're not disposable. - Debug.Assert(isDisposable || extraRentedBytes == null); + // Both rented fields better be null if we're not disposable. + Debug.Assert(isDisposable || (_extraRentedArrayPoolBytes == null && _extraPooledByteBufferWriter == null)); } /// @@ -63,14 +83,22 @@ public void Dispose() _parsedData.Dispose(); _utf8Json = ReadOnlyMemory.Empty; - // When "extra rented bytes exist" they contain the document, - // and thus need to be cleared before being returned. - byte[]? extraRentedBytes = Interlocked.Exchange(ref _extraRentedBytes, null); + if (_hasExtraRentedArrayPoolBytes) + { + byte[]? extraRentedBytes = Interlocked.Exchange(ref _extraRentedArrayPoolBytes, null); - if (extraRentedBytes != null) + if (extraRentedBytes != null) + { + // When "extra rented bytes exist" it contains the document, + // and thus needs to be cleared before being returned. + extraRentedBytes.AsSpan(0, length).Clear(); + ArrayPool.Shared.Return(extraRentedBytes); + } + } + else if (_hasExtraPooledByteBufferWriter) { - extraRentedBytes.AsSpan(0, length).Clear(); - ArrayPool.Shared.Return(extraRentedBytes); + PooledByteBufferWriter? extraBufferWriter = Interlocked.Exchange(ref _extraPooledByteBufferWriter, null); + extraBufferWriter?.Dispose(); } } @@ -184,7 +212,12 @@ internal int GetEndIndex(int index, bool includeEndElement) return endIndex; } - private ReadOnlyMemory GetRawValue(int index, bool includeQuotes) + internal ReadOnlyMemory GetRootRawValue() + { + return GetRawValue(0, includeQuotes : true); + } + + internal ReadOnlyMemory GetRawValue(int index, bool includeQuotes) { CheckNotDisposed(); @@ -764,7 +797,12 @@ internal JsonElement CloneElement(int index) ReadOnlyMemory segmentCopy = GetRawValue(index, includeQuotes: true).ToArray(); JsonDocument newDocument = - new JsonDocument(segmentCopy, newDb, extraRentedBytes: null, isDisposable: false); + new JsonDocument( + segmentCopy, + newDb, + extraRentedArrayPoolBytes: null, + extraPooledByteBufferWriter: null, + isDisposable: false); return newDocument.RootElement; } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs index 18e69d8b0010fa..5f30dd6d3dd8b1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonElement.cs @@ -1169,6 +1169,13 @@ public string GetRawText() return _parent.GetRawValueAsString(_idx); } + internal ReadOnlyMemory GetRawValue() + { + CheckValidInstance(); + + return _parent.GetRawValue(_idx, includeQuotes: true); + } + internal string GetPropertyRawText() { CheckValidInstance(); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs index cbc393464b1160..2abaaf05ce5c21 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.To.cs @@ -12,15 +12,13 @@ public abstract partial class JsonNode /// JSON representation of current instance. public string ToJsonString(JsonSerializerOptions? options = null) { - using (var output = new PooledByteBufferWriter(JsonSerializerOptions.BufferSizeDefault)) + using var output = new PooledByteBufferWriter(JsonSerializerOptions.BufferSizeDefault); + using (var writer = new Utf8JsonWriter(output, options == null ? default(JsonWriterOptions) : options.GetWriterOptions())) { - using (var writer = new Utf8JsonWriter(output, options == null ? default(JsonWriterOptions) : options.GetWriterOptions())) - { - WriteTo(writer, options); - } - - return JsonHelpers.Utf8GetString(output.WrittenMemory.ToArray()); + WriteTo(writer, options); } + + return JsonHelpers.Utf8GetString(output.WrittenMemory.ToArray()); } /// @@ -44,15 +42,13 @@ public override string ToString() } } - using (var output = new PooledByteBufferWriter(JsonSerializerOptions.BufferSizeDefault)) + using var output = new PooledByteBufferWriter(JsonSerializerOptions.BufferSizeDefault); + using (var writer = new Utf8JsonWriter(output, new JsonWriterOptions { Indented = true })) { - using (var writer = new Utf8JsonWriter(output, new JsonWriterOptions { Indented = true })) - { - WriteTo(writer); - } - - return JsonHelpers.Utf8GetString(output.WrittenMemory.ToArray()); + WriteTo(writer); } + + return JsonHelpers.Utf8GetString(output.WrittenMemory.ToArray()); } /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs index 07da2c99ebffa8..8d0f7bb437f10d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonNodeConverter.cs @@ -28,6 +28,7 @@ public override void Write(Utf8JsonWriter writer, JsonNode? value, JsonSerialize if (value == null) { writer.WriteNullValue(); + // Note JsonSerializer.Deserialize(JsonNode?) also calls WriteNullValue() for a null + root JsonNode. } else { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs index 051475001e1941..02c714900b0a3e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs @@ -32,6 +32,7 @@ protected override bool ReadAndCacheConstructorArgument(ref ReadStack state, ref protected override object CreateObject(ref ReadStackFrame frame) { object[] arguments = (object[])frame.CtorArgumentState!.Arguments; + frame.CtorArgumentState.Arguments = null!; var createObject = (JsonTypeInfo.ParameterizedConstructorDelegate?)frame.JsonTypeInfo.CreateObjectWithArgs; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs index 867b74f2996bfc..528ac02b9ef4d5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.cs @@ -76,8 +76,9 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo ReadPropertyValue(obj, ref state, ref tempReader, jsonPropertyInfo, useExtensionProperty); } - ArrayPool.Shared.Return(argumentState.FoundProperties!, clearArray: true); + FoundProperty[] toReturn = argumentState.FoundProperties!; argumentState.FoundProperties = null; + ArrayPool.Shared.Return(toReturn, clearArray: true); } } else @@ -133,8 +134,9 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo } } - ArrayPool.Shared.Return(argumentState.FoundPropertiesAsync!, clearArray: true); + FoundPropertyAsync[] toReturn = argumentState.FoundPropertiesAsync!; argumentState.FoundPropertiesAsync = null; + ArrayPool.Shared.Return(toReturn, clearArray: true); } } @@ -238,9 +240,10 @@ private void ReadConstructorArguments(ref ReadStack state, ref Utf8JsonReader re argumentState.FoundProperties.CopyTo(newCache, 0); - ArrayPool.Shared.Return(argumentState.FoundProperties, clearArray: true); - + FoundProperty[] toReturn = argumentState.FoundProperties; argumentState.FoundProperties = newCache!; + + ArrayPool.Shared.Return(toReturn, clearArray: true); } argumentState.FoundProperties[argumentState.FoundPropertyCount++] = ( @@ -436,9 +439,10 @@ private bool HandlePropertyWithContinuation( argumentState.FoundPropertiesAsync!.CopyTo(newCache, 0); - ArrayPool.Shared.Return(argumentState.FoundPropertiesAsync!, clearArray: true); - + FoundPropertyAsync[] toReturn = argumentState.FoundPropertiesAsync!; argumentState.FoundPropertiesAsync = newCache!; + + ArrayPool.Shared.Return(toReturn, clearArray: true); } // Cache the property name and value. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteArrayConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteArrayConverter.cs index 87bcf3375e2082..c8ea8e09f11fc3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteArrayConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ByteArrayConverter.cs @@ -5,14 +5,26 @@ namespace System.Text.Json.Serialization.Converters { internal sealed class ByteArrayConverter : JsonConverter { - public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override byte[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + return reader.GetBytesFromBase64(); } - public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, byte[]? value, JsonSerializerOptions options) { - writer.WriteBase64StringValue(value); + if (value == null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteBase64StringValue(value); + } } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Document.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Document.cs new file mode 100644 index 00000000000000..45f78a979310e2 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Document.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.CodeAnalysis; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json +{ + public static partial class JsonSerializer + { + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// Options to control the behavior during parsing. + /// + /// is . + /// + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static TValue? Deserialize(this JsonDocument document, JsonSerializerOptions? options = null) + { + if (document == null) + { + throw new ArgumentNullException(nameof(document)); + } + + return ReadUsingOptions(document, typeof(TValue), options); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// The type of the object to convert to and return. + /// Options to control the behavior during parsing. + /// + /// or is . + /// + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static object? Deserialize(this JsonDocument document, Type returnType, JsonSerializerOptions? options = null) + { + if (document == null) + { + throw new ArgumentNullException(nameof(document)); + } + + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + return ReadUsingOptions(document, returnType, options); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// Metadata about the type to convert. + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + public static TValue? Deserialize(this JsonDocument document, JsonTypeInfo jsonTypeInfo) + { + if (document == null) + { + throw new ArgumentNullException(nameof(document)); + } + + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return ReadUsingMetadata(document, jsonTypeInfo); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// The type of the object to convert to and return. + /// A metadata provider for serializable types. + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// The JSON is invalid. + /// + /// -or- + /// + /// is not compatible with the JSON. + /// + /// -or- + /// + /// There is remaining data in the string beyond a single JSON value. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method of the provided + /// returns for the type to convert. + /// + public static object? Deserialize(this JsonDocument document, Type returnType, JsonSerializerContext context) + { + if (document == null) + { + throw new ArgumentNullException(nameof(document)); + } + + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ReadUsingMetadata(document, GetTypeInfo(context, returnType)); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static TValue? ReadUsingOptions(JsonDocument document, Type returnType, JsonSerializerOptions? options) => + ReadUsingMetadata(document, GetTypeInfo(returnType, options)); + + private static TValue? ReadUsingMetadata(JsonDocument document, JsonTypeInfo jsonTypeInfo) + { + ReadOnlySpan utf8Json = document.GetRootRawValue().Span; + return ReadUsingMetadata(utf8Json, jsonTypeInfo); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Element.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Element.cs new file mode 100644 index 00000000000000..9419f646975b72 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Element.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.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json +{ + public static partial class JsonSerializer + { + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// Options to control the behavior during parsing. + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static TValue? Deserialize(this JsonElement element, JsonSerializerOptions? options = null) => + ReadUsingOptions(element, typeof(TValue), options); + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// The type of the object to convert to and return. + /// Options to control the behavior during parsing. + /// + /// is . + /// + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static object? Deserialize(this JsonElement element, Type returnType, JsonSerializerOptions? options = null) + { + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + return ReadUsingOptions(element, returnType, options); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// Metadata about the type to convert. + /// + /// is . + /// + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + public static TValue? Deserialize(this JsonElement element, JsonTypeInfo jsonTypeInfo) + { + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return ReadUsingMetadata(element, jsonTypeInfo); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// The type of the object to convert to and return. + /// A metadata provider for serializable types. + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// The JSON is invalid. + /// + /// -or- + /// + /// is not compatible with the JSON. + /// + /// -or- + /// + /// There is remaining data in the string beyond a single JSON value. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method of the provided + /// returns for the type to convert. + /// + public static object? Deserialize(this JsonElement element, Type returnType, JsonSerializerContext context) + { + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ReadUsingMetadata(element, GetTypeInfo(context, returnType)); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static TValue? ReadUsingOptions(JsonElement element, Type returnType, JsonSerializerOptions? options) => + ReadUsingMetadata(element, GetTypeInfo(returnType, options)); + + private static TValue? ReadUsingMetadata(JsonElement element, JsonTypeInfo jsonTypeInfo) + { + ReadOnlySpan utf8Json = element.GetRawValue().Span; + return ReadUsingMetadata(utf8Json, jsonTypeInfo); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Node.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Node.cs new file mode 100644 index 00000000000000..32de1de46da955 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.Node.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json +{ + public static partial class JsonSerializer + { + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// Options to control the behavior during parsing. + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static TValue? Deserialize(this JsonNode? node, JsonSerializerOptions? options = null) + { + return ReadUsingOptions(node, typeof(TValue), options); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// The type of the object to convert to and return. + /// Options to control the behavior during parsing. + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static object? Deserialize(this JsonNode? node, Type returnType, JsonSerializerOptions? options = null) + { + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + return ReadUsingOptions(node, returnType, options); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// Metadata about the type to convert. + /// + /// is . + /// + /// + /// is not compatible with the JSON. + /// + /// + /// There is no compatible + /// for or its serializable members. + /// + public static TValue? Deserialize(this JsonNode? node, JsonTypeInfo jsonTypeInfo) + { + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return ReadUsingMetadata(node, jsonTypeInfo); + } + + /// + /// Converts the representing a single JSON value into a . + /// + /// A representation of the JSON value. + /// The to convert. + /// The type of the object to convert to and return. + /// A metadata provider for serializable types. + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// The JSON is invalid. + /// + /// -or- + /// + /// is not compatible with the JSON. + /// + /// -or- + /// + /// There is remaining data in the string beyond a single JSON value. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method of the provided + /// returns for the type to convert. + /// + public static object? Deserialize(this JsonNode? node, Type returnType, JsonSerializerContext context) + { + if (returnType == null) + { + throw new ArgumentNullException(nameof(returnType)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ReadUsingMetadata(node, GetTypeInfo(context, returnType)); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static TValue? ReadUsingOptions(JsonNode? node, Type returnType, JsonSerializerOptions? options) => + ReadUsingMetadata(node, GetTypeInfo(returnType, options)); + + private static TValue? ReadUsingMetadata(JsonNode? node, JsonTypeInfo jsonTypeInfo) + { + JsonSerializerOptions options = jsonTypeInfo.Options; + Debug.Assert(options != null); + + // For performance, share the same buffer across serialization and deserialization. + using var output = new PooledByteBufferWriter(options.DefaultBufferSize); + using (var writer = new Utf8JsonWriter(output, options.GetWriterOptions())) + { + if (node is null) + { + writer.WriteNullValue(); + } + else + { + node.WriteTo(writer, options); + } + } + + return ReadUsingMetadata(output.WrittenMemory.Span, jsonTypeInfo); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.String.cs index 29e06ef3742f1b..50add92998c01c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.String.cs @@ -262,7 +262,7 @@ public static partial class JsonSerializer /// The type of the object to convert to and return. /// A metadata provider for serializable types. /// - /// is . + /// or is . /// /// -or- /// @@ -307,7 +307,7 @@ public static partial class JsonSerializer /// The type of the object to convert to and return. /// A metadata provider for serializable types. /// - /// is . + /// or is . /// /// -or- /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.ByteArray.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.ByteArray.cs index b64bceadfda0a4..880ce5528b70c3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.ByteArray.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.ByteArray.cs @@ -122,15 +122,13 @@ private static byte[] WriteCoreBytes(in TValue value, JsonTypeInfo jsonT { JsonSerializerOptions options = jsonTypeInfo.Options; - using (var output = new PooledByteBufferWriter(options.DefaultBufferSize)) + using var output = new PooledByteBufferWriter(options.DefaultBufferSize); + using (var writer = new Utf8JsonWriter(output, options.GetWriterOptions())) { - using (var writer = new Utf8JsonWriter(output, options.GetWriterOptions())) - { - WriteUsingMetadata(writer, value, jsonTypeInfo); - } - - return output.WrittenMemory.ToArray(); + WriteUsingMetadata(writer, value, jsonTypeInfo); } + + return output.WrittenMemory.ToArray(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Document.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Document.cs new file mode 100644 index 00000000000000..1c5d7c75026d77 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Document.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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json +{ + public static partial class JsonSerializer + { + /// + /// Convert the provided value into a . + /// + /// A representation of the JSON value. + /// The value to convert. + /// Options to control the conversion behavior. + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static JsonDocument SerializeToDocument(TValue value, JsonSerializerOptions? options = null) => + WriteDocument(value, GetRuntimeType(value), options); + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// The type of the to convert. + /// Options to control the conversion behavior. + /// + /// is not compatible with . + /// + /// + /// + /// is . + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerOptions? options = null) => + WriteDocument( + value, + GetRuntimeTypeAndValidateInputType(value, inputType), + options); + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// Metadata about the type to convert. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// is . + /// + public static JsonDocument SerializeToDocument(TValue value, JsonTypeInfo jsonTypeInfo) + { + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return WriteDocument(value, jsonTypeInfo); + } + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// The type of the to convert. + /// A metadata provider for serializable types. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method of the provided + /// returns for the type to convert. + /// + /// + /// or is . + /// + public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Type runtimeType = GetRuntimeTypeAndValidateInputType(value, inputType); + return WriteDocument(value, GetTypeInfo(context, runtimeType)); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static JsonDocument WriteDocument(in TValue value, Type runtimeType, JsonSerializerOptions? options) + { + JsonTypeInfo typeInfo = GetTypeInfo(runtimeType, options); + return WriteDocument(value, typeInfo); + } + + private static JsonDocument WriteDocument(in TValue value, JsonTypeInfo jsonTypeInfo) + { + JsonSerializerOptions options = jsonTypeInfo.Options; + Debug.Assert(options != null); + + // For performance, share the same buffer across serialization and deserialization. + // The PooledByteBufferWriter is cleared and returned when JsonDocument.Dispose() is called. + PooledByteBufferWriter output = new(options.DefaultBufferSize); + using (Utf8JsonWriter writer = new(output, options.GetWriterOptions())) + { + WriteUsingMetadata(writer, value, jsonTypeInfo); + } + + return JsonDocument.ParseRented(output, options.GetDocumentOptions()); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Element.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Element.cs new file mode 100644 index 00000000000000..a606feea10221d --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Element.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json +{ + public static partial class JsonSerializer + { + /// + /// Convert the provided value into a . + /// + /// A representation of the JSON value. + /// The value to convert. + /// Options to control the conversion behavior. + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static JsonElement SerializeToElement(TValue value, JsonSerializerOptions? options = null) => + WriteElement(value, GetRuntimeType(value), options); + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// The type of the to convert. + /// Options to control the conversion behavior. + /// + /// is not compatible with . + /// + /// + /// + /// is . + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static JsonElement SerializeToElement(object? value, Type inputType, JsonSerializerOptions? options = null) => + WriteElement( + value, + GetRuntimeTypeAndValidateInputType(value, inputType), + options); + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// Metadata about the type to convert. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// is . + /// + public static JsonElement SerializeToElement(TValue value, JsonTypeInfo jsonTypeInfo) + { + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return WriteElement(value, jsonTypeInfo); + } + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// The type of the to convert. + /// A metadata provider for serializable types. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method of the provided + /// returns for the type to convert. + /// + /// + /// or is . + /// + public static JsonElement SerializeToElement(object? value, Type inputType, JsonSerializerContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Type runtimeType = GetRuntimeTypeAndValidateInputType(value, inputType); + return WriteElement(value, GetTypeInfo(context, runtimeType)); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static JsonElement WriteElement(in TValue value, Type runtimeType, JsonSerializerOptions? options) + { + JsonTypeInfo typeInfo = GetTypeInfo(runtimeType, options); + return WriteElement(value, typeInfo); + } + + private static JsonElement WriteElement(in TValue value, JsonTypeInfo jsonTypeInfo) + { + JsonSerializerOptions options = jsonTypeInfo.Options; + Debug.Assert(options != null); + + // For performance, share the same buffer across serialization and deserialization. + using var output = new PooledByteBufferWriter(options.DefaultBufferSize); + using (var writer = new Utf8JsonWriter(output, options.GetWriterOptions())) + { + WriteUsingMetadata(writer, value, jsonTypeInfo); + } + + return JsonElement.ParseValue(output.WrittenMemory.Span, options.GetDocumentOptions()); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Node.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Node.cs new file mode 100644 index 00000000000000..28856f04cd330d --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.Node.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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json +{ + public static partial class JsonSerializer + { + /// + /// Convert the provided value into a . + /// + /// A representation of the JSON value. + /// The value to convert. + /// Options to control the conversion behavior. + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static JsonNode? SerializeToNode(TValue value, JsonSerializerOptions? options = null) => + WriteNode(value, GetRuntimeType(value), options); + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// The type of the to convert. + /// Options to control the conversion behavior. + /// + /// is not compatible with . + /// + /// + /// + /// is . + /// + /// There is no compatible + /// for or its serializable members. + /// + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + public static JsonNode? SerializeToNode(object? value, Type inputType, JsonSerializerOptions? options = null) => + WriteNode( + value, + GetRuntimeTypeAndValidateInputType(value, inputType), + options); + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// Metadata about the type to convert. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// is . + /// + public static JsonNode? SerializeToNode(TValue value, JsonTypeInfo jsonTypeInfo) + { + if (jsonTypeInfo == null) + { + throw new ArgumentNullException(nameof(jsonTypeInfo)); + } + + return WriteNode(value, jsonTypeInfo); + } + + /// + /// Convert the provided value into a . + /// + /// A representation of the value. + /// The value to convert. + /// The type of the to convert. + /// A metadata provider for serializable types. + /// + /// There is no compatible + /// for or its serializable members. + /// + /// + /// The method of the provided + /// returns for the type to convert. + /// + /// + /// or is . + /// + public static JsonNode? SerializeToNode(object? value, Type inputType, JsonSerializerContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + Type runtimeType = GetRuntimeTypeAndValidateInputType(value, inputType); + return WriteNode(value, GetTypeInfo(context, runtimeType)); + } + + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] + private static JsonNode? WriteNode(in TValue value, Type runtimeType, JsonSerializerOptions? options) + { + JsonTypeInfo typeInfo = GetTypeInfo(runtimeType, options); + return WriteNode(value, typeInfo); + } + + private static JsonNode? WriteNode(in TValue value, JsonTypeInfo jsonTypeInfo) + { + JsonSerializerOptions options = jsonTypeInfo.Options; + Debug.Assert(options != null); + + // For performance, share the same buffer across serialization and deserialization. + using var output = new PooledByteBufferWriter(options.DefaultBufferSize); + using (var writer = new Utf8JsonWriter(output, options.GetWriterOptions())) + { + WriteUsingMetadata(writer, value, jsonTypeInfo); + } + + return JsonNode.Parse(output.WrittenMemory.Span, options.GetNodeOptions()); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs index db52ffc079ffe3..62d408aa8105ef 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs @@ -43,6 +43,9 @@ public static string Serialize(TValue value, JsonSerializerOptions? opti /// There is no compatible /// for or its serializable members. /// + /// + /// is . + /// /// Using a is not as efficient as using UTF-8 /// encoding since the implementation internally uses UTF-8. See also /// and . @@ -96,6 +99,9 @@ public static string Serialize(TValue value, JsonTypeInfo jsonTy /// The method of the provided /// returns for the type to convert. /// + /// + /// or is . + /// /// Using a is not as efficient as using UTF-8 /// encoding since the implementation internally uses UTF-8. See also /// and . diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index b37f11ea941cf3..49e3813b2a16e4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -643,6 +643,16 @@ internal void ClearClasses() _lastClass = null; } + internal JsonDocumentOptions GetDocumentOptions() + { + return new JsonDocumentOptions + { + AllowTrailingCommas = AllowTrailingCommas, + CommentHandling = ReadCommentHandling, + MaxDepth = MaxDepth + }; + } + internal JsonNodeOptions GetNodeOptions() { return new JsonNodeOptions diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs index 395c5f9db1cd18..d909716eb35260 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs @@ -25,8 +25,11 @@ public void Dispose() { // Clear only what we used and return the buffer to the pool new Span(Buffer, 0, ClearMax).Clear(); - ArrayPool.Shared.Return(Buffer); + + byte[] toReturn = Buffer; Buffer = null!; + + ArrayPool.Shared.Return(toReturn); } } } diff --git a/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs index eb67440c0d1883..10311baf8e57ac 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapperForString.cs @@ -8,6 +8,12 @@ namespace System.Text.Json.Serialization.Tests { public abstract partial class JsonSerializerWrapperForString { + /// + /// Do the deserialize methods allow a value of 'null'. + /// For example, deserializing JSON to a String supports null by returning a 'null' String reference from a literal value of "null". + /// + protected internal abstract bool SupportsNullValueOnDeserialize { get; } + protected internal abstract Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null); protected internal abstract Task SerializeWrapper(T value, JsonSerializerOptions options = null); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs index c119e7fe504cf8..6f832228b68e63 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.cs @@ -14,6 +14,8 @@ internal sealed class StringSerializerWrapper : JsonSerializerWrapperForString private readonly JsonSerializerContext _defaultContext; private readonly Func _customContextCreator; + protected internal override bool SupportsNullValueOnDeserialize => false; + public StringSerializerWrapper(JsonSerializerContext defaultContext, Func customContextCreator) { _defaultContext = defaultContext ?? throw new ArgumentNullException(nameof(defaultContext)); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs index 6f917532323bd7..913c9196a08684 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.ReadTests.cs @@ -29,9 +29,22 @@ public static void ReadObjectArray() [Fact] public static void ReadNullByteArray() { - string json = @"null"; - byte[] arr = JsonSerializer.Deserialize(json); + byte[] arr = JsonSerializer.Deserialize("null"); Assert.Null(arr); + + PocoWithByteArrayProperty poco = JsonSerializer.Deserialize(@"{""Value"":null}"); + Assert.Null(poco.Value); + + byte[][] jaggedArr = JsonSerializer.Deserialize(@"[null]"); + Assert.Null(jaggedArr[0]); + + Dictionary dict = JsonSerializer.Deserialize>(@"{""key"":null}"); + Assert.Null(dict["key"]); + } + + public class PocoWithByteArrayProperty + { + public byte[] Value { get; set; } } [Fact] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.WriteTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.WriteTests.cs index 4ada24a6f013cf..909611ca2c7323 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.WriteTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Array.WriteTests.cs @@ -40,12 +40,27 @@ public static void WriteEmptyByteArray() Assert.Equal(@"""""", json); } - [Fact] - public static void WriteByteArray() + [Theory] + [InlineData(null, "null")] + [InlineData(new byte[] { }, "\"\"")] + [InlineData(new byte[] { 1, 2 }, "\"AQI=\"")] + public static void WriteByteArray(byte[]? input, string expectedEncoding) { - var input = new byte[] { 1, 2 }; + // root-level serialization string json = JsonSerializer.Serialize(input); - Assert.Equal($"\"{Convert.ToBase64String(input)}\"", json); + Assert.Equal(expectedEncoding, json); + + // object property + json = JsonSerializer.Serialize(new { Property = input }); + Assert.Equal(@$"{{""Property"":{expectedEncoding}}}", json); + + // array element + json = JsonSerializer.Serialize(new[] { input }); + Assert.Equal($"[{expectedEncoding}]", json); + + // dictionary entry + json = JsonSerializer.Serialize(new Dictionary { ["key"] = input }); + Assert.Equal(@$"{{""key"":{expectedEncoding}}}", json); } [Fact] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs new file mode 100644 index 00000000000000..874e87e963b543 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using System.Text.Json.Nodes; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + /// + /// Provides basic tests for serializing To\From DOM types including JsonDocument, JsonElement and JsonNode. + /// + /// The test class provides tests for the JsonTypeInfo and JsonContext permutations. + /// The test class provides tests for input validation. + public static class DomTests + { + private const string Escaped_PlusSign = "\"\\u002B\""; // A '+' sign is escaped as hex. + + private class MyPoco + { + public static MyPoco Create() + { + return new MyPoco { StringProp = "Hello", IntArrayProp = new int[] { 1, 2 } }; + } + + [JsonPropertyOrder(0)] + public string StringProp { get; set; } + + [JsonPropertyOrder(1)] + public int[] IntArrayProp { get; set; } + + public void Verify() + { + Assert.Equal("Hello", StringProp); + Assert.Equal(1, IntArrayProp[0]); + Assert.Equal(2, IntArrayProp[1]); + } + } + + public const string Json = + "{\"StringProp\":\"Hello\",\"IntArrayProp\":[1,2]}"; + + [Fact] + public static void JsonDocumentDeserialize_Generic() + { + using JsonDocument dom = JsonDocument.Parse(Json); + MyPoco obj = dom.Deserialize(); + obj.Verify(); + } + + [Fact] + public static void JsonDocumentDeserialize_NonGeneric() + { + using JsonDocument dom = JsonDocument.Parse(Json); + MyPoco obj = (MyPoco)dom.Deserialize(typeof(MyPoco)); + obj.Verify(); + } + + [Fact] + public static void JsonDocumentDeserialize_Null() + { + using JsonDocument dom = JsonDocument.Parse("null"); + MyPoco obj = dom.Deserialize(); + Assert.Null(obj); + } + + [Fact] + public static void JsonElementDeserialize_Generic() + { + using JsonDocument document = JsonDocument.Parse(Json); + JsonElement dom = document.RootElement; + MyPoco obj = JsonSerializer.Deserialize(dom); + obj.Verify(); + } + + [Fact] + public static void JsonElementDeserialize_NonGeneric() + { + using JsonDocument document = JsonDocument.Parse(Json); + JsonElement dom = document.RootElement; + MyPoco obj = (MyPoco)JsonSerializer.Deserialize(dom, typeof(MyPoco)); + obj.Verify(); + } + + [Fact] + public static void JsonElementDeserialize_Null() + { + using JsonDocument document = JsonDocument.Parse("null"); + JsonElement dom = document.RootElement; + MyPoco obj = dom.Deserialize(); + Assert.Null(obj); + } + + [Fact] + public static void JsonElementDeserialize_FromChildElement() + { + using JsonDocument document = JsonDocument.Parse(Json); + JsonElement dom = document.RootElement.GetProperty("IntArrayProp"); + int[] arr = JsonSerializer.Deserialize(dom); + Assert.Equal(1, arr[0]); + Assert.Equal(2, arr[1]); + } + + [Fact] + public static void JsonNodeDeserialize_Generic() + { + JsonNode dom = JsonNode.Parse(Json); + MyPoco obj = dom.Deserialize(); + obj.Verify(); + } + + [Fact] + public static void JsonNodeDeserialize_NonGeneric() + { + JsonNode dom = JsonNode.Parse(Json); + MyPoco obj = (MyPoco)dom.Deserialize(typeof(MyPoco)); + obj.Verify(); + } + + [Fact] + public static void JsonNodeDeserialize_Null() + { + JsonNode node = null; + MyPoco obj = JsonSerializer.Deserialize(node); + Assert.Null(obj); + } + + [Fact] + public static void JsonElementDeserialize_FromChildNode() + { + JsonNode dom = JsonNode.Parse(Json)["IntArrayProp"]; + int[] arr = JsonSerializer.Deserialize(dom); + Assert.Equal(1, arr[0]); + Assert.Equal(2, arr[1]); + } + + [Fact] + public static void SerializeToDocument() + { + MyPoco obj = MyPoco.Create(); + using JsonDocument dom = JsonSerializer.SerializeToDocument(obj); + + JsonElement stringProp = dom.RootElement.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + + JsonElement[] elements = dom.RootElement.GetProperty("IntArrayProp").EnumerateArray().ToArray(); + Assert.Equal(JsonValueKind.Number, elements[0].ValueKind); + Assert.Equal(1, elements[0].GetInt32()); + Assert.Equal(JsonValueKind.Number, elements[1].ValueKind); + Assert.Equal(2, elements[1].GetInt32()); + } + + [Fact] + public static void SerializeToElement() + { + MyPoco obj = MyPoco.Create(); + JsonElement dom = JsonSerializer.SerializeToElement(obj); + + JsonElement stringProp = dom.GetProperty("StringProp"); + Assert.Equal(JsonValueKind.String, stringProp.ValueKind); + Assert.Equal("Hello", stringProp.ToString()); + + JsonElement[] elements = dom.GetProperty("IntArrayProp").EnumerateArray().ToArray(); + Assert.Equal(JsonValueKind.Number, elements[0].ValueKind); + Assert.Equal(1, elements[0].GetInt32()); + Assert.Equal(JsonValueKind.Number, elements[1].ValueKind); + Assert.Equal(2, elements[1].GetInt32()); + } + + [Fact] + public static void SerializeToNode() + { + MyPoco obj = MyPoco.Create(); + JsonNode dom = JsonSerializer.SerializeToNode(obj); + + JsonNode stringProp = dom["StringProp"]; + Assert.True(stringProp is JsonValue); + Assert.Equal("Hello", stringProp.AsValue().GetValue()); + + JsonNode arrayProp = dom["IntArrayProp"]; + Assert.IsType(arrayProp); + Assert.Equal(1, arrayProp[0].AsValue().GetValue()); + Assert.Equal(2, arrayProp[1].AsValue().GetValue()); + } + + [Fact] + public static void SerializeToDocument_WithEscaping() + { + using JsonDocument document = JsonSerializer.SerializeToDocument("+"); + JsonElement dom = document.RootElement; + Assert.Equal(JsonValueKind.String, dom.ValueKind); + Assert.Equal(Escaped_PlusSign, dom.GetRawText()); + + string json = dom.Deserialize(); + Assert.Equal("+", json); + } + + [Fact] + public static void SerializeToElement_WithEscaping() + { + JsonElement dom = JsonSerializer.SerializeToElement("+"); + Assert.Equal(JsonValueKind.String, dom.ValueKind); + Assert.Equal(Escaped_PlusSign, dom.GetRawText()); + + string json = dom.Deserialize(); + Assert.Equal("+", json); + } + + [Fact] + public static void SerializeToNode_WithEscaping() + { + JsonNode dom = JsonSerializer.SerializeToNode("+"); + Assert.Equal(Escaped_PlusSign, dom.ToJsonString()); + + string json = dom.Deserialize(); + Assert.Equal("+", json); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.cs new file mode 100644 index 00000000000000..0603e82b47cad2 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerApiValidation.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.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using System.Text.Json.Serialization.Tests; +using System.Threading.Tasks; +using Xunit; + +namespace System.Text.Json.Serialization.Tests +{ + public class JsonSerializerApiValidation_Span : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_Span() : base(JsonSerializerWrapperForString.SpanSerializer) { } + } + + public class JsonSerializerApiValidation_String : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_String() : base(JsonSerializerWrapperForString.StringSerializer) { } + } + + public class JsonSerializerApiValidation_AsyncStream : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_AsyncStream() : base(JsonSerializerWrapperForString.AsyncStreamSerializer) { } + } + + public class JsonSerializerApiValidation_SyncStream : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_SyncStream() : base(JsonSerializerWrapperForString.SyncStreamSerializer) { } + } + + public class JsonSerializerApiValidation_Writer : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_Writer() : base(JsonSerializerWrapperForString.ReaderWriterSerializer) { } + } + + public class JsonSerializerApiValidation_Document : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_Document() : base(JsonSerializerWrapperForString.DocumentSerializer) { } + } + + public class JsonSerializerApiValidation_Element : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_Element() : base(JsonSerializerWrapperForString.ElementSerializer) { } + } + + public class JsonSerializerApiValidation_Node : JsonSerializerApiValidation + { + public JsonSerializerApiValidation_Node() : base(JsonSerializerWrapperForString.NodeSerializer) { } + } +} + +/// +/// Verifies input values for public JsonSerializer methods. +/// +public abstract class JsonSerializerApiValidation +{ + private class MyPoco { } + + internal partial class MyDummyContext : JsonSerializerContext + { + public MyDummyContext() : base(new JsonSerializerOptions(), new JsonSerializerOptions()) { } + public MyDummyContext(JsonSerializerOptions options) : base(options, new JsonSerializerOptions()) { } + public override JsonTypeInfo? GetTypeInfo(Type type) => throw new NotImplementedException(); + } + + private JsonTypeInfo myDummyTypeInfo = JsonMetadataServices.CreateObjectInfo( + new JsonSerializerOptions(), + createObjectFunc: static () => throw new NotImplementedException(), + propInitFunc: null, + default, + serializeFunc: (Utf8JsonWriter writer, MyPoco value) => throw new NotImplementedException()); + + private JsonSerializerWrapperForString Serializer { get; } + + public JsonSerializerApiValidation(JsonSerializerWrapperForString serializer) + { + Serializer = serializer; + } + + [Fact] + public async Task DeserializeNullException() + { + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: "{}", jsonTypeInfo: null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: "{}", type: null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: "{}", type: typeof(MyPoco), context: null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: "{}", type: null, context: new MyDummyContext())); + + if (!Serializer.SupportsNullValueOnDeserialize) + { + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: null, type: typeof(MyPoco), context: new MyDummyContext())); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: null, type: typeof(MyPoco))); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: null)); + await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper(json: null, jsonTypeInfo: myDummyTypeInfo)); + } + } + + [Fact] + public async Task SerializeNullException() + { + await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(value: new MyPoco(), jsonTypeInfo: null)); + await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(value: new MyPoco(), inputType: null)); + await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(value: new MyPoco(), inputType: typeof(MyPoco), context: null)); + await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(value: new MyPoco(), inputType: null, context: new MyDummyContext())); + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs index 6b691004df94b9..a715e49f4bfe4f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/JsonSerializerWrapperForString.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; +using System.Text.Json.Nodes; using System.Text.Json.Serialization.Metadata; using System.Threading.Tasks; using Xunit; @@ -21,9 +22,14 @@ public abstract partial class JsonSerializerWrapperForString public static JsonSerializerWrapperForString AsyncStreamSerializerWithSmallBuffer => new AsyncStreamSerializerWrapperWithSmallBuffer(); public static JsonSerializerWrapperForString SyncStreamSerializer => new SyncStreamSerializerWrapper(); public static JsonSerializerWrapperForString ReaderWriterSerializer => new ReaderWriterSerializerWrapper(); + public static JsonSerializerWrapperForString DocumentSerializer => new DocumentSerializerWrapper(); + public static JsonSerializerWrapperForString ElementSerializer => new ElementSerializerWrapper(); + public static JsonSerializerWrapperForString NodeSerializer => new NodeSerializerWrapper(); private class SpanSerializerWrapper : JsonSerializerWrapperForString { + protected internal override bool SupportsNullValueOnDeserialize => true; // a 'null' value is supported via implicit operator. + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) { byte[] result = JsonSerializer.SerializeToUtf8Bytes(value, inputType, options); @@ -71,6 +77,8 @@ protected internal override Task DeserializeWrapper(string json, Type ty private class StringSerializerWrapper : JsonSerializerWrapperForString { + protected internal override bool SupportsNullValueOnDeserialize => true; + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) { return Task.FromResult(JsonSerializer.Serialize(value, inputType, options)); @@ -114,69 +122,89 @@ protected internal override Task DeserializeWrapper(string json, Type ty private class AsyncStreamSerializerWrapper : JsonSerializerWrapperForString { + protected internal override bool SupportsNullValueOnDeserialize => false; + protected internal override async Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); await JsonSerializer.SerializeAsync(stream, value, inputType, options); return Encoding.UTF8.GetString(stream.ToArray()); } protected internal override async Task SerializeWrapper(T value, JsonSerializerOptions options = null) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); await JsonSerializer.SerializeAsync(stream, value, options); return Encoding.UTF8.GetString(stream.ToArray()); } protected internal override async Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); await JsonSerializer.SerializeAsync(stream, value, inputType, context); return Encoding.UTF8.GetString(stream.ToArray()); } protected internal override async Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); await JsonSerializer.SerializeAsync(stream, value, jsonTypeInfo); return Encoding.UTF8.GetString(stream.ToArray()); } protected internal override async Task DeserializeWrapper(string json, JsonSerializerOptions options = null) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return await JsonSerializer.DeserializeAsync(stream, options ?? _optionsWithSmallBuffer); + // Emulate a null Stream for API validation tests. + return await JsonSerializer.DeserializeAsync((Stream)null, options ?? _optionsWithSmallBuffer); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return await JsonSerializer.DeserializeAsync(stream, options ?? _optionsWithSmallBuffer); } protected internal override async Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return await JsonSerializer.DeserializeAsync(stream, type, options ?? _optionsWithSmallBuffer); + // Emulate a null Stream for API validation tests. + return await JsonSerializer.DeserializeAsync((Stream)null, type, options ?? _optionsWithSmallBuffer); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return await JsonSerializer.DeserializeAsync(stream, type, options ?? _optionsWithSmallBuffer); } protected internal override async Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return await JsonSerializer.DeserializeAsync(stream, jsonTypeInfo); + // Emulate a null Stream for API validation tests. + return await JsonSerializer.DeserializeAsync((Stream)null, jsonTypeInfo); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return await JsonSerializer.DeserializeAsync(stream, jsonTypeInfo); } protected internal override async Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return await JsonSerializer.DeserializeAsync(stream, type, context); + // Emulate a null Stream for API validation tests. + return await JsonSerializer.DeserializeAsync((Stream)null, type, context); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return await JsonSerializer.DeserializeAsync(stream, type, context); } } private class AsyncStreamSerializerWrapperWithSmallBuffer : AsyncStreamSerializerWrapper { + protected internal override bool SupportsNullValueOnDeserialize => false; + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) { if (options == null) @@ -195,98 +223,130 @@ protected internal override Task SerializeWrapper(T value, JsonSerial private class SyncStreamSerializerWrapper : JsonSerializerWrapperForString { + protected internal override bool SupportsNullValueOnDeserialize => false; + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); JsonSerializer.Serialize(stream, value, inputType, options); return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); JsonSerializer.Serialize(stream, value, options); return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); JsonSerializer.Serialize(stream, value, inputType, context); return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) { - using var stream = new MemoryStream(); + using MemoryStream stream = new(); JsonSerializer.Serialize(stream, value, jsonTypeInfo); return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return Task.FromResult(JsonSerializer.Deserialize(stream, options ?? _optionsWithSmallBuffer)); + // Emulate a null Stream for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize((Stream)null, options ?? _optionsWithSmallBuffer)); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(stream, options ?? _optionsWithSmallBuffer)); } protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return Task.FromResult(JsonSerializer.Deserialize(stream, type, options ?? _optionsWithSmallBuffer)); + // Emulate a null Stream for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize((Stream)null, type, options ?? _optionsWithSmallBuffer)); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(stream, type, options ?? _optionsWithSmallBuffer)); } protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return Task.FromResult(JsonSerializer.Deserialize(stream, jsonTypeInfo)); + // Emulate a null Stream for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize((Stream)null, jsonTypeInfo)); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(stream, jsonTypeInfo)); } protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) { - using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json))) + if (json is null) { - return Task.FromResult(JsonSerializer.Deserialize(stream, type, context)); + // Emulate a null Stream for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize((Stream)null, type, context)); } + + using MemoryStream stream = new(Encoding.UTF8.GetBytes(json)); + return Task.FromResult(JsonSerializer.Deserialize(stream, type, context)); } } private class ReaderWriterSerializerWrapper : JsonSerializerWrapperForString { + protected internal override bool SupportsNullValueOnDeserialize => false; + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) { using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, inputType, options); + using (Utf8JsonWriter writer = new(stream)) + { + JsonSerializer.Serialize(writer, value, inputType, options); + } + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) { using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, options); + using (Utf8JsonWriter writer = new(stream)) + { + JsonSerializer.Serialize(writer, value, options); + } + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) { using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, inputType, context); + using (Utf8JsonWriter writer = new(stream)) + { + JsonSerializer.Serialize(writer, value, inputType, context); + } + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) { using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - JsonSerializer.Serialize(writer, value, jsonTypeInfo); + using (Utf8JsonWriter writer = new(stream)) + { + JsonSerializer.Serialize(writer, value, jsonTypeInfo); + } + return Task.FromResult(Encoding.UTF8.GetString(stream.ToArray())); } @@ -314,5 +374,267 @@ protected internal override Task DeserializeWrapper(string json, Type ty return Task.FromResult(JsonSerializer.Deserialize(ref reader, type, context)); } } + + private class DocumentSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override bool SupportsNullValueOnDeserialize => false; + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + JsonDocument document = JsonSerializer.SerializeToDocument(value, inputType, options); + return Task.FromResult(GetStringFromDocument(document)); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + JsonDocument document = JsonSerializer.SerializeToDocument(value, options); + return Task.FromResult(GetStringFromDocument(document)); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + JsonDocument document = JsonSerializer.SerializeToDocument(value, inputType, context); + return Task.FromResult(GetStringFromDocument(document)); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + JsonDocument document = JsonSerializer.SerializeToDocument(value, jsonTypeInfo); + return Task.FromResult(GetStringFromDocument(document)); + } + + private string GetStringFromDocument(JsonDocument document) + { + // Emulate a null return value. + if (document is null) + { + return "null"; + } + + using MemoryStream stream = new(); + using (Utf8JsonWriter writer = new(stream)) + { + document.WriteTo(writer); + } + + return Encoding.UTF8.GetString(stream.ToArray()); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + if (json is null) + { + // Emulate a null document for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(document: null)); + } + + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.Deserialize(options)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + if (json is null) + { + // Emulate a null document for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(document: null, type)); + } + + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.Deserialize(type, options)); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + if (json is null) + { + // Emulate a null document for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(document: null, jsonTypeInfo)); + } + + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.Deserialize(jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + if (json is null) + { + // Emulate a null document for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(document: null, type, context)); + } + + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.Deserialize(type, context)); + } + } + + private class ElementSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override bool SupportsNullValueOnDeserialize => false; + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + JsonElement element = JsonSerializer.SerializeToElement(value, inputType, options); + return Task.FromResult(GetStringFromElement(element)); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + JsonElement element = JsonSerializer.SerializeToElement(value, options); + return Task.FromResult(GetStringFromElement(element)); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + JsonElement element = JsonSerializer.SerializeToElement(value, inputType, context); + return Task.FromResult(GetStringFromElement(element)); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + JsonElement element = JsonSerializer.SerializeToElement(value, jsonTypeInfo); + return Task.FromResult(GetStringFromElement(element)); + } + + private string GetStringFromElement(JsonElement element) + { + using MemoryStream stream = new MemoryStream(); + using (Utf8JsonWriter writer = new(stream)) + { + element.WriteTo(writer); + } + return Encoding.UTF8.GetString(stream.ToArray()); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.RootElement.Deserialize(options)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.RootElement.Deserialize(type, options)); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.RootElement.Deserialize(jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + using JsonDocument document = JsonDocument.Parse(json); + return Task.FromResult(document.RootElement.Deserialize(type, context)); + } + } + + private class NodeSerializerWrapper : JsonSerializerWrapperForString + { + protected internal override bool SupportsNullValueOnDeserialize => true; + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerOptions options = null) + { + JsonNode node = JsonSerializer.SerializeToNode(value, inputType, options); + + // Emulate a null return value. + if (node is null) + { + return Task.FromResult("null"); + } + + return Task.FromResult(node.ToJsonString()); + } + + protected internal override Task SerializeWrapper(T value, JsonSerializerOptions options = null) + { + JsonNode node = JsonSerializer.SerializeToNode(value, options); + + // Emulate a null return value. + if (node is null) + { + return Task.FromResult("null"); + } + + return Task.FromResult(node.ToJsonString()); + } + + protected internal override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) + { + JsonNode node = JsonSerializer.SerializeToNode(value, inputType, context); + + // Emulate a null return value. + if (node is null) + { + return Task.FromResult("null"); + } + + return Task.FromResult(node.ToJsonString()); + } + + protected internal override Task SerializeWrapper(T value, JsonTypeInfo jsonTypeInfo) + { + JsonNode node = JsonSerializer.SerializeToNode(value, jsonTypeInfo); + + // Emulate a null return value. + if (node is null) + { + return Task.FromResult("null"); + } + + return Task.FromResult(node.ToJsonString()); + } + + protected internal override Task DeserializeWrapper(string json, JsonSerializerOptions options = null) + { + if (json is null) + { + // Emulate a null node for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(node: null)); + } + + JsonNode node = JsonNode.Parse(json); + return Task.FromResult(node.Deserialize(options)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions options = null) + { + if (json is null) + { + // Emulate a null node for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(node: null, type)); + } + + JsonNode node = JsonNode.Parse(json); + return Task.FromResult(node.Deserialize(type, options)); + } + + protected internal override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) + { + if (json is null) + { + // Emulate a null node for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(node: null, jsonTypeInfo)); + } + + JsonNode node = JsonNode.Parse(json); + return Task.FromResult(node.Deserialize(jsonTypeInfo)); + } + + protected internal override Task DeserializeWrapper(string json, Type type, JsonSerializerContext context) + { + if (json is null) + { + // Emulate a null document for API validation tests. + return Task.FromResult(JsonSerializer.Deserialize(node: null, type)); + } + + JsonNode node = JsonNode.Parse(json); + return Task.FromResult(node.Deserialize(type, context)); + } + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs index 0630ae2e7be38f..98f8ba2acf17c6 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/MetadataTests.cs @@ -32,6 +32,21 @@ public sealed class MetadataTests_LowLevel : MetadataTests public MetadataTests_LowLevel() : base(JsonSerializerWrapperForString.ReaderWriterSerializer) { } } + public class MetadataTests_Document : MetadataTests + { + public MetadataTests_Document() : base(JsonSerializerWrapperForString.DocumentSerializer) { } + } + + public class MetadataTests_Element : MetadataTests + { + public MetadataTests_Element() : base(JsonSerializerWrapperForString.ElementSerializer) { } + } + + public class MetadataTests_Node : MetadataTests + { + public MetadataTests_Node() : base(JsonSerializerWrapperForString.NodeSerializer) { } + } + public abstract partial class MetadataTests { protected JsonSerializerWrapperForString Serializer { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs index ada72d48ac6171..d89b36a65a222f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs @@ -1644,6 +1644,21 @@ public class NumberHandlingTests_SyncOverload : NumberHandlingTests_OverloadSpec public NumberHandlingTests_SyncOverload() : base(JsonSerializerWrapperForString.StringSerializer) { } } + public class NumberHandlingTests_Document : NumberHandlingTests_OverloadSpecific + { + public NumberHandlingTests_Document() : base(JsonSerializerWrapperForString.DocumentSerializer) { } + } + + public class NumberHandlingTests_Element : NumberHandlingTests_OverloadSpecific + { + public NumberHandlingTests_Element() : base(JsonSerializerWrapperForString.ElementSerializer) { } + } + + public class NumberHandlingTests_Node : NumberHandlingTests_OverloadSpecific + { + public NumberHandlingTests_Node() : base(JsonSerializerWrapperForString.NodeSerializer) { } + } + public abstract class NumberHandlingTests_OverloadSpecific { private JsonSerializerWrapperForString Deserializer { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs index dfd167d44073e3..26bec2f5a5921b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs @@ -39,6 +39,21 @@ public class PolymorphicTests_Writer : PolymorphicTests public PolymorphicTests_Writer() : base(JsonSerializerWrapperForString.ReaderWriterSerializer) { } } + public class PolymorphicTests_Document : PolymorphicTests + { + public PolymorphicTests_Document() : base(JsonSerializerWrapperForString.DocumentSerializer) { } + } + + public class PolymorphicTests_Element : PolymorphicTests + { + public PolymorphicTests_Element() : base(JsonSerializerWrapperForString.ElementSerializer) { } + } + + public class PolymorphicTests_Node : PolymorphicTests + { + public PolymorphicTests_Node() : base(JsonSerializerWrapperForString.NodeSerializer) { } + } + public abstract class PolymorphicTests { private JsonSerializerWrapperForString Serializer { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index a3d45fd72ed2a5..0f7121cf6e87e0 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -132,6 +132,7 @@ + @@ -141,6 +142,7 @@ + diff --git a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs index 1babd5ea925ca1..d762f2b639fc9d 100644 --- a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs +++ b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/AsyncTaskMethodBuilderTests.cs @@ -532,7 +532,7 @@ public static async Task AsyncMethodsDropsStateMachineAndExecutionContextUponCom // We want to make sure that holding on to the resulting Task doesn't keep // that finalizable object alive. - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + bool finalized = false; Task t = null; @@ -544,7 +544,7 @@ async Task YieldOnceAsync(object s) GC.KeepAlive(s); // keep s referenced by the state machine } - var state = new InvokeActionOnFinalization { Action = () => tcs.SetResult() }; + var state = new InvokeActionOnFinalization { Action = () => Volatile.Write(ref finalized, true) }; var al = new AsyncLocal() { Value = state }; // ensure the object is stored in ExecutionContext t = YieldOnceAsync(state); // ensure the object is stored in the state machine al.Value = null; @@ -556,19 +556,15 @@ async Task YieldOnceAsync(object s) await t; // wait for the async method to complete and clear out its state await Task.Yield(); // ensure associated state is not still on the stack as part of the antecedent's execution - for (int i = 0; i < 2; i++) + for (int i = 0; i < 10 && !Volatile.Read(ref finalized); i++) { GC.Collect(); GC.WaitForPendingFinalizers(); } - try - { - await tcs.Task.WaitAsync(TimeSpan.FromSeconds(60)); - } - catch (Exception e) + if (!Volatile.Read(ref finalized)) { - Environment.FailFast("Look at the created dump", e); + Environment.FailFast("Look at the created dump"); } GC.KeepAlive(t); // ensure the object is stored in the state machine diff --git a/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs b/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs index 5b832f2af56cfb..8374c56056996a 100644 --- a/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs +++ b/src/libraries/System.Threading.Tasks/tests/Task/ExecutionContextFlowTest.cs @@ -37,13 +37,13 @@ public static async Task TaskDropsExecutionContextUponCompletion() // We want to make sure that holding on to the resulting Task doesn't keep // that finalizable object alive. - var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + bool finalized = false; Task t = null; Thread runner = new Thread(() => { - var state = new InvokeActionOnFinalization { Action = () => tcs.SetResult() }; + var state = new InvokeActionOnFinalization { Action = () => Volatile.Write(ref finalized, true) }; var al = new AsyncLocal(){ Value = state }; // ensure the object is stored in ExecutionContext t = Task.Run(() => { }); // run a task that'll capture EC al.Value = null; @@ -54,19 +54,15 @@ public static async Task TaskDropsExecutionContextUponCompletion() await t; // wait for the task method to complete and clear out its state - for (int i = 0; i < 2; i++) + for (int i = 0; i < 10 && !Volatile.Read(ref finalized); i++) { GC.Collect(); GC.WaitForPendingFinalizers(); } - try - { - await tcs.Task.WaitAsync(TimeSpan.FromSeconds(60)); // finalizable object should have been collected and finalized - } - catch (Exception e) + if (!Volatile.Read(ref finalized)) { - Environment.FailFast("Look at the created dump", e); + Environment.FailFast("Look at the created dump"); } GC.KeepAlive(t); // ensure the object is stored in the state machine diff --git a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs index 767e5bef92c165..89c8b54f230d7a 100644 --- a/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs +++ b/src/libraries/System.Threading.ThreadPool/tests/ThreadPoolTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading.Tasks; @@ -949,6 +950,57 @@ void AppContextSetData(string name, object value) }).Dispose(); } + [ConditionalFact(nameof(IsThreadingAndRemoteExecutorSupported))] + public static void CooperativeBlockingWithProcessingThreadsAndGoalThreadsAndAddWorkerRaceTest() + { + // Avoid contaminating the main process' environment + RemoteExecutor.Invoke(() => + { + try + { + // The test is run affinitized to at most 2 processors for more frequent repros. The actual test process below + // will inherit the affinity. + Process testParentProcess = Process.GetCurrentProcess(); + testParentProcess.ProcessorAffinity = (nint)testParentProcess.ProcessorAffinity & 0x3; + } + catch (PlatformNotSupportedException) + { + // Processor affinity is not supported on some platforms, try to run the test anyway + } + + RemoteExecutor.Invoke(() => + { + const uint TestDurationMs = 4000; + + var done = new ManualResetEvent(false); + int startTimeMs = Environment.TickCount; + Action completingTask = data => ((TaskCompletionSource)data).SetResult(0); + Action repeatingTask = null; + repeatingTask = () => + { + if ((uint)(Environment.TickCount - startTimeMs) >= TestDurationMs) + { + done.Set(); + return; + } + + Task.Run(repeatingTask); + + var tcs = new TaskCompletionSource(); + Task.Factory.StartNew(completingTask, tcs); + tcs.Task.Wait(); + }; + + for (int i = 0; i < Environment.ProcessorCount; ++i) + { + Task.Run(repeatingTask); + } + + done.CheckedWait(); + }).Dispose(); + }).Dispose(); + } + public static bool IsThreadingAndRemoteExecutorSupported => PlatformDetection.IsThreadingSupported && RemoteExecutor.IsSupported; } diff --git a/src/libraries/pkg/Directory.Build.props b/src/libraries/pkg/Directory.Build.props index d31c32ee6470c6..f1bb5ff9c315df 100644 --- a/src/libraries/pkg/Directory.Build.props +++ b/src/libraries/pkg/Directory.Build.props @@ -11,7 +11,7 @@ - + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 6e39e1b2d12263..3144580dc68d3e 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -103,9 +103,6 @@ - - - diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index 4e601cca8c5e72..b4a19469544991 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -4148,7 +4148,7 @@ mono_unhandled_exception_internal (MonoObject *exc_raw) void mono_unhandled_exception (MonoObject *exc) { - MONO_EXTERNAL_ONLY_VOID (mono_unhandled_exception_internal (exc)); + MONO_EXTERNAL_ONLY_GC_UNSAFE_VOID (mono_unhandled_exception_internal (exc)); } static MonoObjectHandle diff --git a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in index bd8992a751d719..e34ff6d173b85a 100644 --- a/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in +++ b/src/mono/nuget/Microsoft.NET.Workload.Mono.Toolchain.Manifest/WorkloadManifest.json.in @@ -49,7 +49,7 @@ "Microsoft.NETCore.App.Runtime.AOT.Cross.iossimulator-x86" ], "extends": [ "runtimes-ios" ], - "platforms": [ "osx-arm64", "osx-x64" ] + "platforms": [ "win-x64", "osx-arm64", "osx-x64" ] }, "runtimes-ios": { "abstract": true, @@ -72,7 +72,7 @@ "Microsoft.NETCore.App.Runtime.AOT.Cross.maccatalyst-x64" ], "extends": [ "runtimes-maccatalyst" ], - "platforms": [ "osx-arm64", "osx-x64" ] + "platforms": [ "win-x64", "osx-arm64", "osx-x64" ] }, "runtimes-maccatalyst": { "abstract": true, @@ -105,7 +105,7 @@ "Microsoft.NETCore.App.Runtime.AOT.Cross.tvossimulator-x64" ], "extends": [ "runtimes-tvos" ], - "platforms": [ "osx-arm64", "osx-x64" ] + "platforms": [ "win-x64", "osx-arm64", "osx-x64" ] }, "runtimes-tvos": { "abstract": true, diff --git a/src/native/corehost/deps_format.cpp b/src/native/corehost/deps_format.cpp index 8ab0f22caff2cb..fdfc1869ca8b04 100644 --- a/src/native/corehost/deps_format.cpp +++ b/src/native/corehost/deps_format.cpp @@ -439,7 +439,7 @@ bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ve bool deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_path, const rid_fallback_graph_t& rid_fallback_graph) { m_deps_file = deps_path; - m_file_exists = bundle::info_t::config_t::probe(deps_path) || pal::file_exists(deps_path); + m_file_exists = bundle::info_t::config_t::probe(deps_path) || pal::realpath(&m_deps_file, true); json_parser_t json; if (!m_file_exists) @@ -449,7 +449,7 @@ bool deps_json_t::load(bool is_framework_dependent, const pal::string_t& deps_pa return true; } - if (!json.parse_file(deps_path)) + if (!json.parse_file(m_deps_file)) { return false; } diff --git a/src/native/corehost/hostmisc/pal.unix.cpp b/src/native/corehost/hostmisc/pal.unix.cpp index 411298a08f4cef..4a3e86f2f567a8 100644 --- a/src/native/corehost/hostmisc/pal.unix.cpp +++ b/src/native/corehost/hostmisc/pal.unix.cpp @@ -440,7 +440,15 @@ bool pal::get_dotnet_self_registered_dir(pal::string_t* recv) FILE* install_location_file = pal::file_open(install_location_file_path, "r"); if (install_location_file == nullptr) { - trace::error(_X("The install_location file ['%s'] failed to open: %s."), install_location_file_path.c_str(), pal::strerror(errno)); + if (errno == ENOENT) + { + trace::verbose(_X("The install_location file ['%s'] does not exist - skipping."), install_location_file_path.c_str()); + } + else + { + trace::error(_X("The install_location file ['%s'] failed to open: %s."), install_location_file_path.c_str(), pal::strerror(errno)); + } + return false; } diff --git a/src/native/corehost/hostmisc/pal.windows.cpp b/src/native/corehost/hostmisc/pal.windows.cpp index c992f92da8294e..87a5508badbf59 100644 --- a/src/native/corehost/hostmisc/pal.windows.cpp +++ b/src/native/corehost/hostmisc/pal.windows.cpp @@ -645,11 +645,15 @@ bool pal::clr_palstring(const char* cstr, pal::string_t* out) // Return if path is valid and file exists, return true and adjust path as appropriate. bool pal::realpath(string_t* path, bool skip_error_logging) { + if (path->empty()) + { + return false; + } + if (LongFile::IsNormalized(*path)) { WIN32_FILE_ATTRIBUTE_DATA data; - if (path->empty() // An empty path doesn't exist - || GetFileAttributesExW(path->c_str(), GetFileExInfoStandard, &data) != 0) + if (GetFileAttributesExW(path->c_str(), GetFileExInfoStandard, &data) != 0) { return true; } @@ -713,11 +717,6 @@ bool pal::realpath(string_t* path, bool skip_error_logging) bool pal::file_exists(const string_t& path) { - if (path.empty()) - { - return false; - } - string_t tmp(path); return pal::realpath(&tmp, true); } diff --git a/src/native/corehost/runtime_config.cpp b/src/native/corehost/runtime_config.cpp index 986c44c9bad36f..fe6a0546cff923 100644 --- a/src/native/corehost/runtime_config.cpp +++ b/src/native/corehost/runtime_config.cpp @@ -334,9 +334,9 @@ bool runtime_config_t::ensure_dev_config_parsed() trace::verbose(_X("Attempting to read dev runtime config: %s"), m_dev_path.c_str()); pal::string_t retval; - if (!pal::file_exists(m_dev_path)) + if (!pal::realpath(&m_dev_path, true)) { - // Not existing is valid. + // It is valid for the runtimeconfig.dev.json to not exist. return true; } @@ -402,7 +402,7 @@ bool runtime_config_t::ensure_parsed() trace::verbose(_X("Did not successfully parse the runtimeconfig.dev.json")); } - if (!bundle::info_t::config_t::probe(m_path) && !pal::file_exists(m_path)) + if (!bundle::info_t::config_t::probe(m_path) && !pal::realpath(&m_path, true)) { // Not existing is not an error. return true; diff --git a/src/native/eventpipe/ds-ipc-pal-socket.c b/src/native/eventpipe/ds-ipc-pal-socket.c index 766aa320735c64..2f7cb928706c51 100644 --- a/src/native/eventpipe/ds-ipc-pal-socket.c +++ b/src/native/eventpipe/ds-ipc-pal-socket.c @@ -323,11 +323,8 @@ ipc_socket_create_uds (DiagnosticsIpc *ipc) DS_ENTER_BLOCKING_PAL_SECTION; new_socket = socket (ipc->server_address_family, socket_type, 0); #ifndef SOCK_CLOEXEC - if (new_socket != DS_IPC_INVALID_SOCKET) { - if (fcntl (new_socket, F_SETFD, FD_CLOEXEC) == -1) { - EP_ASSERT (!"Failed to set CLOEXEC"); - } - } + if (new_socket != DS_IPC_INVALID_SOCKET) + fcntl (new_socket, F_SETFD, FD_CLOEXEC); // ignore any failures; this is best effort #endif // SOCK_CLOEXEC DS_EXIT_BLOCKING_PAL_SECTION; return new_socket; @@ -356,9 +353,9 @@ ipc_socket_create_tcp (DiagnosticsIpc *ipc) new_socket = socket (ipc->server_address_family, socket_type, IPPROTO_TCP); if (new_socket != DS_IPC_INVALID_SOCKET) { #ifndef SOCK_CLOEXEC - if (fcntl (new_socket, F_SETFD, FD_CLOEXEC) == -1) { - EP_ASSERT (!"Failed to set CLOEXEC"); - } +#ifndef HOST_WIN32 + fcntl (new_socket, F_SETFD, FD_CLOEXEC); // ignore any failures; this is best effort +#endif // HOST_WIN32 #endif // SOCK_CLOEXEC int option_value = 1; setsockopt (new_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&option_value, sizeof (option_value)); diff --git a/src/native/eventpipe/ep-types.h b/src/native/eventpipe/ep-types.h index 63e36c6481879d..8bec5f127caeda 100644 --- a/src/native/eventpipe/ep-types.h +++ b/src/native/eventpipe/ep-types.h @@ -63,7 +63,7 @@ struct _EventPipeProviderCallbackData { #else struct _EventPipeProviderCallbackData_Internal { #endif - const ep_char8_t *filter_data; + ep_char8_t *filter_data; EventPipeCallback callback_function; void *callback_data; int64_t keywords; diff --git a/src/native/eventpipe/ep.c b/src/native/eventpipe/ep.c index 38c1e1a69b43e7..eb401dcaa414e6 100644 --- a/src/native/eventpipe/ep.c +++ b/src/native/eventpipe/ep.c @@ -246,8 +246,10 @@ ep_provider_callback_data_alloc_copy (EventPipeProviderCallbackData *provider_ca EventPipeProviderCallbackData *instance = ep_rt_object_alloc (EventPipeProviderCallbackData); ep_raise_error_if_nok (instance != NULL); - if (provider_callback_data_src) + if (provider_callback_data_src) { *instance = *provider_callback_data_src; + instance->filter_data = ep_rt_utf8_string_dup (provider_callback_data_src->filter_data); + } ep_on_exit: return instance; @@ -270,7 +272,7 @@ ep_provider_callback_data_init ( { EP_ASSERT (provider_callback_data != NULL); - provider_callback_data->filter_data = filter_data; + provider_callback_data->filter_data = ep_rt_utf8_string_dup (filter_data); provider_callback_data->callback_function = callback_function; provider_callback_data->callback_data = callback_data; provider_callback_data->keywords = keywords; @@ -289,19 +291,22 @@ ep_provider_callback_data_init_copy ( EP_ASSERT (provider_callback_data_src != NULL); *provider_callback_data_dst = *provider_callback_data_src; + provider_callback_data_dst->filter_data = ep_rt_utf8_string_dup (provider_callback_data_src->filter_data); return provider_callback_data_dst; } void ep_provider_callback_data_fini (EventPipeProviderCallbackData *provider_callback_data) { - ; + ep_return_void_if_nok (provider_callback_data != NULL); + ep_rt_utf8_string_free (provider_callback_data->filter_data); } void ep_provider_callback_data_free (EventPipeProviderCallbackData *provider_callback_data) { ep_return_void_if_nok (provider_callback_data != NULL); + ep_provider_callback_data_fini (provider_callback_data); ep_rt_object_free (provider_callback_data); } diff --git a/src/tests/Common/scripts/crossgen_comparison.py b/src/tests/Common/scripts/crossgen_comparison.py deleted file mode 100644 index 68a67ff72fbf69..00000000000000 --- a/src/tests/Common/scripts/crossgen_comparison.py +++ /dev/null @@ -1,817 +0,0 @@ -#!/usr/bin/env python -# -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. -# -################################################################################ -# -# Module: crossgen_comparison.py -# -# Notes: -# -# Script that -# 1) runs crossgen on System.Private.CoreLib.dll and CoreFX assemblies and -# collects information about the crossgen behaviour (such as the return code, -# stdout/stderr streams, SHA256 hash sum of the resulting file). -# 2) compares the collected information from two crossgen scenarios (e.g. -# x86_arm vs. arm_arm) and report all the differences in their behaviour -# (such as mismatches in the resulting files; hash sums, or missing files). -# -# Example: -# -# The following command -# -# ~/git/coreclr$ python tests/scripts/crossgen_comparison.py crossgen_corelib -# --crossgen artifacts/bin/coreclr/Linux.arm.Checked/crossgen -# --il_corelib artifacts/bin/coreclr/Linux.arm.Checked/IL/System.Private.CoreLib.dll -# --result_dir Linux.arm_arm.Checked -# -# runs Hostarm/arm crossgen on System.Private.CoreLib.dll and puts all the -# information in files Linux.arm_arm.Checked/System.Private.CoreLib-NativeOrReadyToRunImage.json -# and System.Private.CoreLib-DebuggingFile.json -# -# ~/git/coreclr$ cat Linux.arm_arm.Checked/System.Private.CoreLib-NativeOrReadyToRunImage.json -# { -# "AssemblyName": "System.Private.CoreLib", -# "ReturnCode": 0, -# "OutputFileHash": "4d27c7f694c20974945e4f7cb43263286a18c56f4d00aac09f6124caa372ba0a", -# "StdErr": [], -# "StdOut": [ -# "Native image /tmp/tmp9ZX7gl/System.Private.CoreLib.dll generated successfully." -# ], -# "OutputFileType": "NativeOrReadyToRunImage", -# "OutputFileSizeInBytes": 9564160 -# } -# -# ~/git/coreclr$ cat Linux.x64_arm.Checked/System.Private.CoreLib-DebuggingFile.json -# { -# "ReturnCode": 0, -# "StdOut": [ -# "Successfully generated perfmap for native assembly '/tmp/tmp9ZX7gl/System.Private.CoreLib.dll'." -# ], -# "OutputFileHash": "f4fff0d88193d3a1422f9f0806a6cea6ac6c1aab0499968c183cbb0755e1084b", -# "OutputFileType": "DebuggingFile", -# "StdErr": [], -# "OutputFileSizeInBytes": 1827867, -# "AssemblyName": "System.Private.CoreLib" -# } -# -# The following command -# -# ~/git/coreclr$ python tests/scripts/crossgen_comparison.py crossgen_dotnet_sdk -# --crossgen artifacts/bin/coreclr/Linux.arm.Checked/x64/crossgen -# --il_corelib artifacts/bin/coreclr/Linux.arm.Checked/IL/System.Private.CoreLib.dll -# --dotnet_sdk dotnet-sdk-latest-linux-arm.tar.gz -# --result_dir Linux.x64_arm.Checked -# -# runs Hostx64/arm crossgen on System.Private.CoreLib.dll in artifacts/Product and on -# all the assemblies inside dotnet-sdk-latest-linux-arm.tar.gz and stores the -# collected information in directory Linux.x64_arm.Checked -# -# ~/git/coreclr$ ls Linux.x64_arm.Checked | head -# Microsoft.AI.DependencyCollector.dll.json -# Microsoft.ApplicationInsights.AspNetCore.dll.json -# Microsoft.ApplicationInsights.dll.json -# Microsoft.AspNetCore.Antiforgery.dll.json -# Microsoft.AspNetCore.ApplicationInsights.HostingStartup.dll.json -# Microsoft.AspNetCore.Authentication.Abstractions.dll.json -# Microsoft.AspNetCore.Authentication.Cookies.dll.json -# Microsoft.AspNetCore.Authentication.Core.dll.json -# Microsoft.AspNetCore.Authentication.dll.json -# Microsoft.AspNetCore.Authentication.Facebook.dll.json -# -# The following command -# -# ~/git/coreclr$ python -u tests/scripts/crossgen_comparison.py compare -# --base_dir Linux.x64_arm.Checked -# --diff_dir Linux.x86_arm.Checked -# -# compares the results of Hostx64/arm crossgen and Hostx86/arm crossgen. -################################################################################ -################################################################################ - -import argparse -import datetime -import asyncio -import glob -import json -import hashlib -import multiprocessing -import os -import tarfile -import tempfile -import re -import shutil -import subprocess -import sys - -################################################################################ -# Argument Parser -################################################################################ - -def build_argument_parser(): - description = """Script that runs crossgen on different assemblies and - collects/compares information about the crossgen behaviour.""" - - parser = argparse.ArgumentParser(description=description) - - subparsers = parser.add_subparsers() - - crossgen_corelib_description = """Runs crossgen on IL System.Private.CoreLib.dll and - collects information about the run.""" - - corelib_parser = subparsers.add_parser('crossgen_corelib', description=crossgen_corelib_description) - corelib_parser.add_argument('--crossgen', dest='crossgen_executable_filename', required=True) - corelib_parser.add_argument('--il_corelib', dest='il_corelib_filename', required=True) - corelib_parser.add_argument('--result_dir', dest='result_dirname', required=True) - corelib_parser.set_defaults(func=crossgen_corelib) - - framework_parser_description = """Runs crossgen on each assembly in Core_Root and - collects information about all the runs.""" - - framework_parser = subparsers.add_parser('crossgen_framework', description=framework_parser_description) - framework_parser.add_argument('--crossgen', dest='crossgen_executable_filename', required=True) - framework_parser.add_argument('--il_corelib', dest='il_corelib_filename', required=True) - framework_parser.add_argument('--core_root', dest='core_root', required=True) - framework_parser.add_argument('--result_dir', dest='result_dirname', required=True) - framework_parser.set_defaults(func=crossgen_framework) - - dotnet_sdk_parser_description = "Unpack .NET SDK archive file and runs crossgen on each assembly." - dotnet_sdk_parser = subparsers.add_parser('crossgen_dotnet_sdk', description=dotnet_sdk_parser_description) - dotnet_sdk_parser.add_argument('--crossgen', dest='crossgen_executable_filename', required=True) - dotnet_sdk_parser.add_argument('--il_corelib', dest='il_corelib_filename', required=True) - dotnet_sdk_parser.add_argument('--dotnet_sdk', dest='dotnet_sdk_filename', required=True) - dotnet_sdk_parser.add_argument('--result_dir', dest='result_dirname', required=True) - dotnet_sdk_parser.set_defaults(func=crossgen_dotnet_sdk) - - compare_parser_description = "Compares crossgen results in directories {base_dir} and {diff_dir}" - - compare_parser = subparsers.add_parser('compare', description=compare_parser_description) - compare_parser.add_argument('--base_dir', dest='base_dirname', required=True) - compare_parser.add_argument('--diff_dir', dest='diff_dirname', required=True) - compare_parser.set_defaults(func=compare_results) - - return parser - -################################################################################ -# Helper class -################################################################################ - -class AsyncSubprocessHelper: - def __init__(self, items, subproc_count=multiprocessing.cpu_count(), verbose=False): - item_queue = asyncio.Queue() - for item in items: - item_queue.put_nowait(item) - - self.items = items - self.subproc_count = subproc_count - self.verbose = verbose - - if 'win32' in sys.platform: - # Windows specific event-loop policy & cmd - asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy()) - - async def __get_item__(self, item, index, size, async_callback, *extra_args): - """ Wrapper to the async callback which will schedule based on the queue - """ - - # Wait for the queue to become free. Then start - # running the sub process. - subproc_id = await self.subproc_count_queue.get() - - print_prefix = "" - - if self.verbose: - print_prefix = "[{}:{}]: ".format(index, size) - - await async_callback(print_prefix, item, *extra_args) - - # Add back to the queue, incase another process wants to run. - self.subproc_count_queue.put_nowait(subproc_id) - - async def __run_to_completion__(self, async_callback, *extra_args): - """ async wrapper for run_to_completion - """ - - chunk_size = self.subproc_count - - # Create a queue with a chunk size of the cpu count - # - # Each run_crossgen invocation will remove an item from the - # queue before running a potentially long running pmi run. - # - # When the queue is drained, we will wait queue.get which - # will wait for when a run_crossgen instance has added back to the - subproc_count_queue = asyncio.Queue(chunk_size) - diff_queue = asyncio.Queue() - - for item in self.items: - diff_queue.put_nowait(item) - - for item in range(chunk_size): - subproc_count_queue.put_nowait(item) - - self.subproc_count_queue = subproc_count_queue - tasks = [] - size = diff_queue.qsize() - - count = 1 - item = diff_queue.get_nowait() if not diff_queue.empty() else None - while item is not None: - tasks.append(self.__get_item__(item, count, size, async_callback, *extra_args)) - count += 1 - - item = diff_queue.get_nowait() if not diff_queue.empty() else None - - await asyncio.gather(*tasks) - - def run_to_completion(self, async_callback, *extra_args): - """ Run until the item queue has been depleted - - Notes: - Acts as a wrapper to abstract the async calls to - async_callback. Note that this will allow cpu_count - amount of running subprocesses. Each time the queue - is emptied, another process will start. Note that - the python code is single threaded, it will just - rely on async/await to start subprocesses at - subprocess_count - """ - - reset_env = os.environ.copy() - - loop = asyncio.get_event_loop() - loop.run_until_complete(self.__run_to_completion__(async_callback, *extra_args)) - loop.close() - - os.environ.update(reset_env) - -################################################################################ -# Globals -################################################################################ - -# List of framework assemblies used for crossgen_framework command -g_Framework_Assemblies = [ - 'CommandLine.dll', - 'Microsoft.CodeAnalysis.CSharp.dll', - 'Microsoft.CodeAnalysis.dll', - 'Microsoft.CodeAnalysis.VisualBasic.dll', - 'Microsoft.CSharp.dll', - 'Microsoft.Diagnostics.FastSerialization.dll', - 'Microsoft.Diagnostics.Tracing.TraceEvent.dll', - 'Microsoft.DotNet.Cli.Utils.dll', - 'Microsoft.DotNet.InternalAbstractions.dll', - 'Microsoft.DotNet.ProjectModel.dll', - 'Microsoft.Extensions.DependencyModel.dll', - 'Microsoft.VisualBasic.dll', - 'Microsoft.Win32.Primitives.dll', - 'Microsoft.Win32.Registry.dll', - 'netstandard.dll', - 'Newtonsoft.Json.dll', - 'NuGet.Common.dll', - 'NuGet.Configuration.dll', - 'NuGet.DependencyResolver.Core.dll', - 'NuGet.Frameworks.dll', - 'NuGet.LibraryModel.dll', - 'NuGet.Packaging.Core.dll', - 'NuGet.Packaging.Core.Types.dll', - 'NuGet.Packaging.dll', - 'NuGet.ProjectModel.dll', - 'NuGet.Protocol.Core.Types.dll', - 'NuGet.Protocol.Core.v3.dll', - 'NuGet.Repositories.dll', - 'NuGet.RuntimeModel.dll', - 'NuGet.Versioning.dll', - 'System.AppContext.dll', - 'System.Buffers.dll', - 'System.Collections.Concurrent.dll', - 'System.Collections.dll', - 'System.Collections.Immutable.dll', - 'System.Collections.NonGeneric.dll', - 'System.Collections.Specialized.dll', - 'System.CommandLine.dll', - 'System.ComponentModel.Annotations.dll', - 'System.ComponentModel.DataAnnotations.dll', - 'System.ComponentModel.dll', - 'System.ComponentModel.EventBasedAsync.dll', - 'System.ComponentModel.Primitives.dll', - 'System.ComponentModel.TypeConverter.dll', - 'System.Configuration.dll', - 'System.Console.dll', - 'System.Core.dll', - 'System.Data.Common.dll', - 'System.Data.dll', - 'System.Diagnostics.Contracts.dll', - 'System.Diagnostics.Debug.dll', - 'System.Diagnostics.DiagnosticSource.dll', - 'System.Diagnostics.EventLog.dll', - 'System.Diagnostics.FileVersionInfo.dll', - 'System.Diagnostics.Process.dll', - 'System.Diagnostics.StackTrace.dll', - 'System.Diagnostics.TextWriterTraceListener.dll', - 'System.Diagnostics.Tools.dll', - 'System.Diagnostics.TraceSource.dll', - 'System.Diagnostics.Tracing.dll', - 'System.dll', - 'System.Drawing.dll', - 'System.Drawing.Primitives.dll', - 'System.Dynamic.Runtime.dll', - 'System.Globalization.Calendars.dll', - 'System.Globalization.dll', - 'System.Globalization.Extensions.dll', - 'System.IO.Compression.Brotli.dll', - 'System.IO.Compression.dll', - 'System.IO.Compression.FileSystem.dll', - 'System.IO.Compression.ZipFile.dll', - 'System.IO.dll', - 'System.IO.FileSystem.AccessControl.dll', - 'System.IO.FileSystem.dll', - 'System.IO.FileSystem.DriveInfo.dll', - 'System.IO.FileSystem.Primitives.dll', - 'System.IO.FileSystem.Watcher.dll', - 'System.IO.IsolatedStorage.dll', - 'System.IO.MemoryMappedFiles.dll', - 'System.IO.Pipes.dll', - 'System.IO.UnmanagedMemoryStream.dll', - 'System.Linq.dll', - 'System.Linq.Expressions.dll', - 'System.Linq.Parallel.dll', - 'System.Linq.Queryable.dll', - 'System.Memory.dll', - 'System.Net.dll', - 'System.Net.Http.dll', - 'System.Net.HttpListener.dll', - 'System.Net.Mail.dll', - 'System.Net.NameResolution.dll', - 'System.Net.NetworkInformation.dll', - 'System.Net.Ping.dll', - 'System.Net.Primitives.dll', - 'System.Net.Requests.dll', - 'System.Net.Security.dll', - 'System.Net.ServicePoint.dll', - 'System.Net.Sockets.dll', - 'System.Net.WebClient.dll', - 'System.Net.WebHeaderCollection.dll', - 'System.Net.WebProxy.dll', - 'System.Net.WebSockets.Client.dll', - 'System.Net.WebSockets.dll', - 'System.Numerics.dll', - 'System.Numerics.Vectors.dll', - 'System.ObjectModel.dll', - 'System.Private.DataContractSerialization.dll', - 'System.Private.Uri.dll', - 'System.Private.Xml.dll', - 'System.Private.Xml.Linq.dll', - 'System.Reflection.DispatchProxy.dll', - 'System.Reflection.dll', - 'System.Reflection.Emit.dll', - 'System.Reflection.Emit.ILGeneration.dll', - 'System.Reflection.Emit.Lightweight.dll', - 'System.Reflection.Extensions.dll', - 'System.Reflection.Metadata.dll', - 'System.Reflection.Primitives.dll', - 'System.Reflection.TypeExtensions.dll', - 'System.Resources.Reader.dll', - 'System.Resources.ResourceManager.dll', - 'System.Resources.Writer.dll', - 'System.Runtime.CompilerServices.Unsafe.dll', - 'System.Runtime.CompilerServices.VisualC.dll', - 'System.Runtime.dll', - 'System.Runtime.Extensions.dll', - 'System.Runtime.Handles.dll', - 'System.Runtime.InteropServices.dll', - 'System.Runtime.InteropServices.RuntimeInformation.dll', - 'System.Runtime.InteropServices.WindowsRuntime.dll', - 'System.Runtime.Loader.dll', - 'System.Runtime.Numerics.dll', - 'System.Runtime.Serialization.dll', - 'System.Runtime.Serialization.Formatters.dll', - 'System.Runtime.Serialization.Json.dll', - 'System.Runtime.Serialization.Primitives.dll', - 'System.Runtime.Serialization.Xml.dll', - 'System.Security.AccessControl.dll', - 'System.Security.Claims.dll', - 'System.Security.Cryptography.Algorithms.dll', - 'System.Security.Cryptography.Cng.dll', - 'System.Security.Cryptography.Csp.dll', - 'System.Security.Cryptography.Encoding.dll', - 'System.Security.Cryptography.OpenSsl.dll', - 'System.Security.Cryptography.Primitives.dll', - 'System.Security.Cryptography.X509Certificates.dll', - 'System.Security.dll', - 'System.Security.Permissions.dll', - 'System.Security.Principal.dll', - 'System.Security.Principal.Windows.dll', - 'System.Security.SecureString.dll', - 'System.ServiceModel.Web.dll', - 'System.ServiceProcess.dll', - 'System.Text.Encoding.CodePages.dll', - 'System.Text.Encoding.dll', - 'System.Text.Encoding.Extensions.dll', - 'System.Text.RegularExpressions.dll', - 'System.Threading.AccessControl.dll', - 'System.Threading.dll', - 'System.Threading.Overlapped.dll', - 'System.Threading.Tasks.Dataflow.dll', - 'System.Threading.Tasks.dll', - 'System.Threading.Tasks.Extensions.dll', - 'System.Threading.Tasks.Parallel.dll', - 'System.Threading.Thread.dll', - 'System.Threading.ThreadPool.dll', - 'System.Threading.Timer.dll', - 'System.Transactions.dll', - 'System.Transactions.Local.dll', - 'System.ValueTuple.dll', - 'System.Web.dll', - 'System.Web.HttpUtility.dll', - 'System.Windows.dll', - 'System.Xml.dll', - 'System.Xml.Linq.dll', - 'System.Xml.ReaderWriter.dll', - 'System.Xml.Serialization.dll', - 'System.Xml.XDocument.dll', - 'System.Xml.XmlDocument.dll', - 'System.Xml.XmlSerializer.dll', - 'System.Xml.XPath.dll', - 'System.Xml.XPath.XDocument.dll', - 'TraceReloggerLib.dll', - 'WindowsBase.dll'] - -class CrossGenRunner: - def __init__(self, crossgen_executable_filename): - self.crossgen_executable_filename = crossgen_executable_filename - self.platform_assemblies_paths_sep = ';' if sys.platform == 'win32' else ':' - - async def crossgen_il_file(self, il_filename, ni_filename, platform_assemblies_paths): - """ - Runs a subprocess "{crossgen_executable_filename} /nologo /Platform_Assemblies_Paths /out {ni_filename} /in {il_filename}" - and returns returncode, stdour, stderr. - """ - args = self._build_args_crossgen_il_file(il_filename, ni_filename, platform_assemblies_paths) - return await self._run_with_args(args) - - async def create_debugging_file(self, ni_filename, debugging_files_dirname, platform_assemblies_paths): - """ - Runs a subprocess "{crossgen_executable_filename} /nologo /Platform_Assemblies_Paths /CreatePerfMap {debugging_files_dirname} /in {il_filename}" on Unix - or "{crossgen_executable_filename} /nologo /Platform_Assemblies_Paths /CreatePdb {debugging_files_dirname} /in {il_filename}" on Windows - and returns returncode, stdout, stderr. - """ - args = self._build_args_create_debugging_file(ni_filename, debugging_files_dirname, platform_assemblies_paths) - return await self._run_with_args(args) - - def _build_args_crossgen_il_file(self, il_filename, ni_filename, platform_assemblies_paths): - args = [] - args.append(self.crossgen_executable_filename) - args.append('/nologo') - args.append('/Platform_Assemblies_Paths') - args.append(self.platform_assemblies_paths_sep.join(platform_assemblies_paths)) - args.append('/out') - args.append(ni_filename) - args.append('/in') - args.append(il_filename) - return args - - def _build_args_create_debugging_file(self, ni_filename, debugging_files_dirname, platform_assemblies_paths): - args = [] - args.append(self.crossgen_executable_filename) - args.append('/nologo') - args.append('/Platform_Assemblies_Paths') - args.append(self.platform_assemblies_paths_sep.join(platform_assemblies_paths)) - args.append('/CreatePdb' if sys.platform == 'win32' else '/CreatePerfMap') - args.append(debugging_files_dirname) - args.append('/in') - args.append(ni_filename) - return args - - async def _run_with_args(self, args): - """ - Creates a subprocess running crossgen with specified set of arguments, - communicates with the owner process - waits for its termination and pulls - returncode, stdour, stderr. - """ - stdout = None - stderr = None - - proc = await asyncio.create_subprocess_shell(" ".join(args), - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE) - stdout, stderr = await proc.communicate() - - return (proc.returncode, stdout.decode(), stderr.decode()) - - -def compute_file_hashsum(filename): - """ - Compute SHA256 file hashsum for {filename}. - """ - algo=hashlib.sha256() - maximum_block_size_in_bytes = 65536 - with open(filename, 'rb') as file: - while True: - block = file.read(maximum_block_size_in_bytes) - if block: - algo.update(block) - else: - break - return algo.hexdigest() - - -################################################################################ -# This describes collected during crossgen information. -################################################################################ -class CrossGenResult: - def __init__(self, assembly_name, returncode, stdout, stderr, output_file_hashsum, output_file_size_in_bytes, output_file_type): - self.assembly_name = assembly_name - self.returncode = returncode - self.stdout = stdout - self.stderr = stderr - self.output_file_hashsum = output_file_hashsum - self.output_file_size_in_bytes = output_file_size_in_bytes - self.output_file_type = output_file_type - -################################################################################ -# JSON Encoder for CrossGenResult objects. -################################################################################ -class CrossGenResultEncoder(json.JSONEncoder): - def default(self, obj): - if isinstance(obj, CrossGenResult): - return { - 'AssemblyName': obj.assembly_name, - 'ReturnCode': obj.returncode, - 'StdOut': obj.stdout.splitlines(), - 'StdErr': obj.stderr.splitlines(), - 'OutputFileHash': obj.output_file_hashsum, - 'OutputFileSizeInBytes': obj.output_file_size_in_bytes, - 'OutputFileType': obj.output_file_type } - # Let the base class default method raise the TypeError - return json.JSONEncoder.default(self, obj) - -################################################################################ -# JSON Decoder for CrossGenResult objects. -################################################################################ -class CrossGenResultDecoder(json.JSONDecoder): - def __init__(self, *args, **kwargs): - json.JSONDecoder.__init__(self, object_hook=self._decode_object, *args, **kwargs) - def _decode_object(self, dict): - try: - assembly_name = dict['AssemblyName'] - returncode = dict['ReturnCode'] - stdout = dict['StdOut'] - stderr = dict['StdErr'] - output_file_hashsum = dict['OutputFileHash'] - output_file_size_in_bytes = dict['OutputFileSizeInBytes'] - output_file_type = dict['OutputFileType'] - return CrossGenResult(assembly_name, returncode, stdout, stderr, output_file_hashsum, output_file_size_in_bytes, output_file_type) - except KeyError: - return dict - - -################################################################################ -# Helper Functions -################################################################################ -def get_assembly_name(il_filename): - basename = os.path.basename(il_filename) - assembly_name, _ = os.path.splitext(basename) - return assembly_name - -class FileTypes: - NativeOrReadyToRunImage = 'NativeOrReadyToRunImage' - DebuggingFile = 'DebuggingFile' - -async def run_crossgen(crossgen_executable_filename, il_filename, ni_filename, platform_assemblies_paths, debugging_files_dirname): - runner = CrossGenRunner(crossgen_executable_filename) - returncode, stdout, stderr = await runner.crossgen_il_file(il_filename, ni_filename, platform_assemblies_paths) - ni_file_hashsum = compute_file_hashsum(ni_filename) if returncode == 0 else None - ni_file_size_in_bytes = os.path.getsize(ni_filename) if returncode == 0 else None - assembly_name = get_assembly_name(il_filename) - crossgen_assembly_result = CrossGenResult(assembly_name, returncode, stdout, stderr, ni_file_hashsum, ni_file_size_in_bytes, output_file_type=FileTypes.NativeOrReadyToRunImage) - - if returncode != 0: - return [crossgen_assembly_result] - - platform_assemblies_paths = platform_assemblies_paths + [os.path.dirname(ni_filename)] - returncode, stdout, stderr = await runner.create_debugging_file(ni_filename, debugging_files_dirname, platform_assemblies_paths) - - if returncode == 0: - filenames = list(filter(lambda filename: not re.match("^{0}\.ni\.".format(assembly_name), filename, re.IGNORECASE) is None, os.listdir(debugging_files_dirname))) - assert len(filenames) == 1 - debugging_filename = os.path.join(debugging_files_dirname, filenames[0]) - debugging_file_hashsum = compute_file_hashsum(debugging_filename) - debugging_file_size_in_bytes = os.path.getsize(debugging_filename) - else: - debugging_file_hashsum = None - debugging_file_size_in_bytes = None - - create_debugging_file_result = CrossGenResult(assembly_name, returncode, stdout, stderr, debugging_file_hashsum, debugging_file_size_in_bytes, output_file_type=FileTypes.DebuggingFile) - - return [crossgen_assembly_result, create_debugging_file_result] - -def save_crossgen_result_to_json_file(crossgen_result, json_filename): - with open(json_filename, 'wt') as json_file: - json.dump(crossgen_result, json_file, cls=CrossGenResultEncoder, indent=2) - -def save_crossgen_results_to_json_files(crossgen_results, result_dirname): - for result in crossgen_results: - json_filename = os.path.join(result_dirname, "{0}-{1}.json".format(result.assembly_name, result.output_file_type)) - save_crossgen_result_to_json_file(result, json_filename) - -def create_output_folders(): - ni_files_dirname = tempfile.mkdtemp() - debugging_files_dirname = os.path.join(ni_files_dirname, "DebuggingFiles") - os.mkdir(debugging_files_dirname) - return ni_files_dirname, debugging_files_dirname - -async def crossgen_corelib(args): - il_corelib_filename = args.il_corelib_filename - assembly_name = os.path.basename(il_corelib_filename) - ni_corelib_dirname, debugging_files_dirname = create_output_folders() - ni_corelib_filename = os.path.join(ni_corelib_dirname, assembly_name) - platform_assemblies_paths = [os.path.dirname(il_corelib_filename)] - - # Validate the paths are correct. - if not os.path.exists(il_corelib_filename): - print("IL Corelib path does not exist.") - sys.exit(1) - - crossgen_results = await run_crossgen(args.crossgen_executable_filename, il_corelib_filename, ni_corelib_filename, platform_assemblies_paths, debugging_files_dirname) - shutil.rmtree(ni_corelib_dirname, ignore_errors=True) - save_crossgen_results_to_json_files(crossgen_results, args.result_dirname) - -def add_ni_extension(filename): - filename,ext = os.path.splitext(filename) - return filename + '.ni' + ext - -def crossgen_framework(args): - global g_Framework_Assemblies - - il_corelib_filename = args.il_corelib_filename - ni_files_dirname, debugging_files_dirname = create_output_folders() - g_Framework_Assemblies = [il_corelib_filename] + g_Framework_Assemblies - - async def run_crossgen_helper(print_prefix, assembly_name): - platform_assemblies_paths = [args.core_root] - print("{}{} {}".format(print_prefix, args.crossgen_executable_filename, assembly_name)) - - if assembly_name == il_corelib_filename: - ni_corelib_filename = os.path.join(ni_files_dirname, os.path.basename(il_corelib_filename)) - crossgen_results = await run_crossgen(args.crossgen_executable_filename, il_corelib_filename, ni_corelib_filename, platform_assemblies_paths, debugging_files_dirname) - save_crossgen_results_to_json_files(crossgen_results, args.result_dirname) - else: - il_filename = os.path.join(args.core_root, assembly_name) - ni_filename = os.path.join(ni_files_dirname, add_ni_extension(assembly_name)) - crossgen_results = await run_crossgen(args.crossgen_executable_filename, il_filename, ni_filename, platform_assemblies_paths, debugging_files_dirname) - save_crossgen_results_to_json_files(crossgen_results, args.result_dirname) - - helper = AsyncSubprocessHelper(g_Framework_Assemblies, verbose=True) - helper.run_to_completion(run_crossgen_helper) - - shutil.rmtree(ni_files_dirname, ignore_errors=True) - -def load_crossgen_result_from_json_file(json_filename): - with open(json_filename, 'rt') as json_file: - return json.load(json_file, cls=CrossGenResultDecoder) - -def load_crossgen_results_from_dir(dirname, output_file_type): - crossgen_results = [] - for filename in glob.glob(os.path.join(dirname, '*.json')): - loaded_result = load_crossgen_result_from_json_file(filename) - if loaded_result.output_file_type == output_file_type: - crossgen_results.append(loaded_result) - return crossgen_results - -def dotnet_sdk_enumerate_assemblies(dotnet_sdk_dirname): - for dirpath, _, filenames in os.walk(dotnet_sdk_dirname): - dirname = os.path.dirname(dirpath) - if dirname.endswith('Microsoft.NETCore.App') or dirname.endswith('Microsoft.AspNetCore.App') or dirname.endswith('Microsoft.AspNetCore.All'): - filenames = filter(lambda filename: not re.match(r'^(Microsoft|System)\..*dll$', filename) is None, filenames) - filenames = filter(lambda filename: filename != 'System.Private.CoreLib.dll', filenames) - yield (dirpath, filenames) - -async def crossgen_dotnet_sdk(args): - dotnet_sdk_dirname = tempfile.mkdtemp() - with tarfile.open(args.dotnet_sdk_filename) as dotnet_sdk_tarfile: - dotnet_sdk_tarfile.extractall(dotnet_sdk_dirname) - - il_corelib_filename = args.il_corelib_filename - ni_files_dirname, debugging_files_dirname = create_output_folders() - ni_corelib_filename = os.path.join(ni_files_dirname, os.path.basename(il_corelib_filename)) - platform_assemblies_paths = [os.path.dirname(il_corelib_filename)] - crossgen_results = await run_crossgen(args.crossgen_executable_filename, il_corelib_filename, ni_corelib_filename, platform_assemblies_paths, debugging_files_dirname) - save_crossgen_results_to_json_files(crossgen_results, args.result_dirname) - - platform_assemblies_paths = [ni_files_dirname] - - for il_files_dirname, _ in dotnet_sdk_enumerate_assemblies(dotnet_sdk_dirname): - platform_assemblies_paths.append(il_files_dirname) - - for il_files_dirname, assembly_names in dotnet_sdk_enumerate_assemblies(dotnet_sdk_dirname): - for assembly_name in assembly_names: - il_filename = os.path.join(il_files_dirname, assembly_name) - ni_filename = os.path.join(ni_files_dirname, add_ni_extension(assembly_name)) - crossgen_results = await run_crossgen(args.crossgen_executable_filename, il_filename, ni_filename, platform_assemblies_paths, debugging_files_dirname) - save_crossgen_results_to_json_files(crossgen_results, args.result_dirname) - shutil.rmtree(ni_files_dirname, ignore_errors=True) - -def print_omitted_assemblies_message(omitted_assemblies, dirname): - print('The information for the following assemblies was omitted from "{0}" directory:'.format(dirname)) - for assembly_name in sorted(omitted_assemblies): - print(' - ' + assembly_name) - -def print_compare_result_message_helper(message_header, base_value, diff_value, base_dirname, diff_dirname): - assert base_value != diff_value - print(message_header) - print(' - "{0}" has "{1}"'.format(base_dirname, base_value)) - print(' - "{0}" has "{1}"'.format(diff_dirname, diff_value)) - -def compare_and_print_message(base_result, diff_result, base_dirname, diff_dirname): - base_diff_are_equal = True - - assert base_result.assembly_name == diff_result.assembly_name - assert base_result.output_file_type == diff_result.output_file_type - - if base_result.returncode != diff_result.returncode: - base_diff_are_equal = False - print_compare_result_message_helper('Return code mismatch for "{0}" assembly for files of type "{1}":'.format(base_result.assembly_name, base_result.output_file_type), base_result.returncode, diff_result.returncode, base_dirname, diff_dirname) - elif base_result.returncode == 0 and diff_result.returncode == 0: - assert not base_result.output_file_hashsum is None - assert not base_result.output_file_size_in_bytes is None - - assert not diff_result.output_file_hashsum is None - assert not diff_result.output_file_size_in_bytes is None - - if base_result.output_file_hashsum != diff_result.output_file_hashsum: - base_diff_are_equal = False - print_compare_result_message_helper('File hash sum mismatch for "{0}" assembly for files of type "{1}":'.format(base_result.assembly_name, base_result.output_file_type), base_result.output_file_hashsum, diff_result.output_file_hashsum, base_dirname, diff_dirname) - - if base_result.output_file_size_in_bytes != diff_result.output_file_size_in_bytes: - base_diff_are_equal = False - print_compare_result_message_helper('File size mismatch for "{0}" assembly for files of type "{1}":'.format(base_result.assembly_name, base_result.output_file_type), base_result.output_file_size_in_bytes, diff_result.output_file_size_in_bytes, base_dirname, diff_dirname) - - return base_diff_are_equal - -def compare_results(args): - """ - Checks whether {base} and {diff} crossgens are "equal": - 1. their return codes are the same; - 2. and if they both succeeded in step 1, their outputs (native images and debugging files (i.e. pdb or perfmap files)) are the same. - """ - base_diff_are_equal = True - for output_file_type in [FileTypes.NativeOrReadyToRunImage, FileTypes.DebuggingFile]: - print('Comparing crossgen results in "{0}" and "{1}" directories for files of type "{2}":'.format(args.base_dirname, args.diff_dirname, output_file_type)) - - base_results = load_crossgen_results_from_dir(args.base_dirname, output_file_type) - diff_results = load_crossgen_results_from_dir(args.diff_dirname, output_file_type) - - base_assemblies = { r.assembly_name for r in base_results } - diff_assemblies = { r.assembly_name for r in diff_results } - both_assemblies = base_assemblies & diff_assemblies - - num_omitted_results = 0 - - omitted_from_base_dir = diff_assemblies - base_assemblies - omitted_from_diff_dir = base_assemblies - diff_assemblies - - if len(omitted_from_base_dir) != 0: - num_omitted_results += len(omitted_from_base_dir) - base_diff_are_equal = False - print_omitted_assemblies_message(omitted_from_base_dir, args.base_dirname) - - if len(omitted_from_diff_dir) != 0: - num_omitted_results += len(omitted_from_diff_dir) - base_diff_are_equal = False - print_omitted_assemblies_message(omitted_from_diff_dir, args.diff_dirname) - - base_results_by_name = dict((r.assembly_name, r) for r in base_results) - diff_results_by_name = dict((r.assembly_name, r) for r in diff_results) - - num_mismatched_results = 0 - - for assembly_name in sorted(both_assemblies): - base_result = base_results_by_name[assembly_name] - diff_result = diff_results_by_name[assembly_name] - if not compare_and_print_message(base_result, diff_result, args.base_dirname, args.diff_dirname): - base_diff_are_equal = False - num_mismatched_results += 1 - - print("Number of omitted results: {0}".format(num_omitted_results)) - print("Number of mismatched results: {0}".format(num_mismatched_results)) - print("Total number of files compared: {0}".format(len(both_assemblies))) - - sys.exit(0 if base_diff_are_equal else 1) - -################################################################################ -# __main__ -################################################################################ - -if __name__ == '__main__': - start = datetime.datetime.now() - - parser = build_argument_parser() - args = parser.parse_args() - func = args.func(args) - - end = datetime.datetime.now() - elapsed = end - start - - print("Elapsed time: {}".format(elapsed.total_seconds())) diff --git a/src/tests/Common/testenvironment.proj b/src/tests/Common/testenvironment.proj index 37392b57df478c..da746f2a71afbe 100644 --- a/src/tests/Common/testenvironment.proj +++ b/src/tests/Common/testenvironment.proj @@ -38,6 +38,7 @@ COMPlus_FeatureSIMD; COMPlus_ForceRelocs; COMPlus_GCStress; + COMPlus_GCName; COMPlus_HeapVerify; COMPlus_JITMinOpts; COMPlus_JitELTHookEnabled; @@ -155,6 +156,8 @@ + + @@ -358,6 +361,9 @@ Varargs supported on this platform + + Allocates large contiguous array that is not consistently available on 32-bit platforms + @@ -575,6 +581,9 @@ https://github.com/dotnet/runtime/issues/12979 + + Allocates large contiguous array that is not consistently available on 32-bit platforms + @@ -705,6 +714,9 @@ https://github.com/dotnet/runtime/issues/10001 + + Allocates large contiguous array that is not consistently available on 32-bit platforms + @@ -1153,6 +1165,9 @@ PlatformDetection.IsPreciseGcSupported false on mono + + PlatformDetection.IsPreciseGcSupported false on mono + PlatformDetection.IsPreciseGcSupported false on mono diff --git a/src/workloads/workloads.csproj b/src/workloads/workloads.csproj index 1f386f1479578a..bc2fa9cc667f48 100644 --- a/src/workloads/workloads.csproj +++ b/src/workloads/workloads.csproj @@ -5,11 +5,12 @@ netcoreapp3.1 net5.0 net472 - $(NuGetPackageRoot)microsoft.dotnet.build.tasks.installers\$(MicrosoftDotNetBuildTasksInstallersPackageVersion)\tools\$(MicrosoftDotNetBuildTasksInstallersTaskTargetFramework)\Microsoft.DotNet.Build.Tasks.Installers.dll + $(NuGetPackageRoot)microsoft.dotnet.build.tasks.installers\$(MicrosoftDotNetBuildTasksInstallersVersion)\tools\$(MicrosoftDotNetBuildTasksInstallersTaskTargetFramework)\Microsoft.DotNet.Build.Tasks.Installers.dll + - + @@ -42,7 +43,7 @@ - + @@ -90,6 +91,23 @@ + + + + + + + + + + @@ -117,6 +137,7 @@ +